Merge "Update test related to usage time api for foreground usage on BatteryUsageStatsProtoTests" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 16389b3..b3e8ea8 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -26,6 +26,7 @@
":android.os.flags-aconfig-java{.generated_srcjars}",
":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
":android.security.flags-aconfig-java{.generated_srcjars}",
+ ":android.service.chooser.flags-aconfig-java{.generated_srcjars}",
":android.service.notification.flags-aconfig-java{.generated_srcjars}",
":android.view.flags-aconfig-java{.generated_srcjars}",
":android.view.accessibility.flags-aconfig-java{.generated_srcjars}",
@@ -54,6 +55,7 @@
":android.app.flags-aconfig-java{.generated_srcjars}",
":android.credentials.flags-aconfig-java{.generated_srcjars}",
":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}",
+ ":com.android.server.flags.pinner-aconfig-java{.generated_srcjars}",
":android.service.voice.flags-aconfig-java{.generated_srcjars}",
":android.media.tv.flags-aconfig-java{.generated_srcjars}",
":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
@@ -583,6 +585,19 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// Pinner Service
+aconfig_declarations {
+ name: "com.android.server.flags.pinner-aconfig",
+ package: "com.android.server.flags",
+ srcs: ["services/core/java/com/android/server/flags/pinner.aconfig"],
+}
+
+java_aconfig_library {
+ name: "com.android.server.flags.pinner-aconfig-java",
+ aconfig_declarations: "com.android.server.flags.pinner-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Voice
aconfig_declarations {
name: "android.service.voice.flags-aconfig",
@@ -683,6 +698,19 @@
aconfig_declarations: "device_policy_aconfig_flags",
}
+// Chooser / "Sharesheet"
+aconfig_declarations {
+ name: "android.service.chooser.flags-aconfig",
+ package: "android.service.chooser",
+ srcs: ["core/java/android/service/chooser/flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.service.chooser.flags-aconfig-java",
+ aconfig_declarations: "android.service.chooser.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// JobScheduler
aconfig_declarations {
name: "framework-jobscheduler-job.flags-aconfig",
@@ -755,6 +783,13 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.hardware.usb.flags-aconfig-java-host",
+ aconfig_declarations: "android.hardware.usb.flags-aconfig",
+ host_supported: true,
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// WindowingTools
aconfig_declarations {
name: "android.tracing.flags-aconfig",
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 158d914..e6c94d8 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -32,6 +32,7 @@
import android.app.AlarmManager;
import android.app.BroadcastOptions;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
@@ -41,6 +42,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -81,6 +83,7 @@
import android.os.UserHandle;
import android.os.WearModeManagerInternal;
import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
@@ -109,6 +112,7 @@
import com.android.server.deviceidle.IDeviceIdleConstraint;
import com.android.server.deviceidle.TvConstraintController;
import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.utils.UserSettingDeviceConfigMediator;
import com.android.server.wm.ActivityTaskManagerInternal;
import org.xmlpull.v1.XmlPullParser;
@@ -1020,7 +1024,8 @@
* global Settings. Any access to this class or its fields should be done while
* holding the DeviceIdleController lock.
*/
- public final class Constants implements DeviceConfig.OnPropertiesChangedListener {
+ public final class Constants extends ContentObserver
+ implements DeviceConfig.OnPropertiesChangedListener {
// Key names stored in the settings value.
private static final String KEY_FLEX_TIME_SHORT = "flex_time_short";
private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
@@ -1396,6 +1401,7 @@
/**
* Amount of time we would like to whitelist an app that is handling a
* {@link android.app.PendingIntent} triggered by a {@link android.app.Notification}.
+ *
* @see #KEY_NOTIFICATION_ALLOWLIST_DURATION_MS
*/
public long NOTIFICATION_ALLOWLIST_DURATION_MS = mDefaultNotificationAllowlistDurationMs;
@@ -1413,9 +1419,14 @@
*/
public boolean USE_MODE_MANAGER = mDefaultUseModeManager;
+ private final ContentResolver mResolver;
private final boolean mSmallBatteryDevice;
+ private final UserSettingDeviceConfigMediator mUserSettingDeviceConfigMediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
- public Constants() {
+ public Constants(Handler handler, ContentResolver resolver) {
+ super(handler);
+ mResolver = resolver;
initDefault();
mSmallBatteryDevice = ActivityManager.isSmallBatteryDevice();
if (mSmallBatteryDevice) {
@@ -1424,8 +1435,14 @@
}
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DEVICE_IDLE,
AppSchedulingModuleThread.getExecutor(), this);
+ mResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_IDLE_CONSTANTS),
+ false, this);
// Load all the constants.
- onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_DEVICE_IDLE));
+ updateSettingsConstantLocked();
+ mUserSettingDeviceConfigMediator.setDeviceConfigProperties(
+ DeviceConfig.getProperties(DeviceConfig.NAMESPACE_DEVICE_IDLE));
+ updateConstantsLocked();
}
private void initDefault() {
@@ -1574,188 +1591,166 @@
return (!COMPRESS_TIME || defTimeout < compTimeout) ? defTimeout : compTimeout;
}
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ synchronized (DeviceIdleController.this) {
+ updateSettingsConstantLocked();
+ updateConstantsLocked();
+ }
+ }
+
+ private void updateSettingsConstantLocked() {
+ try {
+ mUserSettingDeviceConfigMediator.setSettingsString(
+ Settings.Global.getString(mResolver,
+ Settings.Global.DEVICE_IDLE_CONSTANTS));
+ } catch (IllegalArgumentException e) {
+ // Failed to parse the settings string, log this and move on with previous values.
+ Slog.e(TAG, "Bad device idle settings", e);
+ }
+ }
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
synchronized (DeviceIdleController.this) {
- for (String name : properties.getKeyset()) {
- if (name == null) {
- continue;
- }
- switch (name) {
- case KEY_FLEX_TIME_SHORT:
- FLEX_TIME_SHORT = properties.getLong(
- KEY_FLEX_TIME_SHORT, mDefaultFlexTimeShort);
- break;
- case KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT:
- LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = properties.getLong(
- KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
- mDefaultLightIdleAfterInactiveTimeout);
- break;
- case KEY_LIGHT_IDLE_TIMEOUT:
- LIGHT_IDLE_TIMEOUT = properties.getLong(
- KEY_LIGHT_IDLE_TIMEOUT, mDefaultLightIdleTimeout);
- break;
- case KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX:
- LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = properties.getLong(
- KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX,
- mDefaultLightIdleTimeoutInitialFlex);
- break;
- case KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX:
- LIGHT_IDLE_TIMEOUT_MAX_FLEX = properties.getLong(
- KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX,
- mDefaultLightIdleTimeoutMaxFlex);
- break;
- case KEY_LIGHT_IDLE_FACTOR:
- LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat(
- KEY_LIGHT_IDLE_FACTOR, mDefaultLightIdleFactor));
- break;
- case KEY_LIGHT_IDLE_INCREASE_LINEARLY:
- LIGHT_IDLE_INCREASE_LINEARLY = properties.getBoolean(
- KEY_LIGHT_IDLE_INCREASE_LINEARLY,
- mDefaultLightIdleIncreaseLinearly);
- break;
- case KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS:
- LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = properties.getLong(
- KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS,
- mDefaultLightIdleLinearIncreaseFactorMs);
- break;
- case KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS:
- LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = properties.getLong(
- KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS,
- mDefaultLightIdleFlexLinearIncreaseFactorMs);
- break;
- case KEY_LIGHT_MAX_IDLE_TIMEOUT:
- LIGHT_MAX_IDLE_TIMEOUT = properties.getLong(
- KEY_LIGHT_MAX_IDLE_TIMEOUT, mDefaultLightMaxIdleTimeout);
- break;
- case KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET:
- LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = properties.getLong(
- KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET,
- mDefaultLightIdleMaintenanceMinBudget);
- break;
- case KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET:
- LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = properties.getLong(
- KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,
- mDefaultLightIdleMaintenanceMaxBudget);
- break;
- case KEY_MIN_LIGHT_MAINTENANCE_TIME:
- MIN_LIGHT_MAINTENANCE_TIME = properties.getLong(
- KEY_MIN_LIGHT_MAINTENANCE_TIME,
- mDefaultMinLightMaintenanceTime);
- break;
- case KEY_MIN_DEEP_MAINTENANCE_TIME:
- MIN_DEEP_MAINTENANCE_TIME = properties.getLong(
- KEY_MIN_DEEP_MAINTENANCE_TIME,
- mDefaultMinDeepMaintenanceTime);
- break;
- case KEY_INACTIVE_TIMEOUT:
- final long defaultInactiveTimeout = mSmallBatteryDevice
- ? DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY
- : mDefaultInactiveTimeout;
- INACTIVE_TIMEOUT = properties.getLong(
- KEY_INACTIVE_TIMEOUT, defaultInactiveTimeout);
- break;
- case KEY_SENSING_TIMEOUT:
- SENSING_TIMEOUT = properties.getLong(
- KEY_SENSING_TIMEOUT, mDefaultSensingTimeout);
- break;
- case KEY_LOCATING_TIMEOUT:
- LOCATING_TIMEOUT = properties.getLong(
- KEY_LOCATING_TIMEOUT, mDefaultLocatingTimeout);
- break;
- case KEY_LOCATION_ACCURACY:
- LOCATION_ACCURACY = properties.getFloat(
- KEY_LOCATION_ACCURACY, mDefaultLocationAccuracy);
- break;
- case KEY_MOTION_INACTIVE_TIMEOUT:
- MOTION_INACTIVE_TIMEOUT = properties.getLong(
- KEY_MOTION_INACTIVE_TIMEOUT, mDefaultMotionInactiveTimeout);
- break;
- case KEY_MOTION_INACTIVE_TIMEOUT_FLEX:
- MOTION_INACTIVE_TIMEOUT_FLEX = properties.getLong(
- KEY_MOTION_INACTIVE_TIMEOUT_FLEX,
- mDefaultMotionInactiveTimeoutFlex);
- break;
- case KEY_IDLE_AFTER_INACTIVE_TIMEOUT:
- final long defaultIdleAfterInactiveTimeout = mSmallBatteryDevice
- ? DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY
- : mDefaultIdleAfterInactiveTimeout;
- IDLE_AFTER_INACTIVE_TIMEOUT = properties.getLong(
- KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
- defaultIdleAfterInactiveTimeout);
- break;
- case KEY_IDLE_PENDING_TIMEOUT:
- IDLE_PENDING_TIMEOUT = properties.getLong(
- KEY_IDLE_PENDING_TIMEOUT, mDefaultIdlePendingTimeout);
- break;
- case KEY_MAX_IDLE_PENDING_TIMEOUT:
- MAX_IDLE_PENDING_TIMEOUT = properties.getLong(
- KEY_MAX_IDLE_PENDING_TIMEOUT, mDefaultMaxIdlePendingTimeout);
- break;
- case KEY_IDLE_PENDING_FACTOR:
- IDLE_PENDING_FACTOR = properties.getFloat(
- KEY_IDLE_PENDING_FACTOR, mDefaultIdlePendingFactor);
- break;
- case KEY_QUICK_DOZE_DELAY_TIMEOUT:
- QUICK_DOZE_DELAY_TIMEOUT = properties.getLong(
- KEY_QUICK_DOZE_DELAY_TIMEOUT, mDefaultQuickDozeDelayTimeout);
- break;
- case KEY_IDLE_TIMEOUT:
- IDLE_TIMEOUT = properties.getLong(
- KEY_IDLE_TIMEOUT, mDefaultIdleTimeout);
- break;
- case KEY_MAX_IDLE_TIMEOUT:
- MAX_IDLE_TIMEOUT = properties.getLong(
- KEY_MAX_IDLE_TIMEOUT, mDefaultMaxIdleTimeout);
- break;
- case KEY_IDLE_FACTOR:
- IDLE_FACTOR = properties.getFloat(KEY_IDLE_FACTOR, mDefaultIdleFactor);
- break;
- case KEY_MIN_TIME_TO_ALARM:
- MIN_TIME_TO_ALARM = properties.getLong(
- KEY_MIN_TIME_TO_ALARM, mDefaultMinTimeToAlarm);
- break;
- case KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS:
- MAX_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
- KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS,
- mDefaultMaxTempAppAllowlistDurationMs);
- break;
- case KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS:
- MMS_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
- KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS,
- mDefaultMmsTempAppAllowlistDurationMs);
- break;
- case KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS:
- SMS_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
- KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS,
- mDefaultSmsTempAppAllowlistDurationMs);
- break;
- case KEY_NOTIFICATION_ALLOWLIST_DURATION_MS:
- NOTIFICATION_ALLOWLIST_DURATION_MS = properties.getLong(
- KEY_NOTIFICATION_ALLOWLIST_DURATION_MS,
- mDefaultNotificationAllowlistDurationMs);
- break;
- case KEY_WAIT_FOR_UNLOCK:
- WAIT_FOR_UNLOCK = properties.getBoolean(
- KEY_WAIT_FOR_UNLOCK, mDefaultWaitForUnlock);
- break;
- case KEY_USE_WINDOW_ALARMS:
- USE_WINDOW_ALARMS = properties.getBoolean(
- KEY_USE_WINDOW_ALARMS, mDefaultUseWindowAlarms);
- break;
- case KEY_USE_MODE_MANAGER:
- USE_MODE_MANAGER = properties.getBoolean(
- KEY_USE_MODE_MANAGER, mDefaultUseModeManager);
- break;
- default:
- Slog.e(TAG, "Unknown configuration key: " + name);
- break;
- }
- }
+ mUserSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
+ updateConstantsLocked();
}
}
+ private void updateConstantsLocked() {
+ if (mSmallBatteryDevice) return;
+ FLEX_TIME_SHORT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_FLEX_TIME_SHORT, mDefaultFlexTimeShort);
+
+ LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
+ mDefaultLightIdleAfterInactiveTimeout);
+
+ LIGHT_IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_TIMEOUT, mDefaultLightIdleTimeout);
+
+ LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX,
+ mDefaultLightIdleTimeoutInitialFlex);
+
+ LIGHT_IDLE_TIMEOUT_MAX_FLEX = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX,
+ mDefaultLightIdleTimeoutMaxFlex);
+
+ LIGHT_IDLE_FACTOR = Math.max(1, mUserSettingDeviceConfigMediator.getFloat(
+ KEY_LIGHT_IDLE_FACTOR, mDefaultLightIdleFactor));
+
+ LIGHT_IDLE_INCREASE_LINEARLY = mUserSettingDeviceConfigMediator.getBoolean(
+ KEY_LIGHT_IDLE_INCREASE_LINEARLY,
+ mDefaultLightIdleIncreaseLinearly);
+
+ LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS,
+ mDefaultLightIdleLinearIncreaseFactorMs);
+
+ LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS,
+ mDefaultLightIdleFlexLinearIncreaseFactorMs);
+
+ LIGHT_MAX_IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_MAX_IDLE_TIMEOUT, mDefaultLightMaxIdleTimeout);
+
+ LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET,
+ mDefaultLightIdleMaintenanceMinBudget);
+
+ LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,
+ mDefaultLightIdleMaintenanceMaxBudget);
+
+ MIN_LIGHT_MAINTENANCE_TIME = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MIN_LIGHT_MAINTENANCE_TIME,
+ mDefaultMinLightMaintenanceTime);
+
+ MIN_DEEP_MAINTENANCE_TIME = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MIN_DEEP_MAINTENANCE_TIME,
+ mDefaultMinDeepMaintenanceTime);
+
+ final long defaultInactiveTimeout = mSmallBatteryDevice
+ ? DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY
+ : mDefaultInactiveTimeout;
+ INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_INACTIVE_TIMEOUT, defaultInactiveTimeout);
+
+ SENSING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_SENSING_TIMEOUT, mDefaultSensingTimeout);
+
+ LOCATING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_LOCATING_TIMEOUT, mDefaultLocatingTimeout);
+
+ LOCATION_ACCURACY = mUserSettingDeviceConfigMediator.getFloat(
+ KEY_LOCATION_ACCURACY, mDefaultLocationAccuracy);
+
+ MOTION_INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MOTION_INACTIVE_TIMEOUT, mDefaultMotionInactiveTimeout);
+
+ MOTION_INACTIVE_TIMEOUT_FLEX = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MOTION_INACTIVE_TIMEOUT_FLEX,
+ mDefaultMotionInactiveTimeoutFlex);
+
+ final long defaultIdleAfterInactiveTimeout = mSmallBatteryDevice
+ ? DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY
+ : mDefaultIdleAfterInactiveTimeout;
+ IDLE_AFTER_INACTIVE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
+ defaultIdleAfterInactiveTimeout);
+
+ IDLE_PENDING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_IDLE_PENDING_TIMEOUT, mDefaultIdlePendingTimeout);
+
+ MAX_IDLE_PENDING_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MAX_IDLE_PENDING_TIMEOUT, mDefaultMaxIdlePendingTimeout);
+
+ IDLE_PENDING_FACTOR = mUserSettingDeviceConfigMediator.getFloat(
+ KEY_IDLE_PENDING_FACTOR, mDefaultIdlePendingFactor);
+
+ QUICK_DOZE_DELAY_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_QUICK_DOZE_DELAY_TIMEOUT, mDefaultQuickDozeDelayTimeout);
+
+ IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_IDLE_TIMEOUT, mDefaultIdleTimeout);
+
+ MAX_IDLE_TIMEOUT = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MAX_IDLE_TIMEOUT, mDefaultMaxIdleTimeout);
+
+ IDLE_FACTOR = mUserSettingDeviceConfigMediator.getFloat(KEY_IDLE_FACTOR,
+ mDefaultIdleFactor);
+
+ MIN_TIME_TO_ALARM = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MIN_TIME_TO_ALARM, mDefaultMinTimeToAlarm);
+
+ MAX_TEMP_APP_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS,
+ mDefaultMaxTempAppAllowlistDurationMs);
+
+ MMS_TEMP_APP_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS,
+ mDefaultMmsTempAppAllowlistDurationMs);
+
+ SMS_TEMP_APP_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS,
+ mDefaultSmsTempAppAllowlistDurationMs);
+
+ NOTIFICATION_ALLOWLIST_DURATION_MS = mUserSettingDeviceConfigMediator.getLong(
+ KEY_NOTIFICATION_ALLOWLIST_DURATION_MS,
+ mDefaultNotificationAllowlistDurationMs);
+
+ WAIT_FOR_UNLOCK = mUserSettingDeviceConfigMediator.getBoolean(
+ KEY_WAIT_FOR_UNLOCK, mDefaultWaitForUnlock);
+
+ USE_WINDOW_ALARMS = mUserSettingDeviceConfigMediator.getBoolean(
+ KEY_USE_WINDOW_ALARMS, mDefaultUseWindowAlarms);
+
+ USE_MODE_MANAGER = mUserSettingDeviceConfigMediator.getBoolean(
+ KEY_USE_MODE_MANAGER, mDefaultUseModeManager);
+ }
+
void dump(PrintWriter pw) {
pw.println(" Settings:");
@@ -2490,9 +2485,10 @@
return mConnectivityManager;
}
- Constants getConstants(DeviceIdleController controller) {
+ Constants getConstants(DeviceIdleController controller, Handler handler,
+ ContentResolver resolver) {
if (mConstants == null) {
- mConstants = controller.new Constants();
+ mConstants = controller.new Constants(handler, resolver);
}
return mConstants;
}
@@ -2650,7 +2646,7 @@
}
}
- mConstants = mInjector.getConstants(this);
+ mConstants = mInjector.getConstants(this, mHandler, getContext().getContentResolver());
readConfigFileLocked();
updateWhitelistAppIdsLocked();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
index d2150b8..8381d1a 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -109,7 +109,6 @@
import android.content.ContentResolver;
import android.provider.DeviceConfig;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.SparseArray;
@@ -154,7 +153,6 @@
private long mMinSatiatedConsumptionLimit;
private long mMaxSatiatedConsumptionLimit;
- private final KeyValueListParser mParser = new KeyValueListParser(',');
private final Injector mInjector;
private final SparseArray<Action> mActions = new SparseArray<>();
@@ -241,35 +239,36 @@
mRewards.clear();
try {
- mParser.setString(policyValuesString);
+ mUserSettingDeviceConfigMediator.setSettingsString(policyValuesString);
+ mUserSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Global setting key incorrect: ", e);
}
- mMinSatiatedBalanceOther = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceOther = getConstantAsCake(
KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES);
- mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(
KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
DEFAULT_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES,
mMinSatiatedBalanceOther);
- mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceExempted = getConstantAsCake(
KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
mMinSatiatedBalanceHeadlessSystemApp);
- mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
+ mMaxSatiatedBalance = getConstantAsCake(
KEY_AM_MAX_SATIATED_BALANCE, DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES,
Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
- mMinSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mMinSatiatedConsumptionLimit = getConstantAsCake(
KEY_AM_MIN_CONSUMPTION_LIMIT, DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES,
arcToCake(1));
- mInitialSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mInitialSatiatedConsumptionLimit = getConstantAsCake(
KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES,
mMinSatiatedConsumptionLimit);
- mMaxSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mMaxSatiatedConsumptionLimit = getConstantAsCake(
KEY_AM_MAX_CONSUMPTION_LIMIT, DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES,
mInitialSatiatedConsumptionLimit);
- final long exactAllowWhileIdleWakeupBasePrice = getConstantAsCake(mParser, properties,
+ final long exactAllowWhileIdleWakeupBasePrice = getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE_CAKES);
@@ -279,49 +278,49 @@
// run out of credits, and not when the system has run out of stock.
mActions.put(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP_CAKES),
exactAllowWhileIdleWakeupBasePrice,
/* respectsStockLimit */ false));
mActions.put(ACTION_ALARM_WAKEUP_EXACT,
new Action(ACTION_ALARM_WAKEUP_EXACT,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES),
/* respectsStockLimit */ false));
final long inexactAllowWhileIdleWakeupBasePrice =
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE_CAKES);
mActions.put(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
new Action(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP_CAKES),
inexactAllowWhileIdleWakeupBasePrice,
/* respectsStockLimit */ false));
mActions.put(ACTION_ALARM_WAKEUP_INEXACT,
new Action(ACTION_ALARM_WAKEUP_INEXACT,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE_CAKES),
/* respectsStockLimit */ false));
- final long exactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(mParser, properties,
+ final long exactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE_CAKES);
mActions.put(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
new Action(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP_CAKES),
exactAllowWhileIdleNonWakeupBasePrice,
@@ -329,18 +328,18 @@
mActions.put(ACTION_ALARM_NONWAKEUP_EXACT,
new Action(ACTION_ALARM_NONWAKEUP_EXACT,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE_CAKES),
/* respectsStockLimit */ false));
- final long inexactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(mParser, properties,
+ final long inexactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE_CAKES);
- final long inexactAllowWhileIdleNonWakeupCtp = getConstantAsCake(mParser, properties,
+ final long inexactAllowWhileIdleNonWakeupCtp = getConstantAsCake(
KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP_CAKES);
mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
@@ -350,72 +349,72 @@
mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT,
new Action(ACTION_ALARM_NONWAKEUP_INEXACT,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP,
DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE_CAKES)));
mActions.put(ACTION_ALARM_CLOCK,
new Action(ACTION_ALARM_CLOCK,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP,
DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE,
DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE_CAKES),
/* respectsStockLimit */ false));
mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_TOP_ACTIVITY_INSTANT,
DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_TOP_ACTIVITY_ONGOING,
DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_TOP_ACTIVITY_MAX,
DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX_CAKES)));
mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT,
DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING,
DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_SEEN_MAX,
DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX_CAKES)));
mRewards.put(REWARD_NOTIFICATION_INTERACTION,
new Reward(REWARD_NOTIFICATION_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT,
DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING,
DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX,
DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES)));
mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT,
DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING,
DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_WIDGET_INTERACTION_MAX,
DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX_CAKES)));
mRewards.put(REWARD_OTHER_USER_INTERACTION,
new Reward(REWARD_OTHER_USER_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT,
DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING,
DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX,
DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX_CAKES)));
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index a4043dd..61096b9 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -33,9 +33,9 @@
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.utils.UserSettingDeviceConfigMediator;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -197,10 +197,17 @@
}
protected final InternalResourceService mIrs;
+ protected final UserSettingDeviceConfigMediator mUserSettingDeviceConfigMediator;
private static final Modifier[] COST_MODIFIER_BY_INDEX = new Modifier[NUM_COST_MODIFIERS];
EconomicPolicy(@NonNull InternalResourceService irs) {
mIrs = irs;
+ // Don't cross the streams! Mixing Settings/local user config changes with DeviceConfig
+ // config can cause issues since the scales may be different, so use one or the other.
+ // If user settings exist, then just stick with the Settings constants, even if there
+ // are invalid values.
+ mUserSettingDeviceConfigMediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesAllMediator(',');
for (int mId : getCostModifiers()) {
initModifier(mId, irs);
}
@@ -464,28 +471,14 @@
return "UNKNOWN_REWARD:" + Integer.toHexString(eventId);
}
- protected long getConstantAsCake(@NonNull KeyValueListParser parser,
- @Nullable DeviceConfig.Properties properties, String key, long defaultValCake) {
- return getConstantAsCake(parser, properties, key, defaultValCake, 0);
+ protected long getConstantAsCake(String key, long defaultValCake) {
+ return getConstantAsCake(key, defaultValCake, 0);
}
- protected long getConstantAsCake(@NonNull KeyValueListParser parser,
- @Nullable DeviceConfig.Properties properties, String key, long defaultValCake,
- long minValCake) {
- // Don't cross the streams! Mixing Settings/local user config changes with DeviceConfig
- // config can cause issues since the scales may be different, so use one or the other.
- if (parser.size() > 0) {
- // User settings take precedence. Just stick with the Settings constants, even if there
- // are invalid values. It's not worth the time to evaluate all the key/value pairs to
- // make sure there are valid ones before deciding.
- return Math.max(minValCake,
- parseCreditValue(parser.getString(key, null), defaultValCake));
- }
- if (properties != null) {
- return Math.max(minValCake,
- parseCreditValue(properties.getString(key, null), defaultValCake));
- }
- return Math.max(minValCake, defaultValCake);
+ protected long getConstantAsCake(String key, long defaultValCake, long minValCake) {
+ return Math.max(minValCake,
+ parseCreditValue(
+ mUserSettingDeviceConfigMediator.getString(key, null), defaultValCake));
}
@VisibleForTesting
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index 91a291f..69e5736 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -127,7 +127,6 @@
import android.content.ContentResolver;
import android.provider.DeviceConfig;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.SparseArray;
@@ -168,7 +167,6 @@
private long mMinSatiatedConsumptionLimit;
private long mMaxSatiatedConsumptionLimit;
- private final KeyValueListParser mParser = new KeyValueListParser(',');
private final Injector mInjector;
private final SparseArray<Action> mActions = new SparseArray<>();
@@ -274,176 +272,177 @@
mRewards.clear();
try {
- mParser.setString(policyValuesString);
+ mUserSettingDeviceConfigMediator.setSettingsString(policyValuesString);
+ mUserSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Global setting key incorrect: ", e);
}
- mMinSatiatedBalanceOther = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceOther = getConstantAsCake(
KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES);
- mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake(
KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES,
mMinSatiatedBalanceOther);
- mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceExempted = getConstantAsCake(
KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED,
DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
mMinSatiatedBalanceHeadlessSystemApp);
- mMinSatiatedBalanceIncrementalAppUpdater = getConstantAsCake(mParser, properties,
+ mMinSatiatedBalanceIncrementalAppUpdater = getConstantAsCake(
KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES);
- mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
+ mMaxSatiatedBalance = getConstantAsCake(
KEY_JS_MAX_SATIATED_BALANCE, DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
- mMinSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mMinSatiatedConsumptionLimit = getConstantAsCake(
KEY_JS_MIN_CONSUMPTION_LIMIT, DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES,
arcToCake(1));
- mInitialSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mInitialSatiatedConsumptionLimit = getConstantAsCake(
KEY_JS_INITIAL_CONSUMPTION_LIMIT, DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES,
mMinSatiatedConsumptionLimit);
- mMaxSatiatedConsumptionLimit = getConstantAsCake(mParser, properties,
+ mMaxSatiatedConsumptionLimit = getConstantAsCake(
KEY_JS_MAX_CONSUMPTION_LIMIT, DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES,
mInitialSatiatedConsumptionLimit);
mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MAX_START_CTP,
DEFAULT_JS_ACTION_JOB_MAX_START_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_MAX_RUNNING, new Action(ACTION_JOB_MAX_RUNNING,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MAX_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_HIGH_START, new Action(ACTION_JOB_HIGH_START,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_HIGH_START_CTP,
DEFAULT_JS_ACTION_JOB_HIGH_START_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_HIGH_RUNNING, new Action(ACTION_JOB_HIGH_RUNNING,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_DEFAULT_START, new Action(ACTION_JOB_DEFAULT_START,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_DEFAULT_START_CTP,
DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_DEFAULT_RUNNING, new Action(ACTION_JOB_DEFAULT_RUNNING,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_LOW_START, new Action(ACTION_JOB_LOW_START,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_LOW_START_CTP,
DEFAULT_JS_ACTION_JOB_LOW_START_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_LOW_RUNNING, new Action(ACTION_JOB_LOW_RUNNING,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_LOW_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_MIN_START, new Action(ACTION_JOB_MIN_START,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MIN_START_CTP,
DEFAULT_JS_ACTION_JOB_MIN_START_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_MIN_RUNNING, new Action(ACTION_JOB_MIN_RUNNING,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MIN_RUNNING_CTP,
DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE_CAKES)));
mActions.put(ACTION_JOB_TIMEOUT, new Action(ACTION_JOB_TIMEOUT,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP,
DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE,
DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE_CAKES)));
mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_TOP_ACTIVITY_INSTANT,
DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_TOP_ACTIVITY_ONGOING,
DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_TOP_ACTIVITY_MAX,
DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX_CAKES)));
mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT,
DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING,
DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_SEEN_MAX,
DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX_CAKES)));
mRewards.put(REWARD_NOTIFICATION_INTERACTION,
new Reward(REWARD_NOTIFICATION_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT,
DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING,
DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX,
DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES)));
mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT,
DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING,
DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_WIDGET_INTERACTION_MAX,
DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX_CAKES)));
mRewards.put(REWARD_OTHER_USER_INTERACTION,
new Reward(REWARD_OTHER_USER_INTERACTION,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT,
DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING,
DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX,
DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES)));
mRewards.put(REWARD_APP_INSTALL,
new Reward(REWARD_APP_INSTALL,
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_APP_INSTALL_INSTANT,
DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_APP_INSTALL_ONGOING,
DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES),
- getConstantAsCake(mParser, properties,
+ getConstantAsCake(
KEY_JS_REWARD_APP_INSTALL_MAX,
DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES)));
}
diff --git a/api/javadoc-lint-baseline b/api/javadoc-lint-baseline
index a4174ee..7992702 100644
--- a/api/javadoc-lint-baseline
+++ b/api/javadoc-lint-baseline
@@ -1,17 +1,3 @@
-// b/303477132
-android/app/appsearch/AppSearchSchema.java:402: lint: Unresolved link/see tag "#getIndexableNestedProperties()" in android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder [101]
-android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.AppSearchSession [101]
-android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.AppSearchSession [101]
-android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#VERBATIM_SEARCH" in android.app.appsearch.AppSearchSession [101]
-android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#isFeatureSupported" in android.app.appsearch.AppSearchSession [101]
-android/app/appsearch/JoinSpec.java:219: lint: Unresolved link/see tag "android.app.appsearch.SearchSpec.RankingStrategy#RANKING_STRATEGY_JOIN_AGGREGATE_SCORE SearchSpec.RankingStrategy#RANKING_STRATEGY_JOIN_AGGREGATE_SCORE" in android.app.appsearch.JoinSpec.Builder [101]
-android/app/appsearch/SearchSpec.java:230: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.SearchSpec [101]
-android/app/appsearch/SearchSpec.java:237: lint: Unresolved link/see tag "Features#VERBATIM_SEARCH" in android.app.appsearch.SearchSpec [101]
-android/app/appsearch/SearchSpec.java:244: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.SearchSpec [101]
-android/app/appsearch/SearchSpec.java:913: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.SearchSpec.Builder [101]
-android/app/appsearch/SearchSpec.java:925: lint: Unresolved link/see tag "Features#VERBATIM_SEARCH" in android.app.appsearch.SearchSpec.Builder [101]
-android/app/appsearch/SearchSpec.java:929: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.SearchSpec.Builder [101]
-
// b/303582215
android/hardware/camera2/CameraCharacteristics.java:2169: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
android/hardware/camera2/CameraCharacteristics.java:2344: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
index ec2b1f4..a78a465 100644
--- a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
+++ b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
@@ -96,9 +96,9 @@
return env;
}
-std::unique_ptr<UinputDevice> UinputDevice::open(int32_t id, const char* name, int32_t vid,
- int32_t pid, uint16_t bus, uint32_t ffEffectsMax,
- const char* port,
+std::unique_ptr<UinputDevice> UinputDevice::open(int32_t id, const char* name, int32_t vendorId,
+ int32_t productId, int32_t versionId, uint16_t bus,
+ uint32_t ffEffectsMax, const char* port,
std::unique_ptr<DeviceCallback> callback) {
android::base::unique_fd fd(::open(UINPUT_PATH, O_RDWR | O_NONBLOCK | O_CLOEXEC));
if (!fd.ok()) {
@@ -118,8 +118,9 @@
strlcpy(setupDescriptor.name, name, UINPUT_MAX_NAME_SIZE);
setupDescriptor.id.version = 1;
setupDescriptor.id.bustype = bus;
- setupDescriptor.id.vendor = vid;
- setupDescriptor.id.product = pid;
+ setupDescriptor.id.vendor = vendorId;
+ setupDescriptor.id.product = productId;
+ setupDescriptor.id.version = versionId;
setupDescriptor.ff_effects_max = ffEffectsMax;
// Request device configuration.
@@ -242,9 +243,9 @@
return data;
}
-static jlong openUinputDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid,
- jint pid, jint bus, jint ffEffectsMax, jstring rawPort,
- jobject callback) {
+static jlong openUinputDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id,
+ jint vendorId, jint productId, jint versionId, jint bus,
+ jint ffEffectsMax, jstring rawPort, jobject callback) {
ScopedUtfChars name(env, rawName);
if (name.c_str() == nullptr) {
return 0;
@@ -255,8 +256,8 @@
std::make_unique<uinput::DeviceCallback>(env, callback);
std::unique_ptr<uinput::UinputDevice> d =
- uinput::UinputDevice::open(id, name.c_str(), vid, pid, bus, ffEffectsMax, port.c_str(),
- std::move(cb));
+ uinput::UinputDevice::open(id, name.c_str(), vendorId, productId, versionId, bus,
+ ffEffectsMax, port.c_str(), std::move(cb));
return reinterpret_cast<jlong>(d.release());
}
@@ -326,7 +327,7 @@
static JNINativeMethod sMethods[] = {
{"nativeOpenUinputDevice",
- "(Ljava/lang/String;IIIIILjava/lang/String;"
+ "(Ljava/lang/String;IIIIIILjava/lang/String;"
"Lcom/android/commands/uinput/Device$DeviceCallback;)J",
reinterpret_cast<void*>(openUinputDevice)},
{"nativeInjectEvent", "(JIII)V", reinterpret_cast<void*>(injectEvent)},
diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.h b/cmds/uinput/jni/com_android_commands_uinput_Device.h
index 6da3d79..9769a75 100644
--- a/cmds/uinput/jni/com_android_commands_uinput_Device.h
+++ b/cmds/uinput/jni/com_android_commands_uinput_Device.h
@@ -46,9 +46,9 @@
class UinputDevice {
public:
- static std::unique_ptr<UinputDevice> open(int32_t id, const char* name, int32_t vid,
- int32_t pid, uint16_t bus, uint32_t ff_effects_max,
- const char* port,
+ static std::unique_ptr<UinputDevice> open(int32_t id, const char* name, int32_t vendorId,
+ int32_t productId, int32_t versionId, uint16_t bus,
+ uint32_t ff_effects_max, const char* port,
std::unique_ptr<DeviceCallback> callback);
virtual ~UinputDevice();
diff --git a/cmds/uinput/src/com/android/commands/uinput/Device.java b/cmds/uinput/src/com/android/commands/uinput/Device.java
index b0fa34c..787055c 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Device.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Device.java
@@ -61,8 +61,9 @@
System.loadLibrary("uinputcommand_jni");
}
- private static native long nativeOpenUinputDevice(String name, int id, int vid, int pid,
- int bus, int ffEffectsMax, String port, DeviceCallback callback);
+ private static native long nativeOpenUinputDevice(String name, int id, int vendorId,
+ int productId, int versionId, int bus, int ffEffectsMax, String port,
+ DeviceCallback callback);
private static native void nativeCloseUinputDevice(long ptr);
private static native void nativeInjectEvent(long ptr, int type, int code, int value);
private static native void nativeConfigure(int handle, int code, int[] configs);
@@ -71,7 +72,7 @@
private static native int nativeGetEvdevEventCodeByLabel(int type, String label);
private static native int nativeGetEvdevInputPropByLabel(String label);
- public Device(int id, String name, int vid, int pid, int bus,
+ public Device(int id, String name, int vendorId, int productId, int versionId, int bus,
SparseArray<int[]> configuration, int ffEffectsMax,
SparseArray<InputAbsInfo> absInfo, String port) {
mId = id;
@@ -83,19 +84,20 @@
mOutputStream = System.out;
SomeArgs args = SomeArgs.obtain();
args.argi1 = id;
- args.argi2 = vid;
- args.argi3 = pid;
- args.argi4 = bus;
- args.argi5 = ffEffectsMax;
+ args.argi2 = vendorId;
+ args.argi3 = productId;
+ args.argi4 = versionId;
+ args.argi5 = bus;
+ args.argi6 = ffEffectsMax;
if (name != null) {
args.arg1 = name;
} else {
- args.arg1 = id + ":" + vid + ":" + pid;
+ args.arg1 = id + ":" + vendorId + ":" + productId;
}
if (port != null) {
args.arg2 = port;
} else {
- args.arg2 = "uinput:" + id + ":" + vid + ":" + pid;
+ args.arg2 = "uinput:" + id + ":" + vendorId + ":" + productId;
}
mHandler.obtainMessage(MSG_OPEN_UINPUT_DEVICE, args).sendToTarget();
@@ -161,8 +163,10 @@
case MSG_OPEN_UINPUT_DEVICE:
SomeArgs args = (SomeArgs) msg.obj;
String name = (String) args.arg1;
- mPtr = nativeOpenUinputDevice(name, args.argi1, args.argi2,
- args.argi3, args.argi4, args.argi5, (String) args.arg2,
+ mPtr = nativeOpenUinputDevice(name, args.argi1 /* id */,
+ args.argi2 /* vendorId */, args.argi3 /* productId */,
+ args.argi4 /* versionId */, args.argi5 /* bus */,
+ args.argi6 /* ffEffectsMax */, (String) args.arg2 /* port */,
new DeviceCallback());
if (mPtr == 0) {
RuntimeException ex = new RuntimeException(
diff --git a/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
index b89e2cd..7652f24 100644
--- a/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
+++ b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
@@ -19,8 +19,8 @@
import android.annotation.Nullable;
import android.util.SparseArray;
-import java.io.BufferedReader;
import java.io.IOException;
+import java.io.LineNumberReader;
import java.io.Reader;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -47,10 +47,11 @@
private static final int REGISTRATION_DELAY_MILLIS = 500;
private static class CommentAwareReader {
- private final BufferedReader mReader;
+ private final LineNumberReader mReader;
+ private String mPreviousLine;
private String mNextLine;
- CommentAwareReader(BufferedReader in) throws IOException {
+ CommentAwareReader(LineNumberReader in) throws IOException {
mReader = in;
mNextLine = findNextLine();
}
@@ -90,12 +91,46 @@
/** Moves to the next line of the file. */
public void advance() throws IOException {
+ mPreviousLine = mNextLine;
mNextLine = findNextLine();
}
public boolean isAtEndOfFile() {
return mNextLine == null;
}
+
+ /** Returns the previous line, for error messages. */
+ public String getPreviousLine() {
+ return mPreviousLine;
+ }
+
+ /** Returns the number of the <b>previous</b> line. */
+ public int getPreviousLineNumber() {
+ return mReader.getLineNumber() - 1;
+ }
+ }
+
+ public static class ParsingException extends RuntimeException {
+ private final int mLineNumber;
+ private final String mLine;
+
+ ParsingException(String message, CommentAwareReader reader) {
+ this(message, reader.getPreviousLine(), reader.getPreviousLineNumber());
+ }
+
+ ParsingException(String message, String line, int lineNumber) {
+ super(message);
+ mLineNumber = lineNumber;
+ mLine = line;
+ }
+
+ /** Returns a nicely formatted error message, including the line number and line. */
+ public String makeErrorMessage() {
+ return String.format("""
+ Parsing error on line %d: %s
+ --> %s
+ """, mLineNumber, getMessage(), mLine);
+ }
}
private final CommentAwareReader mReader;
@@ -107,7 +142,7 @@
private final Queue<Event> mQueuedEvents = new ArrayDeque<>(2);
public EvemuParser(Reader in) throws IOException {
- mReader = new CommentAwareReader(new BufferedReader(in));
+ mReader = new CommentAwareReader(new LineNumberReader(in));
mQueuedEvents.add(parseRegistrationEvent());
// The kernel takes a little time to set up an evdev device after the initial
@@ -133,20 +168,22 @@
return null;
}
- final String[] parts = expectLineWithParts("E", 4);
+ final String line = expectLine("E");
+ final String[] parts = expectParts(line, 4);
final String[] timeParts = parts[0].split("\\.");
if (timeParts.length != 2) {
- throw new RuntimeException("Invalid timestamp (does not contain a '.')");
+ throw new ParsingException(
+ "Invalid timestamp '" + parts[0] + "' (should contain a single '.')", mReader);
}
// TODO(b/310958309): use timeMicros to set the timestamp on the event being sent.
final long timeMicros =
- Long.parseLong(timeParts[0]) * 1_000_000 + Integer.parseInt(timeParts[1]);
+ parseLong(timeParts[0], 10) * 1_000_000 + parseInt(timeParts[1], 10);
final Event.Builder eb = new Event.Builder();
eb.setId(DEVICE_ID);
eb.setCommand(Event.Command.INJECT);
- final int eventType = Integer.parseInt(parts[1], 16);
- final int eventCode = Integer.parseInt(parts[2], 16);
- final int value = Integer.parseInt(parts[3]);
+ final int eventType = parseInt(parts[1], 16);
+ final int eventCode = parseInt(parts[2], 16);
+ final int value = parseInt(parts[3], 10);
eb.setInjections(new int[] {eventType, eventCode, value});
if (mLastEventTimeMicros == -1) {
@@ -184,11 +221,12 @@
eb.setCommand(Event.Command.REGISTER);
eb.setName(expectLine("N"));
- final String[] idStrings = expectLineWithParts("I", 4);
- eb.setBusId(Integer.parseInt(idStrings[0], 16));
- eb.setVid(Integer.parseInt(idStrings[1], 16));
- eb.setPid(Integer.parseInt(idStrings[2], 16));
- // TODO(b/302297266): support setting the version ID, and set it to idStrings[3].
+ final String idsLine = expectLine("I");
+ final String[] idStrings = expectParts(idsLine, 4);
+ eb.setBusId(parseInt(idStrings[0], 16));
+ eb.setVendorId(parseInt(idStrings[1], 16));
+ eb.setProductId(parseInt(idStrings[2], 16));
+ eb.setVersionId(parseInt(idStrings[3], 16));
final SparseArray<int[]> config = new SparseArray<>();
config.append(Event.UinputControlCode.UI_SET_PROPBIT.getValue(), parseProperties());
@@ -215,33 +253,39 @@
}
private int[] parseProperties() throws IOException {
- final List<String> propBitmapParts = new ArrayList<>();
+ final ArrayList<Integer> propBitmapParts = new ArrayList<>();
String line = acceptLine("P");
while (line != null) {
- propBitmapParts.addAll(List.of(line.strip().split(" ")));
+ String[] parts = line.strip().split(" ");
+ propBitmapParts.ensureCapacity(propBitmapParts.size() + parts.length);
+ for (String part : parts) {
+ propBitmapParts.add(parseBitmapPart(part, line));
+ }
line = acceptLine("P");
}
- return hexStringBitmapToEventCodes(propBitmapParts);
+ return bitmapToEventCodes(propBitmapParts);
}
private void parseAxisBitmaps(SparseArray<int[]> config) throws IOException {
- final Map<Integer, List<String>> axisBitmapParts = new HashMap<>();
+ final Map<Integer, ArrayList<Integer>> axisBitmapParts = new HashMap<>();
String line = acceptLine("B");
while (line != null) {
final String[] parts = line.strip().split(" ");
if (parts.length < 2) {
- throw new RuntimeException(
+ throw new ParsingException(
"Expected event type and at least one bitmap byte on 'B:' line; only found "
- + parts.length + " elements");
+ + parts.length + " elements", mReader);
}
- final int eventType = Integer.parseInt(parts[0], 16);
+ final int eventType = parseInt(parts[0], 16);
// EV_SYN cannot be configured through uinput, so skip it.
if (eventType != Event.EV_SYN) {
if (!axisBitmapParts.containsKey(eventType)) {
axisBitmapParts.put(eventType, new ArrayList<>());
}
+ ArrayList<Integer> bitmapParts = axisBitmapParts.get(eventType);
+ bitmapParts.ensureCapacity(bitmapParts.size() + parts.length);
for (int i = 1; i < parts.length; i++) {
- axisBitmapParts.get(eventType).add(parts[i]);
+ axisBitmapParts.get(eventType).add(parseBitmapPart(parts[i], line));
}
}
line = acceptLine("B");
@@ -253,7 +297,7 @@
}
final Event.UinputControlCode controlCode =
Event.UinputControlCode.forEventType(entry.getKey());
- final int[] eventCodes = hexStringBitmapToEventCodes(entry.getValue());
+ final int[] eventCodes = bitmapToEventCodes(entry.getValue());
if (controlCode != null && eventCodes.length > 0) {
config.append(controlCode.getValue(), eventCodes);
eventTypesToSet.add(entry.getKey());
@@ -263,24 +307,33 @@
Event.UinputControlCode.UI_SET_EVBIT.getValue(), unboxIntList(eventTypesToSet));
}
+ private int parseBitmapPart(String part, String line) {
+ int b = parseInt(part, 16);
+ if (b < 0x0 || b > 0xff) {
+ throw new ParsingException("Bitmap part '" + part
+ + "' invalid; parts must be hexadecimal values between 00 and ff.", mReader);
+ }
+ return b;
+ }
+
private SparseArray<InputAbsInfo> parseAbsInfos() throws IOException {
final SparseArray<InputAbsInfo> absInfos = new SparseArray<>();
String line = acceptLine("A");
while (line != null) {
final String[] parts = line.strip().split(" ");
if (parts.length < 5 || parts.length > 6) {
- throw new RuntimeException(
- "'A:' lines should have the format 'A: <index (hex)> <min> <max> <fuzz> "
+ throw new ParsingException(
+ "AbsInfo lines should have the format 'A: <index (hex)> <min> <max> <fuzz> "
+ "<flat> [<resolution>]'; expected 5 or 6 numbers but found "
- + parts.length);
+ + parts.length, mReader);
}
- final int axisCode = Integer.parseInt(parts[0], 16);
+ final int axisCode = parseInt(parts[0], 16);
final InputAbsInfo info = new InputAbsInfo();
- info.minimum = Integer.parseInt(parts[1]);
- info.maximum = Integer.parseInt(parts[2]);
- info.fuzz = Integer.parseInt(parts[3]);
- info.flat = Integer.parseInt(parts[4]);
- info.resolution = parts.length > 5 ? Integer.parseInt(parts[5]) : 0;
+ info.minimum = parseInt(parts[1], 10);
+ info.maximum = parseInt(parts[2], 10);
+ info.fuzz = parseInt(parts[3], 10);
+ info.flat = parseInt(parts[4], 10);
+ info.resolution = parts.length > 5 ? parseInt(parts[5], 10) : 0;
absInfos.append(axisCode, info);
line = acceptLine("A");
}
@@ -305,7 +358,9 @@
private String expectLine(String type) throws IOException {
final String line = acceptLine(type);
if (line == null) {
- throw new RuntimeException("Expected line of type '" + type + "'");
+ throw new ParsingException("Expected line of type '" + type + "'. (Lines should be in "
+ + "the order N, I, P, B, A, L, S, E.)",
+ mReader.peekLine(), mReader.getPreviousLineNumber() + 1);
} else {
return line;
}
@@ -325,9 +380,8 @@
}
final String[] lineParts = line.split(": ", 2);
if (lineParts.length < 2) {
- // TODO(b/302297266): make a proper exception class for syntax errors, including line
- // numbers, etc.. (We can use LineNumberReader to track them.)
- throw new RuntimeException("Line without ': '");
+ throw new ParsingException("Missing type separator ': '",
+ line, mReader.getPreviousLineNumber() + 1);
}
if (lineParts[0].equals(type)) {
mReader.advance();
@@ -337,31 +391,37 @@
}
}
- /**
- * Like {@link #expectLine(String)}, but also checks that the contents of the line is formed of
- * {@code numParts} space-separated parts.
- *
- * @param type the type of the line to expect, represented by the letter before the ':'.
- * @param numParts the number of parts to expect.
- * @return the part of the line after the ": ", split into {@code numParts} sections.
- */
- private String[] expectLineWithParts(String type, int numParts) throws IOException {
- final String[] parts = expectLine(type).strip().split(" ");
+ private String[] expectParts(String line, int numParts) {
+ final String[] parts = line.strip().split(" ");
if (parts.length != numParts) {
- throw new RuntimeException("Expected a '" + type + "' line with " + numParts
- + " parts, found one with " + parts.length);
+ throw new ParsingException(
+ "Expected a line with " + numParts + " space-separated parts, but found one "
+ + "with " + parts.length, mReader);
}
return parts;
}
- private static int[] hexStringBitmapToEventCodes(List<String> strs) {
+ private int parseInt(String s, int radix) {
+ try {
+ return Integer.parseInt(s, radix);
+ } catch (NumberFormatException ex) {
+ throw new ParsingException(
+ "'" + s + "' is not a valid integer of base " + radix, mReader);
+ }
+ }
+
+ private long parseLong(String s, int radix) {
+ try {
+ return Long.parseLong(s, radix);
+ } catch (NumberFormatException ex) {
+ throw new ParsingException("'" + s + "' is not a valid long of base " + radix, mReader);
+ }
+ }
+
+ private static int[] bitmapToEventCodes(List<Integer> bytes) {
final List<Integer> codes = new ArrayList<>();
- for (int iByte = 0; iByte < strs.size(); iByte++) {
- int b = Integer.parseInt(strs.get(iByte), 16);
- if (b < 0x0 || b > 0xff) {
- throw new RuntimeException("Bitmap part '" + strs.get(iByte)
- + "' invalid; parts must be between 00 and ff.");
- }
+ for (int iByte = 0; iByte < bytes.size(); iByte++) {
+ int b = bytes.get(iByte);
for (int iBit = 0; iBit < 8; iBit++) {
if ((b & 1) != 0) {
codes.add(iByte * 8 + iBit);
diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java
index 5ec40e5..0f16a27 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Event.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -94,8 +94,9 @@
private int mId;
private Command mCommand;
private String mName;
- private int mVid;
- private int mPid;
+ private int mVendorId;
+ private int mProductId;
+ private int mVersionId;
private int mBusId;
private int[] mInjections;
private SparseArray<int[]> mConfiguration;
@@ -118,11 +119,15 @@
}
public int getVendorId() {
- return mVid;
+ return mVendorId;
}
public int getProductId() {
- return mPid;
+ return mProductId;
+ }
+
+ public int getVersionId() {
+ return mVersionId;
}
public int getBus() {
@@ -172,8 +177,8 @@
return "Event{id=" + mId
+ ", command=" + mCommand
+ ", name=" + mName
- + ", vid=" + mVid
- + ", pid=" + mPid
+ + ", vid=" + mVendorId
+ + ", pid=" + mProductId
+ ", busId=" + mBusId
+ ", events=" + Arrays.toString(mInjections)
+ ", configuration=" + mConfiguration
@@ -216,12 +221,16 @@
mEvent.mConfiguration = configuration;
}
- public void setVid(int vid) {
- mEvent.mVid = vid;
+ public void setVendorId(int vendorId) {
+ mEvent.mVendorId = vendorId;
}
- public void setPid(int pid) {
- mEvent.mPid = pid;
+ public void setProductId(int productId) {
+ mEvent.mProductId = productId;
+ }
+
+ public void setVersionId(int versionId) {
+ mEvent.mVersionId = versionId;
}
public void setBusId(int busId) {
diff --git a/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
index 888ec5a..ed3ff33 100644
--- a/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
+++ b/cmds/uinput/src/com/android/commands/uinput/JsonStyleParser.java
@@ -60,8 +60,8 @@
case "command" -> eb.setCommand(
Event.Command.valueOf(mReader.nextString().toUpperCase()));
case "name" -> eb.setName(mReader.nextString());
- case "vid" -> eb.setVid(readInt());
- case "pid" -> eb.setPid(readInt());
+ case "vid" -> eb.setVendorId(readInt());
+ case "pid" -> eb.setProductId(readInt());
case "bus" -> eb.setBusId(readBus());
case "events" -> {
int[] injections = readInjectedEvents().stream()
diff --git a/cmds/uinput/src/com/android/commands/uinput/Uinput.java b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
index 684a12f..04df279 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Uinput.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
@@ -60,6 +60,10 @@
stream = new FileInputStream(f);
}
(new Uinput(stream)).run();
+ } catch (EvemuParser.ParsingException e) {
+ System.err.println(e.makeErrorMessage());
+ error(e.makeErrorMessage(), e);
+ System.exit(1);
} catch (Exception e) {
error("Uinput injection failed.", e);
System.exit(1);
@@ -142,8 +146,9 @@
"Tried to send command \"" + e.getCommand() + "\" to an unregistered device!");
}
int id = e.getId();
- Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), e.getBus(),
- e.getConfiguration(), e.getFfEffectsMax(), e.getAbsInfo(), e.getPort());
+ Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(),
+ e.getVersionId(), e.getBus(), e.getConfiguration(), e.getFfEffectsMax(),
+ e.getAbsInfo(), e.getPort());
mDevices.append(id, d);
}
diff --git a/cmds/uinput/tests/src/com/android/commands/uinput/tests/EvemuParserTest.java b/cmds/uinput/tests/src/com/android/commands/uinput/tests/EvemuParserTest.java
index 6ee987f..06b0aac2 100644
--- a/cmds/uinput/tests/src/com/android/commands/uinput/tests/EvemuParserTest.java
+++ b/cmds/uinput/tests/src/com/android/commands/uinput/tests/EvemuParserTest.java
@@ -19,6 +19,8 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.fail;
+
import android.platform.test.annotations.Postsubmit;
import android.util.SparseArray;
@@ -68,7 +70,7 @@
assertThat(event.getBus()).isEqualTo(0x0001);
assertThat(event.getVendorId()).isEqualTo(0x1234);
assertThat(event.getProductId()).isEqualTo(0x5678);
- // TODO(b/302297266): check version ID once it's supported
+ assertThat(event.getVersionId()).isEqualTo(0x9abc);
}
@Test
@@ -241,6 +243,25 @@
}
@Test
+ public void testErrorLineNumberReporting() throws IOException {
+ StringReader reader = new StringReader("""
+ # EVEMU 1.3
+ N: ACME Widget
+ # Comment to make sure they're taken into account when numbering lines
+ I: 0001 1234 5678 9abc
+ 00 00 00 00 00 00 00 00 # Missing a type
+ E: 0.000001 0001 0015 0001 # KEY_Y press
+ E: 0.000001 0000 0000 0000 # SYN_REPORT
+ """);
+ try {
+ new EvemuParser(reader);
+ fail("Parser should have thrown an error about the line with the missing type.");
+ } catch (EvemuParser.ParsingException ex) {
+ assertThat(ex.makeErrorMessage()).startsWith("Parsing error on line 5:");
+ }
+ }
+
+ @Test
public void testFreeDesktopEvemuRecording() throws IOException {
// This is a real recording from FreeDesktop's evemu-record tool, as a basic compatibility
// check with the FreeDesktop tools.
diff --git a/core/api/current.txt b/core/api/current.txt
index 9d13d8a..09abe2b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9342,6 +9342,7 @@
method public String getClassName();
method public android.content.res.Configuration getConfiguration();
method public int getEventType();
+ method @FlaggedApi("android.app.usage.user_interaction_type_api") @NonNull public android.os.PersistableBundle getExtras();
method public String getPackageName();
method public String getShortcutId();
method public long getTimeStamp();
@@ -9407,6 +9408,8 @@
method @FlaggedApi("android.app.usage.filter_based_event_query_api") @NonNull @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public android.app.usage.UsageEvents queryEvents(@NonNull android.app.usage.UsageEventsQuery);
method public android.app.usage.UsageEvents queryEventsForSelf(long, long);
method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
+ field @FlaggedApi("android.app.usage.user_interaction_type_api") public static final String EXTRA_EVENT_ACTION = "android.app.usage.extra.EVENT_ACTION";
+ field @FlaggedApi("android.app.usage.user_interaction_type_api") public static final String EXTRA_EVENT_CATEGORY = "android.app.usage.extra.EVENT_CATEGORY";
field public static final int INTERVAL_BEST = 4; // 0x4
field public static final int INTERVAL_DAILY = 0; // 0x0
field public static final int INTERVAL_MONTHLY = 2; // 0x2
@@ -9851,7 +9854,7 @@
method @NonNull public android.content.AttributionSource build();
method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@Nullable String);
method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @NonNull public android.content.AttributionSource.Builder setDeviceId(int);
- method @Deprecated @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
+ method @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
method @FlaggedApi("android.permission.flags.set_next_attribution_source") @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
method @NonNull public android.content.AttributionSource.Builder setPackageName(@Nullable String);
method @NonNull public android.content.AttributionSource.Builder setPid(int);
@@ -32837,7 +32840,7 @@
method public static android.os.Message obtain(android.os.Handler, int, Object);
method public static android.os.Message obtain(android.os.Handler, int, int, int);
method public static android.os.Message obtain(android.os.Handler, int, int, int, Object);
- method public android.os.Bundle peekData();
+ method @Nullable public android.os.Bundle peekData();
method public void recycle();
method public void sendToTarget();
method public void setAsynchronous(boolean);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 01ca6d9..fc23f9b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -258,6 +258,7 @@
field public static final String PERFORM_IMS_SINGLE_REGISTRATION = "android.permission.PERFORM_IMS_SINGLE_REGISTRATION";
field public static final String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION";
field public static final String POWER_SAVER = "android.permission.POWER_SAVER";
+ field @FlaggedApi("android.permission.flags.factory_reset_prep_permission_apis") public static final String PREPARE_FACTORY_RESET = "android.permission.PREPARE_FACTORY_RESET";
field public static final String PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE = "android.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE";
field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
@@ -4211,10 +4212,18 @@
public final class UserProperties implements android.os.Parcelable {
method public int describeContents();
+ method public int getShowInQuietMode();
+ method public int getShowInSharingSurfaces();
method public boolean isCredentialShareableWithParent();
method public boolean isMediaSharedWithParent();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserProperties> CREATOR;
+ field public static final int SHOW_IN_QUIET_MODE_DEFAULT = 2; // 0x2
+ field public static final int SHOW_IN_QUIET_MODE_HIDDEN = 1; // 0x1
+ field public static final int SHOW_IN_QUIET_MODE_PAUSED = 0; // 0x0
+ field public static final int SHOW_IN_SHARING_SURFACES_NO = 2; // 0x2
+ field public static final int SHOW_IN_SHARING_SURFACES_SEPARATE = 1; // 0x1
+ field public static final int SHOW_IN_SHARING_SURFACES_WITH_PARENT = 0; // 0x0
}
}
@@ -10560,6 +10569,7 @@
method @NonNull public java.util.List<android.os.UserHandle> getEnabledProfiles();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public android.os.UserHandle getMainUser();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public android.os.UserHandle getPreviousForegroundUser();
+ method @NonNull public String getProfileLabel();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableProfileCount(@NonNull String);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableUserCount(@NonNull String);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f3bad3a..f4c8429 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -780,6 +780,25 @@
}
+package android.app.pinner {
+
+ @FlaggedApi("android.app.pinner_service_client_api") public final class PinnedFileStat implements android.os.Parcelable {
+ ctor @FlaggedApi("android.app.pinner_service_client_api") public PinnedFileStat(@NonNull String, long, @NonNull String);
+ method @FlaggedApi("android.app.pinner_service_client_api") public int describeContents();
+ method @FlaggedApi("android.app.pinner_service_client_api") public long getBytesPinned();
+ method @FlaggedApi("android.app.pinner_service_client_api") @NonNull public String getFilename();
+ method @FlaggedApi("android.app.pinner_service_client_api") @NonNull public String getGroupName();
+ method @FlaggedApi("android.app.pinner_service_client_api") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("android.app.pinner_service_client_api") @NonNull public static final android.os.Parcelable.Creator<android.app.pinner.PinnedFileStat> CREATOR;
+ }
+
+ @FlaggedApi("android.app.pinner_service_client_api") public class PinnerServiceClient {
+ ctor @FlaggedApi("android.app.pinner_service_client_api") public PinnerServiceClient();
+ method @FlaggedApi("android.app.pinner_service_client_api") @NonNull public java.util.List<android.app.pinner.PinnedFileStat> getPinnerStats();
+ }
+
+}
+
package android.app.prediction {
public final class AppPredictor {
@@ -4201,8 +4220,13 @@
public static class WindowInfosListenerForTest.WindowInfo {
field @NonNull public final android.graphics.Rect bounds;
field public final int displayId;
+ field public final boolean isDuplicateTouchToWallpaper;
+ field public final boolean isFocusable;
+ field public final boolean isPreventSplitting;
+ field public final boolean isTouchable;
field public final boolean isTrustedOverlay;
field public final boolean isVisible;
+ field public final boolean isWatchOutsideTouch;
field @NonNull public final String name;
field @NonNull public final android.graphics.Matrix transform;
field @NonNull public final android.os.IBinder windowToken;
diff --git a/core/java/Android.bp b/core/java/Android.bp
index dfe3344..fb1e16a 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -546,6 +546,15 @@
],
}
+// PackageManager common
+filegroup {
+ name: "framework-pm-common-shared-srcs",
+ srcs: [
+ "com/android/server/pm/pkg/AndroidPackage.java",
+ "com/android/server/pm/pkg/AndroidPackageSplit.java",
+ ],
+}
+
java_library {
name: "protolog-lib",
platform_apis: true,
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index a4c3bb8..87c86df 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2934,6 +2934,15 @@
}
@Override
+ public String getSuspendingPackage(String suspendedPackage) {
+ try {
+ return mPM.getSuspendingPackage(suspendedPackage, getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
public boolean isPackageSuspendedForUser(String packageName, int userId) {
try {
return mPM.isPackageSuspendedForUser(packageName, userId);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4f8e8dd..014ddd41 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3484,9 +3484,20 @@
// only do this if the user already has more than one preferred locale
if (android.content.res.Flags.defaultLocale()
&& r.getConfiguration().getLocales().size() > 1) {
- LocaleConfig lc = getUserId() < 0
- ? LocaleConfig.fromContextIgnoringOverride(this)
- : new LocaleConfig(this);
+ LocaleConfig lc;
+ if (getUserId() < 0) {
+ lc = LocaleConfig.fromContextIgnoringOverride(this);
+ } else {
+ // This is needed because the app might have locale config overrides that need to
+ // be read from disk in order for resources to correctly choose which values to
+ // load.
+ StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
+ try {
+ lc = new LocaleConfig(this);
+ } finally {
+ StrictMode.setThreadPolicy(policy);
+ }
+ }
mResourcesManager.setLocaleConfig(lc);
}
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index df6badc..d540748 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -328,7 +328,7 @@
* A splash screen view has copied.
*/
void onSplashScreenViewCopyFinished(int taskId,
- in SplashScreenView.SplashScreenViewParcelable material);
+ in @nullable SplashScreenView.SplashScreenViewParcelable material);
/**
* When the Picture-in-picture state has changed.
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index d8448dc..772b0b4 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -85,6 +85,9 @@
per-file IInstantAppResolver.aidl = file:/services/core/java/com/android/server/pm/OWNERS
per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS
+# Pinner
+per-file pinner-client.aconfig = file:/core/java/android/app/pinner/OWNERS
+
# ResourcesManager
per-file ResourcesManager.java = file:RESOURCES_OWNERS
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 6009c29..24a5157 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -159,7 +159,8 @@
* Loads {@link ApkAssets} and caches them to prevent their garbage collection while the
* instance is alive and reachable.
*/
- private class ApkAssetsSupplier {
+ @VisibleForTesting
+ protected class ApkAssetsSupplier {
final ArrayMap<ApkKey, ApkAssets> mLocalCache = new ArrayMap<>();
/**
@@ -544,7 +545,10 @@
* from an {@link ApkAssetsSupplier} if non-null; otherwise ApkAssets are loaded using
* {@link #loadApkAssets(ApkKey)}.
*/
- private @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
+
+ @VisibleForTesting
+ @UnsupportedAppUsage
+ protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
@Nullable ApkAssetsSupplier apkSupplier) {
final AssetManager.Builder builder = new AssetManager.Builder();
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 6a03c17..ce1d43d 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -180,6 +180,11 @@
@Override
public void injectInputEventToInputFilter(InputEvent event) throws RemoteException {
+ synchronized (mLock) {
+ throwIfCalledByNotTrustedUidLocked();
+ throwIfShutdownLocked();
+ throwIfNotConnectedLocked();
+ }
mAccessibilityManager.injectInputEventToInputFilter(event);
}
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 7be4b36..fb0edb9 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -24,8 +24,15 @@
flag {
name: "lifetime_extension_refactor"
- namespace: "systemui"
- description: "Enables moving notification lifetime extension management from SystemUI to "
- "Notification Manager Service"
- bug: "299448097"
-}
\ No newline at end of file
+ namespace: "systemui"
+ description: "Enables moving notification lifetime extension management from SystemUI to "
+ "Notification Manager Service"
+ bug: "299448097"
+}
+
+flag {
+ name: "visit_risky_uris"
+ namespace: "systemui"
+ description: "Guards the security fix that ensures all URIs in intents and Person.java are valid"
+ bug: "281044385"
+}
diff --git a/core/java/android/app/pinner-client.aconfig b/core/java/android/app/pinner-client.aconfig
new file mode 100644
index 0000000..b60ad9e
--- /dev/null
+++ b/core/java/android/app/pinner-client.aconfig
@@ -0,0 +1,8 @@
+package: "android.app"
+
+flag {
+ namespace: "system_performance"
+ name: "pinner_service_client_api"
+ description: "Control exposing PinnerService APIs."
+ bug: "307594624"
+}
\ No newline at end of file
diff --git a/core/java/android/app/pinner/IPinnerService.aidl b/core/java/android/app/pinner/IPinnerService.aidl
new file mode 100644
index 0000000..e5d0a05
--- /dev/null
+++ b/core/java/android/app/pinner/IPinnerService.aidl
@@ -0,0 +1,12 @@
+package android.app.pinner;
+
+import android.app.pinner.PinnedFileStat;
+
+/**
+ * Interface for processes to communicate with system's PinnerService.
+ * @hide
+ */
+interface IPinnerService {
+ @EnforcePermission("DUMP")
+ List<PinnedFileStat> getPinnerStats();
+}
\ No newline at end of file
diff --git a/core/java/android/app/pinner/OWNERS b/core/java/android/app/pinner/OWNERS
new file mode 100644
index 0000000..3e3fa66
--- /dev/null
+++ b/core/java/android/app/pinner/OWNERS
@@ -0,0 +1,10 @@
+carmenjackson@google.com
+dualli@google.com
+edgararriaga@google.com
+kevinjeon@google.com
+philipcuadra@google.com
+shombert@google.com
+timmurray@google.com
+wessam@google.com
+jdduke@google.com
+shayba@google.com
\ No newline at end of file
diff --git a/core/java/android/app/pinner/PinnedFileStat.aidl b/core/java/android/app/pinner/PinnedFileStat.aidl
new file mode 100644
index 0000000..44217cf
--- /dev/null
+++ b/core/java/android/app/pinner/PinnedFileStat.aidl
@@ -0,0 +1,3 @@
+package android.app.pinner;
+
+parcelable PinnedFileStat;
\ No newline at end of file
diff --git a/core/java/android/app/pinner/PinnedFileStat.java b/core/java/android/app/pinner/PinnedFileStat.java
new file mode 100644
index 0000000..2e36330
--- /dev/null
+++ b/core/java/android/app/pinner/PinnedFileStat.java
@@ -0,0 +1,133 @@
+/*
+ * 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.app.pinner;
+
+import static android.app.Flags.FLAG_PINNER_SERVICE_CLIENT_API;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+@TestApi
+@FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+public final class PinnedFileStat implements Parcelable {
+ private String filename;
+ private long bytesPinned;
+ private String groupName;
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ public long getBytesPinned() {
+ return bytesPinned;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ public @NonNull String getFilename() {
+ return filename;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ public @NonNull String getGroupName() {
+ return groupName;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ public PinnedFileStat(@NonNull String filename, long bytesPinned, @NonNull String groupName) {
+ this.filename = filename;
+ this.bytesPinned = bytesPinned;
+ this.groupName = groupName;
+ }
+
+ private PinnedFileStat(Parcel source) {
+ readFromParcel(source);
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(filename);
+ dest.writeLong(bytesPinned);
+ dest.writeString8(groupName);
+ }
+
+ private void readFromParcel(@NonNull Parcel source) {
+ filename = source.readString8();
+ bytesPinned = source.readLong();
+ groupName = source.readString8();
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ public static final @NonNull Creator<PinnedFileStat> CREATOR = new Creator<>() {
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ @Override
+ public PinnedFileStat createFromParcel(Parcel source) {
+ return new PinnedFileStat(source);
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ @Override
+ public PinnedFileStat[] newArray(int size) {
+ return new PinnedFileStat[size];
+ }
+ };
+}
diff --git a/core/java/android/app/pinner/PinnerServiceClient.java b/core/java/android/app/pinner/PinnerServiceClient.java
new file mode 100644
index 0000000..8b7c6cc
--- /dev/null
+++ b/core/java/android/app/pinner/PinnerServiceClient.java
@@ -0,0 +1,77 @@
+/*
+ * 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.app.pinner;
+
+import static android.app.Flags.FLAG_PINNER_SERVICE_CLIENT_API;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.app.pinner.IPinnerService;
+import android.app.pinner.PinnedFileStat;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Expose PinnerService as an interface to apps.
+ * @hide
+ */
+@TestApi
+@FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+public class PinnerServiceClient {
+ private static String TAG = "PinnerServiceClient";
+ /**
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ public PinnerServiceClient() {}
+
+ /**
+ * Obtain the pinned file stats used for testing infrastructure.
+ * @return List of pinned files or an empty list if failed to retrieve them.
+ * @throws RuntimeException on failure to retrieve stats.
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(FLAG_PINNER_SERVICE_CLIENT_API)
+ public @NonNull List<PinnedFileStat> getPinnerStats() {
+ IBinder binder = ServiceManager.getService("pinner");
+ if (binder == null) {
+ Slog.w(TAG,
+ "Failed to retrieve PinnerService. A common failure reason is due to a lack of selinux permissions.");
+ return new ArrayList<>();
+ }
+ IPinnerService pinnerService = IPinnerService.Stub.asInterface(binder);
+ if (pinnerService == null) {
+ Slog.w(TAG, "Failed to cast PinnerService.");
+ return new ArrayList<>();
+ }
+ List<PinnedFileStat> stats;
+ try {
+ stats = pinnerService.getPinnerStats();
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed to retrieve stats from PinnerService");
+ }
+ return stats;
+ }
+}
diff --git a/core/java/android/app/time/TEST_MAPPING b/core/java/android/app/time/TEST_MAPPING
index 47a152a..0f7a070 100644
--- a/core/java/android/app/time/TEST_MAPPING
+++ b/core/java/android/app/time/TEST_MAPPING
@@ -7,20 +7,6 @@
"include-filter": "android.app."
}
]
- }
- ],
- // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
- "postsubmit": [
- {
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.timezonedetector."
- },
- {
- "include-filter": "com.android.server.timedetector."
- }
- ]
},
{
"name": "CtsTimeTestCases",
@@ -30,5 +16,19 @@
}
]
}
+ ],
+ // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+ "postsubmit": [
+ {
+ "name": "FrameworksTimeServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.timezonedetector."
+ },
+ {
+ "include-filter": "com.android.server.timedetector."
+ }
+ ]
+ }
]
}
diff --git a/core/java/android/app/timedetector/TEST_MAPPING b/core/java/android/app/timedetector/TEST_MAPPING
index 9517fb9..43dd82f 100644
--- a/core/java/android/app/timedetector/TEST_MAPPING
+++ b/core/java/android/app/timedetector/TEST_MAPPING
@@ -7,17 +7,6 @@
"include-filter": "android.app."
}
]
- }
- ],
- // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
- "postsubmit": [
- {
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.timedetector."
- }
- ]
},
{
"name": "CtsTimeTestCases",
@@ -27,5 +16,16 @@
}
]
}
+ ],
+ // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+ "postsubmit": [
+ {
+ "name": "FrameworksTimeServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.timedetector."
+ }
+ ]
+ }
]
}
diff --git a/core/java/android/app/timezonedetector/TEST_MAPPING b/core/java/android/app/timezonedetector/TEST_MAPPING
index fd41b86..2be5614 100644
--- a/core/java/android/app/timezonedetector/TEST_MAPPING
+++ b/core/java/android/app/timezonedetector/TEST_MAPPING
@@ -7,17 +7,6 @@
"include-filter": "android.app."
}
]
- }
- ],
- // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
- "postsubmit": [
- {
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.timezonedetector."
- }
- ]
},
{
"name": "CtsTimeTestCases",
@@ -27,5 +16,16 @@
}
]
}
+ ],
+ // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+ "postsubmit": [
+ {
+ "name": "FrameworksTimeServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.timezonedetector."
+ }
+ ]
+ }
]
}
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index ebd5d64..cf19178 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -22,6 +22,7 @@
import android.app.usage.UsageEvents;
import android.app.usage.UsageEventsQuery;
import android.content.pm.ParceledListSlice;
+import android.os.PersistableBundle;
/**
* System private API for talking with the UsageStatsManagerService.
@@ -77,6 +78,8 @@
String callingPackage);
void reportUsageStop(in IBinder activity, String token, String callingPackage);
void reportUserInteraction(String packageName, int userId);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)")
+ void reportUserInteractionWithBundle(String packageName, int userId, in PersistableBundle eventExtras);
int getUsageSource();
void forceUsageSourceSettingRead();
long getLastTimeAnyComponentUsed(String packageName, String callingPackage);
diff --git a/core/java/android/app/usage/ParcelableUsageEventList.java b/core/java/android/app/usage/ParcelableUsageEventList.java
index aa32392..7bc6cb9 100644
--- a/core/java/android/app/usage/ParcelableUsageEventList.java
+++ b/core/java/android/app/usage/ParcelableUsageEventList.java
@@ -223,6 +223,7 @@
event.mContentAnnotations = null;
event.mNotificationChannelId = null;
event.mLocusId = null;
+ event.mExtras = null;
switch (event.mEventType) {
case Event.CONFIGURATION_CHANGE -> {
@@ -237,6 +238,11 @@
case Event.STANDBY_BUCKET_CHANGED -> event.mBucketAndReason = in.readInt();
case Event.NOTIFICATION_INTERRUPTION -> event.mNotificationChannelId = in.readString();
case Event.LOCUS_ID_SET -> event.mLocusId = in.readString();
+ case Event.USER_INTERACTION -> {
+ if (in.readInt() != 0) {
+ event.mExtras = in.readPersistableBundle(getClass().getClassLoader());
+ }
+ }
}
event.mFlags = in.readInt();
@@ -263,6 +269,14 @@
case Event.STANDBY_BUCKET_CHANGED -> dest.writeInt(event.mBucketAndReason);
case Event.NOTIFICATION_INTERRUPTION -> dest.writeString(event.mNotificationChannelId);
case Event.LOCUS_ID_SET -> dest.writeString(event.mLocusId);
+ case Event.USER_INTERACTION -> {
+ if (event.mExtras != null) {
+ dest.writeInt(1);
+ dest.writePersistableBundle(event.mExtras);
+ } else {
+ dest.writeInt(0);
+ }
+ }
}
dest.writeInt(event.mFlags);
}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 6c7eba0..0ae00cd 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -16,7 +16,9 @@
package android.app.usage;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -24,6 +26,7 @@
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -551,6 +554,22 @@
public int mLocusIdToken = UNASSIGNED_TOKEN;
/** @hide */
+ public PersistableBundle mExtras = null;
+
+ /** @hide */
+ public static class UserInteractionEventExtrasToken {
+ public int mCategoryToken = UNASSIGNED_TOKEN;
+ public int mActionToken = UNASSIGNED_TOKEN;
+
+ public UserInteractionEventExtrasToken() {
+ // Do nothing.
+ }
+ }
+
+ /** @hide */
+ public UserInteractionEventExtrasToken mUserInteractionExtrasToken = null;
+
+ /** @hide */
@EventFlags
public int mFlags;
@@ -650,6 +669,21 @@
}
/**
+ * Retrieves a map of extended data from the event if the event is of type
+ * {@link #USER_INTERACTION}.
+ *
+ * @return the map of all extras that associated with the reported user interaction
+ * event. The returned {@link PersistableBundle} will contain the extras
+ * {@link UsageStatsManager#EXTRA_EVENT_CATEGORY} and
+ * {@link UsageStatsManager#EXTRA_EVENT_ACTION}. {@link PersistableBundle#EMPTY}
+ * will be returned if the details are not available.
+ */
+ @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+ public @NonNull PersistableBundle getExtras() {
+ return mExtras == null ? PersistableBundle.EMPTY : mExtras;
+ }
+
+ /**
* Returns a {@link Configuration} for this event if the event is of type
* {@link #CONFIGURATION_CHANGE}, otherwise it returns null.
*/
@@ -747,6 +781,7 @@
mBucketAndReason = orig.mBucketAndReason;
mNotificationChannelId = orig.mNotificationChannelId;
mLocusId = orig.mLocusId;
+ mExtras = orig.mExtras;
}
}
@@ -987,6 +1022,14 @@
case Event.LOCUS_ID_SET:
p.writeString(event.mLocusId);
break;
+ case Event.USER_INTERACTION:
+ if (event.mExtras != null) {
+ p.writeInt(1);
+ p.writePersistableBundle(event.mExtras);
+ } else {
+ p.writeInt(0);
+ }
+ break;
}
p.writeInt(event.mFlags);
}
@@ -1036,6 +1079,7 @@
eventOut.mContentAnnotations = null;
eventOut.mNotificationChannelId = null;
eventOut.mLocusId = null;
+ eventOut.mExtras = null;
switch (eventOut.mEventType) {
case Event.CONFIGURATION_CHANGE:
@@ -1059,6 +1103,11 @@
case Event.LOCUS_ID_SET:
eventOut.mLocusId = p.readString();
break;
+ case Event.USER_INTERACTION:
+ if (p.readInt() != 0) {
+ eventOut.mExtras = p.readPersistableBundle(getClass().getClassLoader());
+ }
+ break;
}
eventOut.mFlags = p.readInt();
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 4f1c993..85d223d 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -28,6 +28,7 @@
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UserHandleAware;
+import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
@@ -35,6 +36,7 @@
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.Build;
+import android.os.PersistableBundle;
import android.os.PowerWhitelistManager;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -392,6 +394,23 @@
@SystemApi
public static final String EXTRA_TIME_USED = "android.app.usage.extra.TIME_USED";
+ /**
+ * A String extra, when used with {@link UsageEvents.Event#getExtras}, that indicates
+ * the category of the user interaction associated with the event. The category cannot
+ * be more than 127 characters, longer value will be truncated to 127 characters.
+ */
+ @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+ public static final String EXTRA_EVENT_CATEGORY =
+ "android.app.usage.extra.EVENT_CATEGORY";
+
+ /**
+ * A String extra, when used with {@link UsageEvents.Event#getExtras}, that indicates
+ * the action of the user interaction associated with the event. The action cannot be
+ * more than 127 characters, longer value will be truncated to 127 characters.
+ */
+ @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+ public static final String EXTRA_EVENT_ACTION =
+ "android.app.usage.extra.EVENT_ACTION";
/**
* App usage observers will consider the task root package the source of usage.
@@ -562,10 +581,10 @@
* then {@code null} will be returned.</em>
*
* @param beginTime The inclusive beginning of the range of events to include in the results.
- * Defined in terms of "Unix time", see
- * {@link java.lang.System#currentTimeMillis}.
+ * Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
* @param endTime The exclusive end of the range of events to include in the results. Defined
- * in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
+ * in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
* @return A {@link UsageEvents}.
*/
public UsageEvents queryEvents(long beginTime, long endTime) {
@@ -611,10 +630,10 @@
* then {@code null} will be returned.</em>
*
* @param beginTime The inclusive beginning of the range of events to include in the results.
- * Defined in terms of "Unix time", see
- * {@link java.lang.System#currentTimeMillis}.
+ * Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
* @param endTime The exclusive end of the range of events to include in the results. Defined
- * in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
+ * in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
* @return A {@link UsageEvents} object.
*
* @see #queryEvents(long, long)
@@ -1128,6 +1147,7 @@
* Reports user interaction with a given package in the given user.
*
* <p><em>This method is only for use by the system</em>
+ *
* @hide
*/
@RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)
@@ -1140,6 +1160,38 @@
}
/**
+ * Reports user interaction with given package and a particular {@code extras}
+ * in the given user.
+ *
+ * <p>
+ * Note: The structure of {@code extras} is a {@link PersistableBundle} with the
+ * category {@link #EXTRA_EVENT_CATEGORY} and the action {@link #EXTRA_EVENT_ACTION}.
+ * Category provides additional detail about the user interaction, the value
+ * is defined in namespace based. Example: android.app.notification could be used to
+ * indicate that the reported user interaction is related to notification. Action
+ * indicates the general action that performed.
+ * </p>
+ *
+ * @param packageName The package name of the app
+ * @param userId The user id who triggers the user interaction
+ * @param extras The {@link PersistableBundle} that will be used to specify the
+ * extra details for the user interaction event. The {@link PersistableBundle}
+ * must contain the extras {@link #EXTRA_EVENT_CATEGORY},
+ * {@link #EXTRA_EVENT_ACTION}. Cannot be empty.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_USER_INTERACTION_TYPE_API)
+ @RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)
+ public void reportUserInteraction(@NonNull String packageName, @UserIdInt int userId,
+ @NonNull PersistableBundle extras) {
+ try {
+ mService.reportUserInteractionWithBundle(packageName, userId, extras);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Report usage associated with a particular {@code token} has started. Tokens are app defined
* strings used to represent usage of in-app features. Apps with the {@link
* android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time limit observers
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index c6012bb..51a7f1c 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -52,6 +52,7 @@
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.FunctionalUtils;
import java.util.ArrayList;
import java.util.Collections;
@@ -562,6 +563,40 @@
});
}
+ private void tryAdapterConversion(
+ FunctionalUtils.RemoteExceptionIgnoringConsumer<RemoteViews> action,
+ RemoteViews original, String failureMsg) {
+ final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
+ && (mHasPostedLegacyLists = mHasPostedLegacyLists
+ || (original != null && original.hasLegacyLists()));
+
+ if (isConvertingAdapter) {
+ final RemoteViews viewsCopy = new RemoteViews(original);
+ Runnable updateWidgetWithTask = () -> {
+ try {
+ viewsCopy.collectAllIntents().get();
+ action.acceptOrThrow(viewsCopy);
+ } catch (Exception e) {
+ Log.e(TAG, failureMsg, e);
+ }
+ };
+
+ if (Looper.getMainLooper() == Looper.myLooper()) {
+ createUpdateExecutorIfNull().execute(updateWidgetWithTask);
+ return;
+ }
+
+ updateWidgetWithTask.run();
+ return;
+ }
+
+ try {
+ action.acceptOrThrow(original);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
/**
* Set the RemoteViews to use for the specified appWidgetIds.
* <p>
@@ -586,32 +621,8 @@
return;
}
- final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
- && (mHasPostedLegacyLists = mHasPostedLegacyLists
- || (views != null && views.hasLegacyLists()));
-
- if (isConvertingAdapter) {
- views.collectAllIntents();
-
- if (Looper.getMainLooper() == Looper.myLooper()) {
- RemoteViews viewsCopy = new RemoteViews(views);
- createUpdateExecutorIfNull().execute(() -> {
- try {
- mService.updateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy);
- } catch (RemoteException e) {
- Log.e(TAG, "Error updating app widget views in background", e);
- }
- });
-
- return;
- }
- }
-
- try {
- mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ tryAdapterConversion(view -> mService.updateAppWidgetIds(mPackageName, appWidgetIds,
+ view), views, "Error updating app widget views in background");
}
/**
@@ -716,32 +727,9 @@
return;
}
- final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
- && (mHasPostedLegacyLists = mHasPostedLegacyLists
- || (views != null && views.hasLegacyLists()));
-
- if (isConvertingAdapter) {
- views.collectAllIntents();
-
- if (Looper.getMainLooper() == Looper.myLooper()) {
- RemoteViews viewsCopy = new RemoteViews(views);
- createUpdateExecutorIfNull().execute(() -> {
- try {
- mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy);
- } catch (RemoteException e) {
- Log.e(TAG, "Error partially updating app widget views in background", e);
- }
- });
-
- return;
- }
- }
-
- try {
- mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ tryAdapterConversion(view -> mService.partiallyUpdateAppWidgetIds(mPackageName,
+ appWidgetIds, view), views,
+ "Error partially updating app widget views in background");
}
/**
@@ -793,33 +781,8 @@
return;
}
- final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
- && (mHasPostedLegacyLists = mHasPostedLegacyLists
- || (views != null && views.hasLegacyLists()));
-
- if (isConvertingAdapter) {
- views.collectAllIntents();
-
- if (Looper.getMainLooper() == Looper.myLooper()) {
- RemoteViews viewsCopy = new RemoteViews(views);
- createUpdateExecutorIfNull().execute(() -> {
- try {
- mService.updateAppWidgetProvider(provider, viewsCopy);
- } catch (RemoteException e) {
- Log.e(TAG, "Error updating app widget view using provider in background",
- e);
- }
- });
-
- return;
- }
- }
-
- try {
- mService.updateAppWidgetProvider(provider, views);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ tryAdapterConversion(view -> mService.updateAppWidgetProvider(provider, view), views,
+ "Error updating app widget view using provider in background");
}
/**
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 102cbf3..3520c0b 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -246,4 +246,10 @@
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
void unregisterVirtualCamera(in VirtualCameraConfig camera);
+
+ /**
+ * Returns the id of the virtual camera with given config.
+ */
+ @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+ int getVirtualCameraId(in VirtualCameraConfig camera);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index da8277c..9492a62 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -19,6 +19,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.companion.virtual.audio.VirtualAudioDevice;
@@ -340,12 +341,17 @@
return mVirtualAudioDevice;
}
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@NonNull
VirtualCamera createVirtualCamera(@NonNull VirtualCameraConfig config) {
- return new VirtualCamera(mVirtualDevice, config);
+ try {
+ mVirtualDevice.registerVirtualCamera(config);
+ return new VirtualCamera(mVirtualDevice, config);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
- @NonNull
void setShowPointerIcon(boolean showPointerIcon) {
try {
mVirtualDevice.setShowPointerIcon(showPointerIcon);
diff --git a/core/java/android/companion/virtual/camera/VirtualCamera.java b/core/java/android/companion/virtual/camera/VirtualCamera.java
index beee86f..52afa4e 100644
--- a/core/java/android/companion/virtual/camera/VirtualCamera.java
+++ b/core/java/android/companion/virtual/camera/VirtualCamera.java
@@ -66,15 +66,8 @@
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public VirtualCamera(
@NonNull IVirtualDevice virtualDevice, @NonNull VirtualCameraConfig config) {
- mVirtualDevice = virtualDevice;
+ mVirtualDevice = Objects.requireNonNull(virtualDevice);
mConfig = Objects.requireNonNull(config);
- Objects.requireNonNull(virtualDevice);
- // TODO(b/310857519): Avoid registration inside constructor.
- try {
- mVirtualDevice.registerVirtualCamera(config);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
}
/** Returns the configuration of this virtual camera instance. */
@@ -83,6 +76,20 @@
return mConfig;
}
+ /**
+ * Returns the id of this virtual camera instance.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @NonNull
+ public String getId() {
+ try {
+ return Integer.toString(mVirtualDevice.getVirtualCameraId(mConfig));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@Override
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void close() {
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index 10da8b1..f0477d4 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -32,6 +32,13 @@
}
flag {
+ name: "consistent_display_flags"
+ namespace: "virtual_devices"
+ description: "Make virtual display flags consistent with display manager"
+ bug: "300905478"
+}
+
+flag {
name: "vdm_custom_home"
namespace: "virtual_devices"
description: "Enable custom home API"
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 4b2cee6..697c25c 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -730,10 +730,7 @@
/**
* The next app to receive the permission protected data.
- *
- * @deprecated Use {@link #setNextAttributionSource} instead.
*/
- @Deprecated
public @NonNull Builder setNext(@Nullable AttributionSource value) {
checkNotUsed();
mBuilderFieldsSet |= 0x20;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index babfba1..98623de 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -312,6 +312,8 @@
Bundle getSuspendedPackageAppExtras(String packageName, int userId);
+ String getSuspendingPackage(String packageName, int userId);
+
/**
* Backup/restore support - only the system uid may use these.
*/
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index fe31c9d..6775f9b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -9965,6 +9965,21 @@
}
/**
+ * Get the name of the package that suspended the given package. Packages can be suspended by
+ * device administrators or apps holding {@link android.Manifest.permission#MANAGE_USERS} or
+ * {@link android.Manifest.permission#SUSPEND_APPS}.
+ *
+ * @param suspendedPackage The package that has been suspended.
+ * @return Name of the package that suspended the given package. Returns {@code null} if the
+ * given package is not currently suspended and the platform package name - i.e.
+ * {@code "android"} - if the package was suspended by a device admin.
+ * @hide
+ */
+ public @Nullable String getSuspendingPackage(@NonNull String suspendedPackage) {
+ throw new UnsupportedOperationException("getSuspendingPackage not implemented");
+ }
+
+ /**
* Query if an app is currently stopped.
*
* @return {@code true} if the given package is stopped, {@code false} otherwise
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index f532c4c..445ca0c 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
@@ -49,7 +50,8 @@
private static final String ATTR_SHOW_IN_LAUNCHER = "showInLauncher";
private static final String ATTR_START_WITH_PARENT = "startWithParent";
private static final String ATTR_SHOW_IN_SETTINGS = "showInSettings";
- private static final String ATTR_HIDE_IN_SETTINGS_IN_QUIET_MODE = "hideInSettingsInQuietMode";
+ private static final String ATTR_SHOW_IN_QUIET_MODE = "showInQuietMode";
+ private static final String ATTR_SHOW_IN_SHARING_SURFACES = "showInSharingSurfaces";
private static final String ATTR_INHERIT_DEVICE_POLICY = "inheritDevicePolicy";
private static final String ATTR_USE_PARENTS_CONTACTS = "useParentsContacts";
private static final String ATTR_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA =
@@ -81,7 +83,8 @@
INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT,
INDEX_DELETE_APP_WITH_PARENT,
INDEX_ALWAYS_VISIBLE,
- INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE,
+ INDEX_SHOW_IN_QUIET_MODE,
+ INDEX_SHOW_IN_SHARING_SURFACES,
INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE,
})
@Retention(RetentionPolicy.SOURCE)
@@ -99,8 +102,9 @@
private static final int INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT = 9;
private static final int INDEX_DELETE_APP_WITH_PARENT = 10;
private static final int INDEX_ALWAYS_VISIBLE = 11;
- private static final int INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE = 12;
+ private static final int INDEX_SHOW_IN_QUIET_MODE = 12;
private static final int INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE = 13;
+ private static final int INDEX_SHOW_IN_SHARING_SURFACES = 14;
/** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
private long mPropertiesPresent = 0;
@@ -286,6 +290,81 @@
*/
public static final int CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_NO_FILTERING = 1;
+ /**
+ * Possible values for the profile visibility when in quiet mode. This affects the profile data
+ * and apps surfacing in Settings, sharing surfaces, and file picker surfaces. It signifies
+ * whether the profile data and apps will be shown or not.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "SHOW_IN_QUIET_MODE_",
+ value = {
+ SHOW_IN_QUIET_MODE_PAUSED,
+ SHOW_IN_QUIET_MODE_HIDDEN,
+ SHOW_IN_QUIET_MODE_DEFAULT,
+ }
+ )
+ public @interface ShowInQuietMode {
+ }
+
+ /**
+ * Indicates that the profile should still be visible in quiet mode but should be shown as
+ * paused (e.g. by greying out its icons).
+ */
+ @SuppressLint("UnflaggedApi") // b/306636213
+ public static final int SHOW_IN_QUIET_MODE_PAUSED = 0;
+ /**
+ * Indicates that the profile should not be visible when the profile is in quiet mode.
+ * For example, the profile should not be shown in tabbed views in Settings, files sharing
+ * surfaces etc when in quiet mode.
+ */
+ @SuppressLint("UnflaggedApi") // b/306636213
+ public static final int SHOW_IN_QUIET_MODE_HIDDEN = 1;
+ /**
+ * Indicates that quiet mode should not have any effect on the profile visibility. If the
+ * profile is meant to be visible, it will remain visible and vice versa.
+ */
+ @SuppressLint("UnflaggedApi") // b/306636213
+ public static final int SHOW_IN_QUIET_MODE_DEFAULT = 2;
+
+ /**
+ * Possible values for the profile apps visibility in sharing surfaces. This indicates the
+ * profile data and apps should be shown in separate tabs or mixed with its parent user's data
+ * and apps in sharing surfaces and file picker surfaces.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "SHOW_IN_SHARING_SURFACES_",
+ value = {
+ SHOW_IN_SHARING_SURFACES_SEPARATE,
+ SHOW_IN_SHARING_SURFACES_WITH_PARENT,
+ SHOW_IN_SHARING_SURFACES_NO,
+ }
+ )
+ public @interface ShowInSharingSurfaces {
+ }
+
+ /**
+ * Indicates that the profile data and apps should be shown in sharing surfaces intermixed with
+ * parent user's data and apps.
+ */
+ @SuppressLint("UnflaggedApi") // b/306636213
+ public static final int SHOW_IN_SHARING_SURFACES_WITH_PARENT = SHOW_IN_LAUNCHER_WITH_PARENT;
+
+ /**
+ * Indicates that the profile data and apps should be shown in sharing surfaces separate from
+ * parent user's data and apps.
+ */
+ @SuppressLint("UnflaggedApi") // b/306636213
+ public static final int SHOW_IN_SHARING_SURFACES_SEPARATE = SHOW_IN_LAUNCHER_SEPARATE;
+
+ /**
+ * Indicates that the profile data and apps should not be shown in sharing surfaces at all.
+ */
+ @SuppressLint("UnflaggedApi") // b/306636213
+ public static final int SHOW_IN_SHARING_SURFACES_NO = SHOW_IN_LAUNCHER_NO;
/**
* Creates a UserProperties (intended for the SystemServer) that stores a reference to the given
@@ -331,7 +410,6 @@
if (hasManagePermission) {
// Add items that require MANAGE_USERS or stronger.
setShowInSettings(orig.getShowInSettings());
- setHideInSettingsInQuietMode(orig.getHideInSettingsInQuietMode());
setUseParentsContacts(orig.getUseParentsContacts());
setAuthAlwaysRequiredToDisableQuietMode(
orig.isAuthAlwaysRequiredToDisableQuietMode());
@@ -343,6 +421,8 @@
setShowInLauncher(orig.getShowInLauncher());
setMediaSharedWithParent(orig.isMediaSharedWithParent());
setCredentialShareableWithParent(orig.isCredentialShareableWithParent());
+ setShowInQuietMode(orig.getShowInQuietMode());
+ setShowInSharingSurfaces(orig.getShowInSharingSurfaces());
}
/**
@@ -419,40 +499,59 @@
private @ShowInSettings int mShowInSettings;
/**
- * Returns whether a user should be shown in the Settings app depending on the quiet mode.
- * This is generally inapplicable for non-profile users.
+ * Returns whether a user should be shown in the Settings and sharing surfaces depending on the
+ * {@link android.os.UserManager#requestQuietModeEnabled(boolean, android.os.UserHandle)
+ * quiet mode}. This is only applicable to profile users since the quiet mode concept is only
+ * applicable to profile users.
*
- * <p> {@link #getShowInSettings()} returns whether / how a user should be shown in Settings.
- * However, if this behaviour should be changed based on the quiet mode of the user, then this
- * property can be used. If the property is not set then the user is shown in the Settings app
- * irrespective of whether the user is in quiet mode or not. If the property is set, then the
- * user is shown in the Settings app only if the user is not in the quiet mode. Please note that
- * this property takes effect only if {@link #getShowInSettings()} does not return
- * {@link #SHOW_IN_SETTINGS_NO}.
+ * <p> Please note that, in Settings, this property takes effect only if
+ * {@link #getShowInSettings()} does not return {@link #SHOW_IN_SETTINGS_NO}.
+ * Also note that in Sharing surfaces this property takes effect only if
+ * {@link #getShowInSharingSurfaces()} does not return {@link #SHOW_IN_SHARING_SURFACES_NO}.
*
- * <p> The caller must have {@link android.Manifest.permission#MANAGE_USERS} to query this
- * property.
- *
- * @return true if a profile should be shown in the Settings only when the user is not in the
- * quiet mode.
- *
- * See also {@link #getShowInSettings()}, {@link #setShowInSettings(int)},
- * {@link ShowInSettings}
- *
- * @hide
+ * @return One of {@link #SHOW_IN_QUIET_MODE_HIDDEN},
+ * {@link #SHOW_IN_QUIET_MODE_PAUSED}, or
+ * {@link #SHOW_IN_QUIET_MODE_DEFAULT} depending on whether the profile should be
+ * shown in quiet mode or not.
*/
- public boolean getHideInSettingsInQuietMode() {
- if (isPresent(INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE)) return mHideInSettingsInQuietMode;
- if (mDefaultProperties != null) return mDefaultProperties.mHideInSettingsInQuietMode;
+ @SuppressLint("UnflaggedApi") // b/306636213
+ public @ShowInQuietMode int getShowInQuietMode() {
+ // NOTE: Launcher currently does not make use of this property.
+ if (isPresent(INDEX_SHOW_IN_QUIET_MODE)) return mShowInQuietMode;
+ if (mDefaultProperties != null) return mDefaultProperties.mShowInQuietMode;
throw new SecurityException(
- "You don't have permission to query HideInSettingsInQuietMode");
+ "You don't have permission to query ShowInQuietMode");
}
/** @hide */
- public void setHideInSettingsInQuietMode(boolean hideInSettingsInQuietMode) {
- this.mHideInSettingsInQuietMode = hideInSettingsInQuietMode;
- setPresent(INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE);
+ public void setShowInQuietMode(@ShowInQuietMode int showInQuietMode) {
+ this.mShowInQuietMode = showInQuietMode;
+ setPresent(INDEX_SHOW_IN_QUIET_MODE);
}
- private boolean mHideInSettingsInQuietMode;
+ private int mShowInQuietMode;
+
+ /**
+ * Returns whether a user's data and apps should be shown in sharing surfaces in a separate tab
+ * or mixed with the parent user's data/apps. This is only applicable to profile users.
+ *
+ * @return One of {@link #SHOW_IN_SHARING_SURFACES_NO},
+ * {@link #SHOW_IN_SHARING_SURFACES_SEPARATE}, or
+ * {@link #SHOW_IN_SHARING_SURFACES_WITH_PARENT} depending on whether the profile
+ * should be shown separate from its parent's data, mixed with the parent's data, or
+ * not shown at all.
+ */
+ @SuppressLint("UnflaggedApi") // b/306636213
+ public @ShowInSharingSurfaces int getShowInSharingSurfaces() {
+ if (isPresent(INDEX_SHOW_IN_SHARING_SURFACES)) return mShowInSharingSurfaces;
+ if (mDefaultProperties != null) return mDefaultProperties.mShowInSharingSurfaces;
+ throw new SecurityException(
+ "You don't have permission to query ShowInSharingSurfaces");
+ }
+ /** @hide */
+ public void setShowInSharingSurfaces(@ShowInSharingSurfaces int showInSharingSurfaces) {
+ this.mShowInSharingSurfaces = showInSharingSurfaces;
+ setPresent(INDEX_SHOW_IN_SHARING_SURFACES);
+ }
+ private int mShowInSharingSurfaces;
/**
* Returns whether a profile should be started when its parent starts (unless in quiet mode).
@@ -799,8 +898,11 @@
case ATTR_SHOW_IN_SETTINGS:
setShowInSettings(parser.getAttributeInt(i));
break;
- case ATTR_HIDE_IN_SETTINGS_IN_QUIET_MODE:
- setHideInSettingsInQuietMode(parser.getAttributeBoolean(i));
+ case ATTR_SHOW_IN_QUIET_MODE:
+ setShowInQuietMode(parser.getAttributeInt(i));
+ break;
+ case ATTR_SHOW_IN_SHARING_SURFACES:
+ setShowInSharingSurfaces(parser.getAttributeInt(i));
break;
case ATTR_INHERIT_DEVICE_POLICY:
setInheritDevicePolicy(parser.getAttributeInt(i));
@@ -858,9 +960,12 @@
if (isPresent(INDEX_SHOW_IN_SETTINGS)) {
serializer.attributeInt(null, ATTR_SHOW_IN_SETTINGS, mShowInSettings);
}
- if (isPresent(INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE)) {
- serializer.attributeBoolean(null, ATTR_HIDE_IN_SETTINGS_IN_QUIET_MODE,
- mHideInSettingsInQuietMode);
+ if (isPresent(INDEX_SHOW_IN_QUIET_MODE)) {
+ serializer.attributeInt(null, ATTR_SHOW_IN_QUIET_MODE,
+ mShowInQuietMode);
+ }
+ if (isPresent(INDEX_SHOW_IN_SHARING_SURFACES)) {
+ serializer.attributeInt(null, ATTR_SHOW_IN_SHARING_SURFACES, mShowInSharingSurfaces);
}
if (isPresent(INDEX_INHERIT_DEVICE_POLICY)) {
serializer.attributeInt(null, ATTR_INHERIT_DEVICE_POLICY,
@@ -912,7 +1017,8 @@
dest.writeInt(mShowInLauncher);
dest.writeBoolean(mStartWithParent);
dest.writeInt(mShowInSettings);
- dest.writeBoolean(mHideInSettingsInQuietMode);
+ dest.writeInt(mShowInQuietMode);
+ dest.writeInt(mShowInSharingSurfaces);
dest.writeInt(mInheritDevicePolicy);
dest.writeBoolean(mUseParentsContacts);
dest.writeBoolean(mUpdateCrossProfileIntentFiltersOnOTA);
@@ -936,7 +1042,8 @@
mShowInLauncher = source.readInt();
mStartWithParent = source.readBoolean();
mShowInSettings = source.readInt();
- mHideInSettingsInQuietMode = source.readBoolean();
+ mShowInQuietMode = source.readInt();
+ mShowInSharingSurfaces = source.readInt();
mInheritDevicePolicy = source.readInt();
mUseParentsContacts = source.readBoolean();
mUpdateCrossProfileIntentFiltersOnOTA = source.readBoolean();
@@ -974,7 +1081,10 @@
private @ShowInLauncher int mShowInLauncher = SHOW_IN_LAUNCHER_WITH_PARENT;
private boolean mStartWithParent = false;
private @ShowInSettings int mShowInSettings = SHOW_IN_SETTINGS_WITH_PARENT;
- private boolean mHideInSettingsInQuietMode = false;
+ private @ShowInQuietMode int mShowInQuietMode =
+ SHOW_IN_QUIET_MODE_PAUSED;
+ private @ShowInSharingSurfaces int mShowInSharingSurfaces =
+ SHOW_IN_SHARING_SURFACES_SEPARATE;
private @InheritDevicePolicy int mInheritDevicePolicy = INHERIT_DEVICE_POLICY_NO;
private boolean mUseParentsContacts = false;
private boolean mUpdateCrossProfileIntentFiltersOnOTA = false;
@@ -1005,9 +1115,15 @@
return this;
}
- /** Sets the value for {@link #mHideInSettingsInQuietMode} */
- public Builder setHideInSettingsInQuietMode(boolean hideInSettingsInQuietMode) {
- mHideInSettingsInQuietMode = hideInSettingsInQuietMode;
+ /** Sets the value for {@link #mShowInQuietMode} */
+ public Builder setShowInQuietMode(@ShowInQuietMode int showInQuietMode) {
+ mShowInQuietMode = showInQuietMode;
+ return this;
+ }
+
+ /** Sets the value for {@link #mShowInSharingSurfaces}. */
+ public Builder setShowInSharingSurfaces(@ShowInSharingSurfaces int showInSharingSurfaces) {
+ mShowInSharingSurfaces = showInSharingSurfaces;
return this;
}
@@ -1081,7 +1197,8 @@
mShowInLauncher,
mStartWithParent,
mShowInSettings,
- mHideInSettingsInQuietMode,
+ mShowInQuietMode,
+ mShowInSharingSurfaces,
mInheritDevicePolicy,
mUseParentsContacts,
mUpdateCrossProfileIntentFiltersOnOTA,
@@ -1100,7 +1217,8 @@
@ShowInLauncher int showInLauncher,
boolean startWithParent,
@ShowInSettings int showInSettings,
- boolean hideInSettingsInQuietMode,
+ @ShowInQuietMode int showInQuietMode,
+ @ShowInSharingSurfaces int showInSharingSurfaces,
@InheritDevicePolicy int inheritDevicePolicy,
boolean useParentsContacts, boolean updateCrossProfileIntentFiltersOnOTA,
@CrossProfileIntentFilterAccessControlLevel int crossProfileIntentFilterAccessControl,
@@ -1114,7 +1232,8 @@
setShowInLauncher(showInLauncher);
setStartWithParent(startWithParent);
setShowInSettings(showInSettings);
- setHideInSettingsInQuietMode(hideInSettingsInQuietMode);
+ setShowInQuietMode(showInQuietMode);
+ setShowInSharingSurfaces(showInSharingSurfaces);
setInheritDevicePolicy(inheritDevicePolicy);
setUseParentsContacts(useParentsContacts);
setUpdateCrossProfileIntentFiltersOnOTA(updateCrossProfileIntentFiltersOnOTA);
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index a565f68..d21b818 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -94,3 +94,10 @@
description: "Feature flag to read install related information from an APK."
bug: "275658500"
}
+
+flag {
+ name: "use_pia_v2"
+ namespace: "package_manager_service"
+ description: "Feature flag to enable the refactored Package Installer app with updated UI."
+ bug: "182205982"
+}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 9ec082a..6c6b33b 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -42,3 +42,10 @@
description: "Allow using all cpu cores during a user switch."
bug: "308105403"
}
+
+flag {
+ name: "enable_biometrics_to_unlock_private_space"
+ namespace: "profile_experiences"
+ description: "Add support to unlock the private space using biometrics"
+ bug: "312184187"
+}
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index d786d9a..18c95bfb 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -66,6 +66,13 @@
+ ".extra.sensor";
/**
+ * An extra containing the notification id that triggered the intent
+ * @hide
+ */
+ public static final String EXTRA_NOTIFICATION_ID = SensorPrivacyManager.class.getName()
+ + ".extra.notification_id";
+
+ /**
* An extra indicating if all sensors are affected
* @hide
*/
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index dfc27ca..507e814 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -16,6 +16,7 @@
package android.hardware.camera2;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -24,6 +25,8 @@
import android.hardware.camera2.impl.SyntheticKey;
import android.util.Log;
+import com.android.internal.camera.flags.Flags;
+
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
index d4ce0eb..5cbb0bb 100644
--- a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
@@ -16,8 +16,6 @@
package android.hardware.camera2.params;
-import static com.android.internal.R.string.hardware;
-
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index ca84b35..6a83cee 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -682,6 +682,7 @@
static class BatteryConsumerDataLayout {
private static final Key[] KEY_ARRAY = new Key[0];
+ public static final int POWER_MODEL_NOT_INCLUDED = -1;
public final String[] customPowerComponentNames;
public final int customPowerComponentCount;
public final boolean powerModelsIncluded;
@@ -713,7 +714,9 @@
// Declare the Key for the power component, ignoring other dimensions.
perComponentKeys.add(
new Key(componentId, PROCESS_STATE_ANY,
- powerModelsIncluded ? columnIndex++ : -1, // power model
+ powerModelsIncluded
+ ? columnIndex++
+ : POWER_MODEL_NOT_INCLUDED, // power model
columnIndex++, // power
columnIndex++ // usage duration
));
@@ -736,7 +739,9 @@
perComponentKeys.add(
new Key(componentId, processState,
- powerModelsIncluded ? columnIndex++ : -1, // power model
+ powerModelsIncluded
+ ? columnIndex++
+ : POWER_MODEL_NOT_INCLUDED, // power model
columnIndex++, // power
columnIndex++ // usage duration
));
@@ -843,11 +848,27 @@
@SuppressWarnings("unchecked")
@NonNull
+ public T addConsumedPower(@PowerComponent int componentId, double componentPower,
+ @PowerModel int powerModel) {
+ mPowerComponentsBuilder.addConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
+ componentPower, powerModel);
+ return (T) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ @NonNull
public T setConsumedPower(Key key, double componentPower, @PowerModel int powerModel) {
mPowerComponentsBuilder.setConsumedPower(key, componentPower, powerModel);
return (T) this;
}
+ @SuppressWarnings("unchecked")
+ @NonNull
+ public T addConsumedPower(Key key, double componentPower, @PowerModel int powerModel) {
+ mPowerComponentsBuilder.addConsumedPower(key, componentPower, powerModel);
+ return (T) this;
+ }
+
/**
* Sets the amount of drain attributed to the specified custom drain type.
*
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index e85b7bf..16ffaef 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -4029,6 +4029,17 @@
}
/**
+ * A helper object passed to various dump... methods to integrate with such objects
+ * as BatteryUsageStatsProvider.
+ */
+ public interface BatteryStatsDumpHelper {
+ /**
+ * Generates BatteryUsageStats based on the specified BatteryStats.
+ */
+ BatteryUsageStats getBatteryUsageStats(BatteryStats batteryStats, boolean detailed);
+ }
+
+ /**
* Dumps the ControllerActivityCounter if it has any data worth dumping.
* The order of the arguments in the final check in line is:
*
@@ -4390,7 +4401,7 @@
* NOTE: all times are expressed in microseconds, unless specified otherwise.
*/
public final void dumpCheckinLocked(Context context, PrintWriter pw, int which, int reqUid,
- boolean wifiOnly) {
+ boolean wifiOnly, BatteryStatsDumpHelper dumpHelper) {
if (which != BatteryStats.STATS_SINCE_CHARGED) {
dumpLine(pw, 0, STAT_NAMES[which], "err",
@@ -4652,7 +4663,7 @@
}
}
- final BatteryUsageStats stats = getBatteryUsageStats(context, true /* detailed */);
+ final BatteryUsageStats stats = dumpHelper.getBatteryUsageStats(this, true /* detailed */);
dumpLine(pw, 0 /* uid */, category, POWER_USE_SUMMARY_DATA,
formatCharge(stats.getBatteryCapacity()),
formatCharge(stats.getConsumedPower()),
@@ -5127,7 +5138,7 @@
@SuppressWarnings("unused")
public final void dumpLocked(Context context, PrintWriter pw, String prefix, final int which,
- int reqUid, boolean wifiOnly) {
+ int reqUid, boolean wifiOnly, BatteryStatsDumpHelper dumpHelper) {
if (which != BatteryStats.STATS_SINCE_CHARGED) {
pw.println("ERROR: BatteryStats.dump called for which type " + which
@@ -5854,7 +5865,7 @@
pw.println();
- BatteryUsageStats stats = getBatteryUsageStats(context, true /* detailed */);
+ BatteryUsageStats stats = dumpHelper.getBatteryUsageStats(this, true /* detailed */);
stats.dump(pw, prefix);
List<UidMobileRadioStats> uidMobileRadioStats =
@@ -7642,10 +7653,11 @@
/**
* Dumps a human-readable summary of the battery statistics to the given PrintWriter.
*
- * @param pw a Printer to receive the dump output.
+ * @param pw a Printer to receive the dump output.
*/
@SuppressWarnings("unused")
- public void dump(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
+ public void dump(Context context, PrintWriter pw, int flags, int reqUid, long histStart,
+ BatteryStatsDumpHelper dumpHelper) {
synchronized (this) {
prepareForDumpLocked();
}
@@ -7663,12 +7675,12 @@
}
synchronized (this) {
- dumpLocked(context, pw, flags, reqUid, filtering);
+ dumpLocked(context, pw, flags, reqUid, filtering, dumpHelper);
}
}
private void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid,
- boolean filtering) {
+ boolean filtering, BatteryStatsDumpHelper dumpHelper) {
if (!filtering) {
SparseArray<? extends Uid> uidStats = getUidStats();
final int NU = uidStats.size();
@@ -7803,15 +7815,15 @@
pw.println(" System starts: " + getStartCount()
+ ", currently on battery: " + getIsOnBattery());
dumpLocked(context, pw, "", STATS_SINCE_CHARGED, reqUid,
- (flags&DUMP_DEVICE_WIFI_ONLY) != 0);
+ (flags & DUMP_DEVICE_WIFI_ONLY) != 0, dumpHelper);
pw.println();
}
}
// This is called from BatteryStatsService.
@SuppressWarnings("unused")
- public void dumpCheckin(Context context, PrintWriter pw,
- List<ApplicationInfo> apps, int flags, long histStart) {
+ public void dumpCheckin(Context context, PrintWriter pw, List<ApplicationInfo> apps, int flags,
+ long histStart, BatteryStatsDumpHelper dumpHelper) {
synchronized (this) {
prepareForDumpLocked();
@@ -7829,12 +7841,12 @@
}
synchronized (this) {
- dumpCheckinLocked(context, pw, apps, flags);
+ dumpCheckinLocked(context, pw, apps, flags, dumpHelper);
}
}
private void dumpCheckinLocked(Context context, PrintWriter pw, List<ApplicationInfo> apps,
- int flags) {
+ int flags, BatteryStatsDumpHelper dumpHelper) {
if (apps != null) {
SparseArray<Pair<ArrayList<String>, MutableBoolean>> uids = new SparseArray<>();
for (int i=0; i<apps.size(); i++) {
@@ -7881,7 +7893,7 @@
(Object[])lineArgs);
}
dumpCheckinLocked(context, pw, STATS_SINCE_CHARGED, -1,
- (flags&DUMP_DEVICE_WIFI_ONLY) != 0);
+ (flags & DUMP_DEVICE_WIFI_ONLY) != 0, dumpHelper);
}
}
@@ -7891,7 +7903,7 @@
* @hide
*/
public void dumpProtoLocked(Context context, FileDescriptor fd, List<ApplicationInfo> apps,
- int flags, long histStart) {
+ int flags, long histStart, BatteryStatsDumpHelper dumpHelper) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
prepareForDumpLocked();
@@ -7909,7 +7921,8 @@
proto.write(BatteryStatsProto.END_PLATFORM_VERSION, getEndPlatformVersion());
if ((flags & DUMP_DAILY_ONLY) == 0) {
- final BatteryUsageStats stats = getBatteryUsageStats(context, false /* detailed */);
+ final BatteryUsageStats stats =
+ dumpHelper.getBatteryUsageStats(this, false /* detailed */);
ProportionalAttributionCalculator proportionalAttributionCalculator =
new ProportionalAttributionCalculator(context, stats);
dumpProtoAppsLocked(proto, stats, apps, proportionalAttributionCalculator);
@@ -8856,8 +8869,6 @@
return !tm.isDataCapable();
}
- protected abstract BatteryUsageStats getBatteryUsageStats(Context context, boolean detailed);
-
private boolean shouldHidePowerComponent(int powerComponent) {
return powerComponent == BatteryConsumer.POWER_COMPONENT_IDLE
|| powerComponent == BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index cd52b5c0..ed31002 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -36,6 +36,7 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -586,7 +587,8 @@
+ "(" + BatteryConsumer.processStateToString(key.processState) + ")";
}
printPowerComponent(pw, prefix, label, devicePowerMah, appsPowerMah,
- deviceConsumer.getPowerModel(key),
+ mIncludesPowerModels ? deviceConsumer.getPowerModel(key)
+ : BatteryConsumer.POWER_MODEL_UNDEFINED,
deviceConsumer.getUsageDurationMillis(key));
}
}
@@ -774,6 +776,15 @@
super.finalize();
}
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ dump(pw, "");
+ pw.flush();
+ return sw.toString();
+ }
+
/**
* Builder for BatteryUsageStats.
*/
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 330b992..c0d1fb9 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -131,6 +131,7 @@
int getUserBadgeDarkColorResId(int userId);
int getUserStatusBarIconResId(int userId);
boolean hasBadge(int userId);
+ int getProfileLabelResId(int userId);
boolean isUserUnlocked(int userId);
boolean isUserRunning(int userId);
boolean isUserForeground(int userId);
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index da647e2..161951e 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -437,6 +438,7 @@
* @see #getData()
* @see #setData(Bundle)
*/
+ @Nullable
public Bundle peekData() {
return data;
}
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index 9e5f539..9c11ad4 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -15,6 +15,7 @@
*/
package android.os;
+import static android.os.BatteryConsumer.BatteryConsumerDataLayout.POWER_MODEL_NOT_INCLUDED;
import static android.os.BatteryConsumer.POWER_COMPONENT_ANY;
import static android.os.BatteryConsumer.PROCESS_STATE_ANY;
import static android.os.BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
@@ -118,7 +119,7 @@
@BatteryConsumer.PowerModel
int getPowerModel(BatteryConsumer.Key key) {
- if (key.mPowerModelColumnIndex == -1) {
+ if (key.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) {
throw new IllegalStateException(
"Power model IDs were not requested in the BatteryUsageStatsQuery");
}
@@ -468,7 +469,7 @@
mMinConsumedPowerThreshold = minConsumedPowerThreshold;
for (BatteryConsumer.Key[] keys : mData.layout.keys) {
for (BatteryConsumer.Key key : keys) {
- if (key.mPowerModelColumnIndex != -1) {
+ if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) {
mData.putInt(key.mPowerModelColumnIndex, POWER_MODEL_UNINITIALIZED);
}
}
@@ -478,11 +479,19 @@
@NonNull
public Builder setConsumedPower(BatteryConsumer.Key key, double componentPower,
int powerModel) {
- if (Math.abs(componentPower) < mMinConsumedPowerThreshold) {
- componentPower = 0;
- }
mData.putDouble(key.mPowerColumnIndex, componentPower);
- if (key.mPowerModelColumnIndex != -1) {
+ if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) {
+ mData.putInt(key.mPowerModelColumnIndex, powerModel);
+ }
+ return this;
+ }
+
+ @NonNull
+ public Builder addConsumedPower(BatteryConsumer.Key key, double componentPower,
+ int powerModel) {
+ mData.putDouble(key.mPowerColumnIndex,
+ mData.getDouble(key.mPowerColumnIndex) + componentPower);
+ if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) {
mData.putInt(key.mPowerModelColumnIndex, powerModel);
}
return this;
@@ -496,9 +505,6 @@
*/
@NonNull
public Builder setConsumedPowerForCustomComponent(int componentId, double componentPower) {
- if (Math.abs(componentPower) < mMinConsumedPowerThreshold) {
- componentPower = 0;
- }
final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
if (index < 0 || index >= mData.layout.customPowerComponentCount) {
throw new IllegalArgumentException(
@@ -575,12 +581,12 @@
mData.getLong(key.mDurationColumnIndex)
+ otherData.getLong(otherKey.mDurationColumnIndex));
- if (key.mPowerModelColumnIndex == -1) {
+ if (key.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) {
continue;
}
boolean undefined = false;
- if (otherKey.mPowerModelColumnIndex == -1) {
+ if (otherKey.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) {
undefined = true;
} else {
final int powerModel = mData.getInt(key.mPowerModelColumnIndex);
@@ -641,19 +647,26 @@
*/
@NonNull
public PowerComponents build() {
- mData.putDouble(mData.layout.totalConsumedPowerColumnIndex, getTotalPower());
-
for (BatteryConsumer.Key[] keys : mData.layout.keys) {
for (BatteryConsumer.Key key : keys) {
- if (key.mPowerModelColumnIndex != -1) {
+ if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) {
if (mData.getInt(key.mPowerModelColumnIndex) == POWER_MODEL_UNINITIALIZED) {
mData.putInt(key.mPowerModelColumnIndex,
BatteryConsumer.POWER_MODEL_UNDEFINED);
}
}
+
+ if (mMinConsumedPowerThreshold != 0) {
+ if (mData.getDouble(key.mPowerColumnIndex) < mMinConsumedPowerThreshold) {
+ mData.putDouble(key.mPowerColumnIndex, 0);
+ }
+ }
}
}
+ if (mData.getDouble(mData.layout.totalConsumedPowerColumnIndex) == 0) {
+ mData.putDouble(mData.layout.totalConsumedPowerColumnIndex, getTotalPower());
+ }
return new PowerComponents(this);
}
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 08d6e02..ec6d20f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -5693,6 +5693,44 @@
}
/**
+ * Returns the string/label that should be used to represent the context user. For example,
+ * this string can represent a profile in tabbed views. This is only applicable to
+ * {@link #isProfile() profile users}. This string is translated to the device default language.
+ *
+ * @return String representing the label for the context user.
+ *
+ * @throws android.content.res.Resources.NotFoundException if the user does not have a label
+ * defined.
+ *
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("UnflaggedApi") // b/306636213
+ @UserHandleAware(
+ requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.QUERY_USERS,
+ Manifest.permission.INTERACT_ACROSS_USERS})
+ public @NonNull String getProfileLabel() {
+ if (isManagedProfile(mUserId)) {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getResources().getString(
+ android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB,
+ () -> getDefaultProfileLabel(mUserId));
+ }
+ return getDefaultProfileLabel(mUserId);
+ }
+
+ private String getDefaultProfileLabel(int userId) {
+ try {
+ final int resourceId = mService.getProfileLabelResId(userId);
+ return Resources.getSystem().getString(resourceId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* If the user is a {@link UserManager#isProfile profile}, checks if the user
* shares media with its parent user (the user that created this profile).
* Returns false for any other type of user.
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index dc86e3f5..5cbc18e 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -56,4 +56,11 @@
namespace: "permissions"
description: "enables logging of the OP_ENABLE_MOBILE_DATA_BY_USER"
bug: "222650148"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "factory_reset_prep_permission_apis"
+ namespace: "wallet_integration"
+ description: "enable Permission PREPARE_FACTORY_RESET."
+ bug: "302016478"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ff6ec29..8f18c5f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14975,6 +14975,38 @@
public static final String APP_OPS_CONSTANTS = "app_ops_constants";
/**
+ * Device Idle (Doze) specific settings.
+ * This is encoded as a key=value list, separated by commas. Ex:
+ *
+ * "inactive_to=60000,sensing_to=400000"
+ *
+ * The following keys are supported:
+ *
+ * <pre>
+ * inactive_to (long)
+ * sensing_to (long)
+ * motion_inactive_to (long)
+ * idle_after_inactive_to (long)
+ * idle_pending_to (long)
+ * max_idle_pending_to (long)
+ * idle_pending_factor (float)
+ * quick_doze_delay_to (long)
+ * idle_to (long)
+ * max_idle_to (long)
+ * idle_factor (float)
+ * min_time_to_alarm (long)
+ * max_temp_app_whitelist_duration (long)
+ * notification_whitelist_duration (long)
+ * </pre>
+ *
+ * <p>
+ * Type: string
+ * @hide
+ * @see com.android.server.DeviceIdleController.Constants
+ */
+ public static final String DEVICE_IDLE_CONSTANTS = "device_idle_constants";
+
+ /**
* Battery Saver specific settings
* This is encoded as a key=value list, separated by commas. Ex:
*
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig
new file mode 100644
index 0000000..5978383
--- /dev/null
+++ b/core/java/android/service/chooser/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.service.chooser"
+
+flag {
+ name: "support_nfc_resolver"
+ namespace: "systemui"
+ description: "This flag controls the new NFC 'resolver' activity"
+ bug: "268089816"
+}
+
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 0063d13..886727e 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -490,16 +490,32 @@
/**
* Notify changes to activity state changes on certain subscription.
*
+ * @param subId for which data activity state changed.
+ * @param dataActivityType indicates the latest data activity type e.g. {@link
+ * TelephonyManager#DATA_ACTIVITY_IN}
+ */
+ public void notifyDataActivityChanged(int subId, @DataActivityType int dataActivityType) {
+ try {
+ sRegistry.notifyDataActivityForSubscriber(subId, dataActivityType);
+ } catch (RemoteException ex) {
+ // system process is dead
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Notify changes to activity state changes on certain subscription.
+ *
* @param slotIndex for which data activity changed. Can be derived from subId except
* when subId is invalid.
* @param subId for which data activity state changed.
- * @param dataActivityType indicates the latest data activity type e.g, {@link
+ * @param dataActivityType indicates the latest data activity type e.g. {@link
* TelephonyManager#DATA_ACTIVITY_IN}
*/
public void notifyDataActivityChanged(int slotIndex, int subId,
@DataActivityType int dataActivityType) {
try {
- sRegistry.notifyDataActivityForSubscriber(slotIndex, subId, dataActivityType);
+ sRegistry.notifyDataActivityForSubscriberWithSlot(slotIndex, subId, dataActivityType);
} catch (RemoteException ex) {
// system process is dead
throw ex.rethrowFromSystemServer();
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 9f886c8..d131dc9 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -69,6 +69,7 @@
private final String mName;
private final int mVendorId;
private final int mProductId;
+ private final int mDeviceBus;
private final String mDescriptor;
private final InputDeviceIdentifier mIdentifier;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -468,8 +469,8 @@
* Called by native code
*/
private InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
- int productId, String descriptor, boolean isExternal, int sources, int keyboardType,
- KeyCharacterMap keyCharacterMap, @Nullable String keyboardLanguageTag,
+ int productId, int deviceBus, String descriptor, boolean isExternal, int sources,
+ int keyboardType, KeyCharacterMap keyCharacterMap, @Nullable String keyboardLanguageTag,
@Nullable String keyboardLayoutType, boolean hasVibrator, boolean hasMicrophone,
boolean hasButtonUnderPad, boolean hasSensor, boolean hasBattery, int usiVersionMajor,
int usiVersionMinor, int associatedDisplayId) {
@@ -479,6 +480,7 @@
mName = name;
mVendorId = vendorId;
mProductId = productId;
+ mDeviceBus = deviceBus;
mDescriptor = descriptor;
mIsExternal = isExternal;
mSources = sources;
@@ -512,6 +514,7 @@
mName = in.readString();
mVendorId = in.readInt();
mProductId = in.readInt();
+ mDeviceBus = in.readInt();
mDescriptor = in.readString();
mIsExternal = in.readInt() != 0;
mSources = in.readInt();
@@ -551,6 +554,7 @@
private String mName = "";
private int mVendorId = 0;
private int mProductId = 0;
+ private int mDeviceBus = 0;
private String mDescriptor = "";
private boolean mIsExternal = false;
private int mSources = 0;
@@ -604,6 +608,12 @@
return this;
}
+ /** @see InputDevice#getDeviceBus() */
+ public Builder setDeviceBus(int deviceBus) {
+ mDeviceBus = deviceBus;
+ return this;
+ }
+
/** @see InputDevice#getDescriptor() */
public Builder setDescriptor(String descriptor) {
mDescriptor = descriptor;
@@ -705,6 +715,7 @@
mName,
mVendorId,
mProductId,
+ mDeviceBus,
mDescriptor,
mIsExternal,
mSources,
@@ -847,6 +858,21 @@
}
/**
+ * Gets the device bus used by given device, if available.
+ * <p>
+ * The device bus is the communication system used for transferring data
+ * (e.g. USB, Bluetooth etc.). This value comes from the kernel (from input.h).
+ * A value of 0 will be assigned where the device bus is not available.
+ * </p>
+ *
+ * @return The device bus of a given device
+ * @hide
+ */
+ public int getDeviceBus() {
+ return mDeviceBus;
+ }
+
+ /**
* Gets the input device descriptor, which is a stable identifier for an input device.
* <p>
* An input device descriptor uniquely identifies an input device. Its value
@@ -1448,6 +1474,7 @@
out.writeString(mName);
out.writeInt(mVendorId);
out.writeInt(mProductId);
+ out.writeInt(mDeviceBus);
out.writeString(mDescriptor);
out.writeInt(mIsExternal ? 1 : 0);
out.writeInt(mSources);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 8befe8a..cbbe785 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -503,9 +503,6 @@
// be dumped as additional context
private static volatile boolean sDebugUsageAfterRelease = false;
- static GlobalTransactionWrapper sGlobalTransaction;
- static long sTransactionNestCount = 0;
-
private static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(SurfaceControl.class.getClassLoader(),
nativeGetNativeSurfaceControlFinalizer());
@@ -859,33 +856,47 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"FRAME_RATE_SELECTION_STRATEGY_"},
- value = {FRAME_RATE_SELECTION_STRATEGY_SELF,
- FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN})
+ value = {FRAME_RATE_SELECTION_STRATEGY_PROPAGATE,
+ FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN,
+ FRAME_RATE_SELECTION_STRATEGY_SELF})
public @interface FrameRateSelectionStrategy {}
// From window.h. Keep these in sync.
/**
* Default value. The layer uses its own frame rate specifications, assuming it has any
- * specifications, instead of its parent's.
+ * specifications, instead of its parent's. If it does not have its own frame rate
+ * specifications, it will try to use its parent's. It will propagate its specifications to any
+ * descendants that do not have their own.
+ *
* However, {@link #FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN} on an ancestor layer
- * supersedes this behavior, meaning that this layer will inherit the frame rate specifications
- * of that ancestor layer.
+ * supersedes this behavior, meaning that this layer will inherit frame rate specifications
+ * regardless of whether it has its own.
* @hide
*/
- public static final int FRAME_RATE_SELECTION_STRATEGY_SELF = 0;
+ public static final int FRAME_RATE_SELECTION_STRATEGY_PROPAGATE = 0;
/**
* The layer's frame rate specifications will propagate to and override those of its descendant
* layers.
- * The layer with this strategy has the {@link #FRAME_RATE_SELECTION_STRATEGY_SELF} behavior
- * for itself. This does mean that any parent or ancestor layer that also has the strategy
- * {@link FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN} will override this layer's
+ *
+ * The layer itself has the {@link #FRAME_RATE_SELECTION_STRATEGY_PROPAGATE} behavior.
+ * Thus, ancestor layer that also has the strategy
+ * {@link #FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN} will override this layer's
* frame rate specifications.
* @hide
*/
public static final int FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN = 1;
/**
+ * The layer's frame rate specifications will not propagate to its descendant
+ * layers, even if the descendant layer has no frame rate specifications.
+ * However, {@link #FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN} on an ancestor
+ * layer supersedes this behavior.
+ * @hide
+ */
+ public static final int FRAME_RATE_SELECTION_STRATEGY_SELF = 2;
+
+ /**
* Builder class for {@link SurfaceControl} objects.
*
* By default the surface will be hidden, and have "unset" bounds, meaning it can
@@ -1576,54 +1587,30 @@
return mNativeObject != 0;
}
- /*
- * set surface parameters.
- * needs to be inside open/closeTransaction block
- */
-
/** start a transaction
* @hide
- */
- @UnsupportedAppUsage
- public static void openTransaction() {
- synchronized (SurfaceControl.class) {
- if (sGlobalTransaction == null) {
- sGlobalTransaction = new GlobalTransactionWrapper();
- }
- synchronized(SurfaceControl.class) {
- sTransactionNestCount++;
- }
- }
- }
-
- /**
- * Merge the supplied transaction in to the deprecated "global" transaction.
- * This clears the supplied transaction in an identical fashion to {@link Transaction#merge}.
- * <p>
- * This is a utility for interop with legacy-code and will go away with the Global Transaction.
- * @hide
+ * @deprecated Use regular Transaction instead.
*/
@Deprecated
- public static void mergeToGlobalTransaction(Transaction t) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.merge(t);
- }
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.VANILLA_ICE_CREAM,
+ publicAlternatives = "Use {@code SurfaceControl.Transaction} instead",
+ trackingBug = 247078497)
+ public static void openTransaction() {
+ // TODO(b/247078497): It was used for global transaction (all usages are removed).
+ // Keep the method declaration to avoid breaking reference from legacy access.
}
/** end a transaction
* @hide
+ * @deprecated Use regular Transaction instead.
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.VANILLA_ICE_CREAM,
+ publicAlternatives = "Use {@code SurfaceControl.Transaction} instead",
+ trackingBug = 247078497)
public static void closeTransaction() {
- synchronized(SurfaceControl.class) {
- if (sTransactionNestCount == 0) {
- Log.e(TAG,
- "Call to SurfaceControl.closeTransaction without matching openTransaction");
- } else if (--sTransactionNestCount > 0) {
- return;
- }
- sGlobalTransaction.applyGlobalTransaction(false);
- }
+ // TODO(b/247078497): It was used for global transaction (all usages are removed).
+ // Keep the method declaration to avoid breaking reference from legacy access.
}
/**
@@ -4499,39 +4486,6 @@
}
/**
- * As part of eliminating usage of the global Transaction we expose
- * a SurfaceControl.getGlobalTransaction function. However calling
- * apply on this global transaction (rather than using closeTransaction)
- * would be very dangerous. So for the global transaction we use this
- * subclass of Transaction where the normal apply throws an exception.
- */
- private static class GlobalTransactionWrapper extends SurfaceControl.Transaction {
- void applyGlobalTransaction(boolean sync) {
- applyResizedSurfaces();
- notifyReparentedSurfaces();
- nativeApplyTransaction(mNativeObject, sync, /*oneWay*/ false);
- }
-
- @Override
- public void apply(boolean sync) {
- throw new RuntimeException("Global transaction must be applied from closeTransaction");
- }
- }
-
- /**
- * This is a refactoring utility function to enable lower levels of code to be refactored
- * from using the global transaction (and instead use a passed in Transaction) without
- * having to refactor the higher levels at the same time.
- * The returned global transaction can't be applied, it must be applied from closeTransaction
- * Unless you are working on removing Global Transaction usage in the WindowManager, this
- * probably isn't a good function to use.
- * @hide
- */
- public static Transaction getGlobalTransaction() {
- return sGlobalTransaction;
- }
-
- /**
* @hide
*/
public void resize(int w, int h) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d591f89..d58c07d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -33018,7 +33018,7 @@
}
private float getSizePercentage() {
- if (mResources == null || getAlpha() == 0 || getVisibility() != VISIBLE) {
+ if (mResources == null || getVisibility() != VISIBLE) {
return 0;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7b45600..cac5387 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -130,7 +130,6 @@
import android.graphics.HardwareRenderer;
import android.graphics.HardwareRenderer.FrameDrawingCallback;
import android.graphics.HardwareRendererObserver;
-import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
@@ -206,7 +205,6 @@
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
-import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureSession;
@@ -993,7 +991,7 @@
// for idleness handling.
private boolean mHasIdledMessage = false;
// time for touch boost period.
- private static final int FRAME_RATE_TOUCH_BOOST_TIME = 1500;
+ private static final int FRAME_RATE_TOUCH_BOOST_TIME = 3000;
// time for checking idle status periodically.
private static final int FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS = 500;
// time for revaluating the idle status before lowering the frame rate.
@@ -4029,56 +4027,20 @@
}
private void notifyContentCaptureEvents() {
- try {
- if (!isContentCaptureEnabled()) {
- if (DEBUG_CONTENT_CAPTURE) {
- Log.d(mTag, "notifyContentCaptureEvents while disabled");
- }
- mAttachInfo.mContentCaptureEvents = null;
- return;
- }
- if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents");
- }
- MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
- .getMainContentCaptureSession();
- for (int i = 0; i < mAttachInfo.mContentCaptureEvents.size(); i++) {
- int sessionId = mAttachInfo.mContentCaptureEvents.keyAt(i);
- mainSession.notifyViewTreeEvent(sessionId, /* started= */ true);
- ArrayList<Object> events = mAttachInfo.mContentCaptureEvents
- .valueAt(i);
- for_each_event: for (int j = 0; j < events.size(); j++) {
- Object event = events.get(j);
- if (event instanceof AutofillId) {
- mainSession.notifyViewDisappeared(sessionId, (AutofillId) event);
- } else if (event instanceof View) {
- View view = (View) event;
- ContentCaptureSession session = view.getContentCaptureSession();
- if (session == null) {
- Log.w(mTag, "no content capture session on view: " + view);
- continue for_each_event;
- }
- int actualId = session.getId();
- if (actualId != sessionId) {
- Log.w(mTag, "content capture session mismatch for view (" + view
- + "): was " + sessionId + " before, it's " + actualId + " now");
- continue for_each_event;
- }
- ViewStructure structure = session.newViewStructure(view);
- view.onProvideContentCaptureStructure(structure, /* flags= */ 0);
- session.notifyViewAppeared(structure);
- } else if (event instanceof Insets) {
- mainSession.notifyViewInsetsChanged(sessionId, (Insets) event);
- } else {
- Log.w(mTag, "invalid content capture event: " + event);
- }
- }
- mainSession.notifyViewTreeEvent(sessionId, /* started= */ false);
+ if (!isContentCaptureEnabled()) {
+ if (DEBUG_CONTENT_CAPTURE) {
+ Log.d(mTag, "notifyContentCaptureEvents while disabled");
}
mAttachInfo.mContentCaptureEvents = null;
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ return;
}
+
+ final ContentCaptureManager manager = mAttachInfo.mContentCaptureManager;
+ if (manager != null && mAttachInfo.mContentCaptureEvents != null) {
+ final MainContentCaptureSession session = manager.getMainContentCaptureSession();
+ session.notifyContentCaptureEvents(mAttachInfo.mContentCaptureEvents);
+ }
+ mAttachInfo.mContentCaptureEvents = null;
}
private void notifyHolderSurfaceDestroyed() {
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
index a11c6d0..a404bd6 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
@@ -54,7 +54,7 @@
* @param displayId the logical display id.
* @param scale magnification scale.
*/
- void setScale(int displayId, float scale);
+ void setScaleForWindowMagnification(int displayId, float scale);
/**
* Disables window magnification on specified display with animation.
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 5a058ff..a829747 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -18,6 +18,7 @@
import static android.view.contentcapture.ContentCaptureHelper.sDebug;
import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
import static android.view.contentcapture.ContentCaptureHelper.toSet;
+import static android.view.contentcapture.flags.Flags.runOnBackgroundThreadEnabled;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -52,6 +53,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.RingBuffer;
import com.android.internal.util.SyncResultReceiver;
@@ -495,10 +497,9 @@
@GuardedBy("mLock")
private int mFlags;
- // TODO(b/119220549): use UI Thread directly (as calls are one-way) or a shared thread / handler
- // held at the Application level
- @NonNull
- private final Handler mHandler;
+ @Nullable
+ @GuardedBy("mLock")
+ private Handler mHandler;
@GuardedBy("mLock")
private MainContentCaptureSession mMainSession;
@@ -562,11 +563,6 @@
if (sVerbose) Log.v(TAG, "Constructor for " + context.getPackageName());
- // TODO(b/119220549): we might not even need a handler, as the IPCs are oneway. But if we
- // do, then we should optimize it to run the tests after the Choreographer finishes the most
- // important steps of the frame.
- mHandler = Handler.createAsync(Looper.getMainLooper());
-
mDataShareAdapterResourceManager = new LocalDataShareAdapterResourceManager();
if (mOptions.contentProtectionOptions.enableReceiver
@@ -594,13 +590,27 @@
public MainContentCaptureSession getMainContentCaptureSession() {
synchronized (mLock) {
if (mMainSession == null) {
- mMainSession = new MainContentCaptureSession(mContext, this, mHandler, mService);
+ mMainSession = new MainContentCaptureSession(
+ mContext, this, prepareContentCaptureHandler(), mService);
if (sVerbose) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession);
}
return mMainSession;
}
}
+ @NonNull
+ @GuardedBy("mLock")
+ private Handler prepareContentCaptureHandler() {
+ if (mHandler == null) {
+ if (runOnBackgroundThreadEnabled()) {
+ mHandler = BackgroundThread.getHandler();
+ } else {
+ mHandler = Handler.createAsync(Looper.getMainLooper());
+ }
+ }
+ return mHandler;
+ }
+
/** @hide */
@UiThread
public void onActivityCreated(@NonNull IBinder applicationToken,
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index d9b0f80..14ec14b 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -34,7 +34,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UiThread;
import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.graphics.Insets;
@@ -50,7 +49,10 @@
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Log;
+import android.util.SparseArray;
import android.util.TimeUtils;
+import android.view.View;
+import android.view.ViewStructure;
import android.view.autofill.AutofillId;
import android.view.contentcapture.ViewNode.ViewStructureImpl;
import android.view.contentprotection.ContentProtectionEventProcessor;
@@ -58,6 +60,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
+import com.android.modules.expresslog.Counter;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -66,6 +69,7 @@
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Main session associated with a context.
@@ -79,6 +83,9 @@
private static final String TAG = MainContentCaptureSession.class.getSimpleName();
+ private static final String CONTENT_CAPTURE_WRONG_THREAD_METRIC_ID =
+ "content_capture.value_content_capture_wrong_thread_count";
+
// For readability purposes...
private static final boolean FORCE_FLUSH = true;
@@ -163,6 +170,8 @@
@Nullable
private final LocalLog mFlushHistory;
+ private final AtomicInteger mWrongThreadCount = new AtomicInteger(0);
+
/**
* Binder object used to update the session state.
*/
@@ -207,7 +216,8 @@
} else {
binder = null;
}
- mainSession.mHandler.post(() -> mainSession.onSessionStarted(resultCode, binder));
+ mainSession.mHandler.post(() ->
+ mainSession.onSessionStarted(resultCode, binder));
}
}
@@ -244,9 +254,14 @@
/**
* Starts this session.
*/
- @UiThread
void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
@NonNull ComponentName component, int flags) {
+ runOnContentCaptureThread(() -> startImpl(token, shareableActivityToken, component, flags));
+ }
+
+ private void startImpl(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+ @NonNull ComponentName component, int flags) {
+ checkOnContentCaptureThread();
if (!isContentCaptureEnabled()) return;
if (sVerbose) {
@@ -280,17 +295,15 @@
Log.w(TAG, "Error starting session for " + component.flattenToShortString() + ": " + e);
}
}
-
@Override
void onDestroy() {
- mHandler.removeMessages(MSG_FLUSH);
- mHandler.post(() -> {
+ clearAndRunOnContentCaptureThread(() -> {
try {
flush(FLUSH_REASON_SESSION_FINISHED);
} finally {
destroySession();
}
- });
+ }, MSG_FLUSH);
}
/**
@@ -302,8 +315,8 @@
* @hide
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- @UiThread
public void onSessionStarted(int resultCode, @Nullable IBinder binder) {
+ checkOnContentCaptureThread();
if (binder != null) {
mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
mDirectServiceVulture = () -> {
@@ -347,13 +360,12 @@
/** @hide */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- @UiThread
public void sendEvent(@NonNull ContentCaptureEvent event) {
sendEvent(event, /* forceFlush= */ false);
}
- @UiThread
private void sendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+ checkOnContentCaptureThread();
final int eventType = event.getType();
if (sVerbose) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED
@@ -396,15 +408,15 @@
}
}
- @UiThread
private void sendContentProtectionEvent(@NonNull ContentCaptureEvent event) {
+ checkOnContentCaptureThread();
if (mContentProtectionEventProcessor != null) {
mContentProtectionEventProcessor.processEvent(event);
}
}
- @UiThread
private void sendContentCaptureEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+ checkOnContentCaptureThread();
final int eventType = event.getType();
final int maxBufferSize = mManager.mOptions.maxBufferSize;
if (mEvents == null) {
@@ -538,13 +550,13 @@
flush(flushReason);
}
- @UiThread
private boolean hasStarted() {
+ checkOnContentCaptureThread();
return mState != UNKNOWN_STATE;
}
- @UiThread
private void scheduleFlush(@FlushReason int reason, boolean checkExisting) {
+ checkOnContentCaptureThread();
if (sVerbose) {
Log.v(TAG, "handleScheduleFlush(" + getDebugState(reason)
+ ", checkExisting=" + checkExisting);
@@ -588,8 +600,8 @@
mHandler.postDelayed(() -> flushIfNeeded(reason), MSG_FLUSH, flushFrequencyMs);
}
- @UiThread
private void flushIfNeeded(@FlushReason int reason) {
+ checkOnContentCaptureThread();
if (mEvents == null || mEvents.isEmpty()) {
if (sVerbose) Log.v(TAG, "Nothing to flush");
return;
@@ -600,8 +612,12 @@
/** @hide */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@Override
- @UiThread
public void flush(@FlushReason int reason) {
+ runOnContentCaptureThread(() -> flushImpl(reason));
+ }
+
+ private void flushImpl(@FlushReason int reason) {
+ checkOnContentCaptureThread();
if (mEvents == null || mEvents.size() == 0) {
if (sVerbose) {
Log.v(TAG, "Don't flush for empty event buffer.");
@@ -669,8 +685,8 @@
* Resets the buffer and return a {@link ParceledListSlice} with the previous events.
*/
@NonNull
- @UiThread
private ParceledListSlice<ContentCaptureEvent> clearEvents() {
+ checkOnContentCaptureThread();
// NOTE: we must save a reference to the current mEvents and then set it to to null,
// otherwise clearing it would clear it in the receiving side if the service is also local.
if (mEvents == null) {
@@ -684,14 +700,15 @@
/** hide */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- @UiThread
public void destroySession() {
+ checkOnContentCaptureThread();
if (sDebug) {
Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
+ (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
+ getDebugState());
}
+ reportWrongThreadMetric();
try {
mSystemServerInterface.finishSession(mId);
} catch (RemoteException e) {
@@ -710,8 +727,8 @@
// clearings out.
/** @hide */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- @UiThread
public void resetSession(int newState) {
+ checkOnContentCaptureThread();
if (sVerbose) {
Log.v(TAG, "handleResetSession(" + getActivityName() + "): from "
+ getStateAsString(mState) + " to " + getStateAsString(newState));
@@ -794,24 +811,26 @@
// change should also get get rid of the "internalNotifyXXXX" methods above
void notifyChildSessionStarted(int parentSessionId, int childSessionId,
@NonNull ContentCaptureContext clientContext) {
- mHandler.post(() -> sendEvent(new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
+ runOnContentCaptureThread(
+ () -> sendEvent(new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
.setParentSessionId(parentSessionId).setClientContext(clientContext),
FORCE_FLUSH));
}
void notifyChildSessionFinished(int parentSessionId, int childSessionId) {
- mHandler.post(() -> sendEvent(new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
+ runOnContentCaptureThread(
+ () -> sendEvent(new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
.setParentSessionId(parentSessionId), FORCE_FLUSH));
}
void notifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) {
- mHandler.post(() -> sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED)
+ runOnContentCaptureThread(() ->
+ sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED)
.setViewNode(node.mNode)));
}
- /** Public because is also used by ViewRootImpl */
- public void notifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
- mHandler.post(() -> sendEvent(
+ void notifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
+ runOnContentCaptureThread(() -> sendEvent(
new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id)));
}
@@ -836,52 +855,102 @@
final int startIndex = Selection.getSelectionStart(text);
final int endIndex = Selection.getSelectionEnd(text);
- mHandler.post(() -> sendEvent(
+ runOnContentCaptureThread(() -> sendEvent(
new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED)
.setAutofillId(id).setText(eventText)
.setComposingIndex(composingStart, composingEnd)
.setSelectionIndex(startIndex, endIndex)));
}
- /** Public because is also used by ViewRootImpl */
- public void notifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
- mHandler.post(() -> sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED)
+ void notifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
+ runOnContentCaptureThread(() ->
+ sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED)
.setInsets(viewInsets)));
}
- /** Public because is also used by ViewRootImpl */
- public void notifyViewTreeEvent(int sessionId, boolean started) {
+ void notifyViewTreeEvent(int sessionId, boolean started) {
final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED;
final boolean disableFlush = mManager.getFlushViewTreeAppearingEventDisabled();
- mHandler.post(() -> sendEvent(
+ runOnContentCaptureThread(() -> sendEvent(
new ContentCaptureEvent(sessionId, type),
disableFlush ? !started : FORCE_FLUSH));
}
void notifySessionResumed(int sessionId) {
- mHandler.post(() -> sendEvent(
+ runOnContentCaptureThread(() -> sendEvent(
new ContentCaptureEvent(sessionId, TYPE_SESSION_RESUMED), FORCE_FLUSH));
}
void notifySessionPaused(int sessionId) {
- mHandler.post(() -> sendEvent(
+ runOnContentCaptureThread(() -> sendEvent(
new ContentCaptureEvent(sessionId, TYPE_SESSION_PAUSED), FORCE_FLUSH));
}
void notifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
- mHandler.post(() -> sendEvent(new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED)
+ runOnContentCaptureThread(() ->
+ sendEvent(new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED)
.setClientContext(context), FORCE_FLUSH));
}
/** public because is also used by ViewRootImpl */
public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) {
- mHandler.post(() -> sendEvent(
+ runOnContentCaptureThread(() -> sendEvent(
new ContentCaptureEvent(sessionId, TYPE_WINDOW_BOUNDS_CHANGED)
.setBounds(bounds)
));
}
+ /** public because is also used by ViewRootImpl */
+ public void notifyContentCaptureEvents(
+ @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
+ runOnContentCaptureThread(() -> notifyContentCaptureEventsImpl(contentCaptureEvents));
+ }
+
+ private void notifyContentCaptureEventsImpl(
+ @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
+ checkOnContentCaptureThread();
+ try {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents");
+ }
+ for (int i = 0; i < contentCaptureEvents.size(); i++) {
+ int sessionId = contentCaptureEvents.keyAt(i);
+ notifyViewTreeEvent(sessionId, /* started= */ true);
+ ArrayList<Object> events = contentCaptureEvents.valueAt(i);
+ for_each_event: for (int j = 0; j < events.size(); j++) {
+ Object event = events.get(j);
+ if (event instanceof AutofillId) {
+ notifyViewDisappeared(sessionId, (AutofillId) event);
+ } else if (event instanceof View) {
+ View view = (View) event;
+ ContentCaptureSession session = view.getContentCaptureSession();
+ if (session == null) {
+ Log.w(TAG, "no content capture session on view: " + view);
+ continue for_each_event;
+ }
+ int actualId = session.getId();
+ if (actualId != sessionId) {
+ Log.w(TAG, "content capture session mismatch for view (" + view
+ + "): was " + sessionId + " before, it's " + actualId + " now");
+ continue for_each_event;
+ }
+ ViewStructure structure = session.newViewStructure(view);
+ view.onProvideContentCaptureStructure(structure, /* flags= */ 0);
+ session.notifyViewAppeared(structure);
+ } else if (event instanceof Insets) {
+ notifyViewInsetsChanged(sessionId, (Insets) event);
+ } else {
+ Log.w(TAG, "invalid content capture event: " + event);
+ }
+ }
+ notifyViewTreeEvent(sessionId, /* started= */ false);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ }
+
@Override
void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
super.dump(prefix, pw);
@@ -960,17 +1029,14 @@
return getDebugState() + ", reason=" + getFlushReasonAsString(reason);
}
- @UiThread
private boolean isContentProtectionReceiverEnabled() {
return mManager.mOptions.contentProtectionOptions.enableReceiver;
}
- @UiThread
private boolean isContentCaptureReceiverEnabled() {
return mManager.mOptions.enableReceiver;
}
- @UiThread
private boolean isContentProtectionEnabled() {
// Should not be possible for mComponentName to be null here but check anyway
// Should not be possible for groups to be empty if receiver is enabled but check anyway
@@ -980,4 +1046,49 @@
&& (!mManager.mOptions.contentProtectionOptions.requiredGroups.isEmpty()
|| !mManager.mOptions.contentProtectionOptions.optionalGroups.isEmpty());
}
+
+ /**
+ * Checks that the current work is running on the assigned thread from {@code mHandler} and
+ * count the number of times running on the wrong thread.
+ *
+ * <p>It is not guaranteed that the callers always invoke function from a single thread.
+ * Therefore, accessing internal properties in {@link MainContentCaptureSession} should
+ * always delegate to the assigned thread from {@code mHandler} for synchronization.</p>
+ */
+ private void checkOnContentCaptureThread() {
+ final boolean onContentCaptureThread = mHandler.getLooper().isCurrentThread();
+ if (!onContentCaptureThread) {
+ mWrongThreadCount.incrementAndGet();
+ Log.e(TAG, "MainContentCaptureSession running on " + Thread.currentThread());
+ }
+ }
+
+ /** Reports number of times running on the wrong thread. */
+ private void reportWrongThreadMetric() {
+ Counter.logIncrement(
+ CONTENT_CAPTURE_WRONG_THREAD_METRIC_ID, mWrongThreadCount.getAndSet(0));
+ }
+
+ /**
+ * Ensures that {@code r} will be running on the assigned thread.
+ *
+ * <p>This is to prevent unnecessary delegation to Handler that results in fragmented runnable.
+ * </p>
+ */
+ private void runOnContentCaptureThread(@NonNull Runnable r) {
+ if (!mHandler.getLooper().isCurrentThread()) {
+ mHandler.post(r);
+ } else {
+ r.run();
+ }
+ }
+
+ private void clearAndRunOnContentCaptureThread(@NonNull Runnable r, int what) {
+ if (!mHandler.getLooper().isCurrentThread()) {
+ mHandler.removeMessages(what);
+ mHandler.post(r);
+ } else {
+ r.run();
+ }
+ }
}
diff --git a/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java b/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java
index aaf90bd..858401a9 100644
--- a/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java
+++ b/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UiThread;
import android.content.ContentCaptureOptions;
import android.content.pm.ParceledListSlice;
import android.os.Handler;
@@ -102,7 +101,6 @@
}
/** Main entry point for {@link ContentCaptureEvent} processing. */
- @UiThread
public void processEvent(@NonNull ContentCaptureEvent event) {
if (EVENT_TYPES_TO_STORE.contains(event.getType())) {
storeEvent(event);
@@ -112,7 +110,6 @@
}
}
- @UiThread
private void storeEvent(@NonNull ContentCaptureEvent event) {
// Ensure receiver gets the package name which might not be set
ViewNode viewNode = (event.getViewNode() != null) ? event.getViewNode() : new ViewNode();
@@ -121,7 +118,6 @@
mEventBuffer.append(event);
}
- @UiThread
private void processViewAppearedEvent(@NonNull ContentCaptureEvent event) {
ViewNode viewNode = event.getViewNode();
String eventText = ContentProtectionUtils.getEventTextLower(event);
@@ -154,7 +150,6 @@
}
}
- @UiThread
private void loginDetected() {
if (mLastFlushTime == null
|| Instant.now().isAfter(mLastFlushTime.plus(MIN_DURATION_BETWEEN_FLUSHING))) {
@@ -163,13 +158,11 @@
resetLoginFlags();
}
- @UiThread
private void resetLoginFlags() {
mGroupsAll.forEach(group -> group.mFound = false);
mAnyGroupFound = false;
}
- @UiThread
private void maybeResetLoginFlags() {
if (mAnyGroupFound) {
if (mResetLoginRemainingEventsToProcess <= 0) {
@@ -183,7 +176,6 @@
}
}
- @UiThread
private void flush() {
mLastFlushTime = Instant.now();
@@ -192,7 +184,6 @@
mHandler.post(() -> handlerOnLoginDetected(events));
}
- @UiThread
@NonNull
private ParceledListSlice<ContentCaptureEvent> clearEvents() {
List<ContentCaptureEvent> events = Arrays.asList(mEventBuffer.toArray());
diff --git a/core/java/android/webkit/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl
index e177731..c6bd20c 100644
--- a/core/java/android/webkit/IWebViewUpdateService.aidl
+++ b/core/java/android/webkit/IWebViewUpdateService.aidl
@@ -79,4 +79,9 @@
* Used by Settings to enable/disable multiprocess.
*/
void enableMultiProcess(boolean enable);
+
+ /**
+ * Used by Settings to get the default WebView package.
+ */
+ WebViewProviderInfo getDefaultWebViewPackage();
}
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 1b9ff44..8e89541 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -16,6 +16,8 @@
package android.webkit;
+import static android.webkit.Flags.updateServiceV2;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -205,6 +207,9 @@
* Returns whether WebView should run in multiprocess mode.
*/
public boolean isMultiProcessEnabled() {
+ if (updateServiceV2()) {
+ return true;
+ }
try {
return WebViewFactory.getUpdateService().isMultiProcessEnabled();
} catch (RemoteException e) {
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index bc7a5fd..e14ae72 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -16,6 +16,8 @@
package android.webkit;
+import static android.webkit.Flags.updateServiceV2;
+
import android.content.pm.PackageInfo;
import android.os.Build;
import android.os.ChildZygoteProcess;
@@ -50,8 +52,8 @@
private static PackageInfo sPackage;
/**
- * Flag for whether multi-process WebView is enabled. If this is {@code false}, the zygote
- * will not be started.
+ * Flag for whether multi-process WebView is enabled. If this is {@code false}, the zygote will
+ * not be started. Should be removed entirely after we remove the updateServiceV2 flag.
*/
@GuardedBy("sLock")
private static boolean sMultiprocessEnabled = false;
@@ -73,11 +75,19 @@
public static boolean isMultiprocessEnabled() {
synchronized (sLock) {
- return sMultiprocessEnabled && sPackage != null;
+ if (updateServiceV2()) {
+ return sPackage != null;
+ } else {
+ return sMultiprocessEnabled && sPackage != null;
+ }
}
}
public static void setMultiprocessEnabled(boolean enabled) {
+ if (updateServiceV2()) {
+ throw new IllegalStateException(
+ "setMultiprocessEnabled shouldn't be called if update_service_v2 flag is set.");
+ }
synchronized (sLock) {
sMultiprocessEnabled = enabled;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index a919c00..1acebf4 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1054,8 +1054,7 @@
}
private class SetRemoteCollectionItemListAdapterAction extends Action {
- @NonNull
- private CompletableFuture<RemoteCollectionItems> mItemsFuture;
+ private @Nullable RemoteCollectionItems mItems;
final Intent mServiceIntent;
int mIntentId = -1;
boolean mIsReplacedIntoAction = false;
@@ -1064,92 +1063,46 @@
@NonNull RemoteCollectionItems items) {
mViewId = id;
items.setHierarchyRootData(getHierarchyRootData());
- mItemsFuture = CompletableFuture.completedFuture(items);
+ mItems = items;
mServiceIntent = null;
}
SetRemoteCollectionItemListAdapterAction(@IdRes int id, Intent intent) {
mViewId = id;
- mItemsFuture = getItemsFutureFromIntentWithTimeout(intent);
- setHierarchyRootData(getHierarchyRootData());
+ mItems = null;
mServiceIntent = intent;
}
- private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
- Intent intent) {
- if (intent == null) {
- Log.e(LOG_TAG, "Null intent received when generating adapter future");
- return CompletableFuture.completedFuture(new RemoteCollectionItems
- .Builder().build());
- }
-
- final Context context = ActivityThread.currentApplication();
- final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>();
-
- context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE),
- result.defaultExecutor(), new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName componentName,
- IBinder iBinder) {
- RemoteCollectionItems items;
- try {
- items = IRemoteViewsFactory.Stub.asInterface(iBinder)
- .getRemoteCollectionItems();
- } catch (RemoteException re) {
- items = new RemoteCollectionItems.Builder().build();
- Log.e(LOG_TAG, "Error getting collection items from the factory",
- re);
- } finally {
- context.unbindService(this);
- }
-
- result.complete(items);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName componentName) { }
- });
-
- result.completeOnTimeout(
- new RemoteCollectionItems.Builder().build(),
- MAX_ADAPTER_CONVERSION_WAITING_TIME_MS, TimeUnit.MILLISECONDS);
-
- return result;
- }
-
SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
mViewId = parcel.readInt();
mIntentId = parcel.readInt();
- mItemsFuture = CompletableFuture.completedFuture(mIntentId != -1
- ? null
- : new RemoteCollectionItems(parcel, getHierarchyRootData()));
mServiceIntent = parcel.readTypedObject(Intent.CREATOR);
+ mItems = mServiceIntent != null
+ ? null
+ : new RemoteCollectionItems(parcel, getHierarchyRootData());
}
@Override
public void setHierarchyRootData(HierarchyRootData rootData) {
- if (mIntentId == -1) {
- mItemsFuture = mItemsFuture
- .thenApply(rc -> {
- rc.setHierarchyRootData(rootData);
- return rc;
- });
+ if (mItems != null) {
+ mItems.setHierarchyRootData(rootData);
return;
}
- // Set the root data for items in the cache instead
- mCollectionCache.setHierarchyDataForId(mIntentId, rootData);
+ if (mIntentId != -1) {
+ // Set the root data for items in the cache instead
+ mCollectionCache.setHierarchyDataForId(mIntentId, rootData);
+ }
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mViewId);
dest.writeInt(mIntentId);
- if (mIntentId == -1) {
- RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
- items.writeToParcel(dest, flags, /* attached= */ true);
- }
dest.writeTypedObject(mServiceIntent, flags);
+ if (mItems != null) {
+ mItems.writeToParcel(dest, flags, /* attached= */ true);
+ }
}
@Override
@@ -1159,7 +1112,9 @@
if (target == null) return;
RemoteCollectionItems items = mIntentId == -1
- ? getCollectionItemsFromFuture(mItemsFuture)
+ ? mItems == null
+ ? new RemoteCollectionItems.Builder().build()
+ : mItems
: mCollectionCache.getItemsForId(mIntentId);
// Ensure that we are applying to an AppWidget root
@@ -1216,51 +1171,32 @@
@Override
public void visitUris(@NonNull Consumer<Uri> visitor) {
- RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
- items.visitUris(visitor);
- }
- }
+ if (mIntentId != -1 || mItems == null) {
+ return;
+ }
- private static RemoteCollectionItems getCollectionItemsFromFuture(
- CompletableFuture<RemoteCollectionItems> itemsFuture) {
- RemoteCollectionItems items;
- try {
- items = itemsFuture.get();
- } catch (Exception e) {
- Log.e(LOG_TAG, "Error getting collection items from future", e);
- items = new RemoteCollectionItems.Builder().build();
+ mItems.visitUris(visitor);
}
-
- return items;
}
/**
* @hide
*/
- public void collectAllIntents() {
- mCollectionCache.collectAllIntentsNoComplete(this);
+ public CompletableFuture<Void> collectAllIntents() {
+ return mCollectionCache.collectAllIntentsNoComplete(this);
}
private class RemoteCollectionCache {
private SparseArray<String> mIdToUriMapping = new SparseArray<>();
private HashMap<String, RemoteCollectionItems> mUriToCollectionMapping = new HashMap<>();
- // We don't put this into the parcel
- private HashMap<String, CompletableFuture<RemoteCollectionItems>> mTempUriToFutureMapping =
- new HashMap<>();
-
RemoteCollectionCache() { }
RemoteCollectionCache(RemoteCollectionCache src) {
- boolean isWaitingCache = src.mTempUriToFutureMapping.size() != 0;
for (int i = 0; i < src.mIdToUriMapping.size(); i++) {
String uri = src.mIdToUriMapping.valueAt(i);
mIdToUriMapping.put(src.mIdToUriMapping.keyAt(i), uri);
- if (isWaitingCache) {
- mTempUriToFutureMapping.put(uri, src.mTempUriToFutureMapping.get(uri));
- } else {
- mUriToCollectionMapping.put(uri, src.mUriToCollectionMapping.get(uri));
- }
+ mUriToCollectionMapping.put(uri, src.mUriToCollectionMapping.get(uri));
}
}
@@ -1281,14 +1217,8 @@
void setHierarchyDataForId(int intentId, HierarchyRootData data) {
String uri = mIdToUriMapping.get(intentId);
- if (mTempUriToFutureMapping.get(uri) != null) {
- CompletableFuture<RemoteCollectionItems> itemsFuture =
- mTempUriToFutureMapping.get(uri);
- mTempUriToFutureMapping.put(uri, itemsFuture.thenApply(rc -> {
- rc.setHierarchyRootData(data);
- return rc;
- }));
-
+ if (mUriToCollectionMapping.get(uri) == null) {
+ Log.e(LOG_TAG, "Error setting hierarchy data for id=" + intentId);
return;
}
@@ -1301,14 +1231,17 @@
return mUriToCollectionMapping.get(uri);
}
- void collectAllIntentsNoComplete(@NonNull RemoteViews inViews) {
+ CompletableFuture<Void> collectAllIntentsNoComplete(@NonNull RemoteViews inViews) {
+ CompletableFuture<Void> collectionFuture = CompletableFuture.completedFuture(null);
if (inViews.hasSizedRemoteViews()) {
for (RemoteViews remoteViews : inViews.mSizedRemoteViews) {
- remoteViews.collectAllIntents();
+ collectionFuture = CompletableFuture.allOf(collectionFuture,
+ collectAllIntentsNoComplete(remoteViews));
}
} else if (inViews.hasLandscapeAndPortraitLayouts()) {
- inViews.mLandscape.collectAllIntents();
- inViews.mPortrait.collectAllIntents();
+ collectionFuture = CompletableFuture.allOf(
+ collectAllIntentsNoComplete(inViews.mLandscape),
+ collectAllIntentsNoComplete(inViews.mPortrait));
} else if (inViews.mActions != null) {
for (Action action : inViews.mActions) {
if (action instanceof SetRemoteCollectionItemListAdapterAction rca) {
@@ -1318,40 +1251,95 @@
}
if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) {
- String uri = mIdToUriMapping.get(rca.mIntentId);
- mTempUriToFutureMapping.put(uri, rca.mItemsFuture);
- rca.mItemsFuture = CompletableFuture.completedFuture(null);
+ final String uri = mIdToUriMapping.get(rca.mIntentId);
+ collectionFuture = CompletableFuture.allOf(collectionFuture,
+ getItemsFutureFromIntentWithTimeout(rca.mServiceIntent)
+ .thenAccept(rc -> {
+ rc.setHierarchyRootData(getHierarchyRootData());
+ mUriToCollectionMapping.put(uri, rc);
+ }));
+ rca.mItems = null;
continue;
}
// Differentiate between the normal collection actions and the ones with
// intents.
if (rca.mServiceIntent != null) {
- String uri = rca.mServiceIntent.toUri(0);
+ final String uri = rca.mServiceIntent.toUri(0);
int index = mIdToUriMapping.indexOfValue(uri);
if (index == -1) {
int newIntentId = mIdToUriMapping.size();
rca.mIntentId = newIntentId;
mIdToUriMapping.put(newIntentId, uri);
- // mUriToIntentMapping.put(uri, mServiceIntent);
- mTempUriToFutureMapping.put(uri, rca.mItemsFuture);
} else {
rca.mIntentId = mIdToUriMapping.keyAt(index);
+ rca.mItems = null;
+ continue;
}
- rca.mItemsFuture = CompletableFuture.completedFuture(null);
+ collectionFuture = CompletableFuture.allOf(collectionFuture,
+ getItemsFutureFromIntentWithTimeout(rca.mServiceIntent)
+ .thenAccept(rc -> {
+ rc.setHierarchyRootData(getHierarchyRootData());
+ mUriToCollectionMapping.put(uri, rc);
+ }));
+ rca.mItems = null;
} else {
- RemoteCollectionItems items = getCollectionItemsFromFuture(
- rca.mItemsFuture);
- for (RemoteViews views : items.mViews) {
- views.collectAllIntents();
+ for (RemoteViews views : rca.mItems.mViews) {
+ collectionFuture = CompletableFuture.allOf(collectionFuture,
+ collectAllIntentsNoComplete(views));
}
}
} else if (action instanceof ViewGroupActionAdd vgaa
&& vgaa.mNestedViews != null) {
- vgaa.mNestedViews.collectAllIntents();
+ collectionFuture = CompletableFuture.allOf(collectionFuture,
+ collectAllIntentsNoComplete(vgaa.mNestedViews));
}
}
}
+
+ return collectionFuture;
+ }
+
+ private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
+ Intent intent) {
+ if (intent == null) {
+ Log.e(LOG_TAG, "Null intent received when generating adapter future");
+ return CompletableFuture.completedFuture(new RemoteCollectionItems
+ .Builder().build());
+ }
+
+ final Context context = ActivityThread.currentApplication();
+ final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>();
+
+ context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE),
+ result.defaultExecutor(), new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName componentName,
+ IBinder iBinder) {
+ RemoteCollectionItems items;
+ try {
+ items = IRemoteViewsFactory.Stub.asInterface(iBinder)
+ .getRemoteCollectionItems();
+ } catch (RemoteException re) {
+ items = new RemoteCollectionItems.Builder().build();
+ Log.e(LOG_TAG, "Error getting collection items from the factory",
+ re);
+ } finally {
+ context.unbindService(this);
+ }
+
+ result.complete(items);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) { }
+ });
+
+ result.completeOnTimeout(
+ new RemoteCollectionItems.Builder().build(),
+ MAX_ADAPTER_CONVERSION_WAITING_TIME_MS, TimeUnit.MILLISECONDS);
+
+ return result;
}
public void writeToParcel(Parcel out, int flags) {
@@ -1360,10 +1348,7 @@
out.writeInt(mIdToUriMapping.keyAt(i));
String intentUri = mIdToUriMapping.valueAt(i);
out.writeString8(intentUri);
- RemoteCollectionItems items = mTempUriToFutureMapping.get(intentUri) != null
- ? getCollectionItemsFromFuture(mTempUriToFutureMapping.get(intentUri))
- : mUriToCollectionMapping.get(intentUri);
- items.writeToParcel(out, flags, true);
+ mUriToCollectionMapping.get(intentUri).writeToParcel(out, flags, true);
}
}
}
diff --git a/core/java/android/window/SystemPerformanceHinter.java b/core/java/android/window/SystemPerformanceHinter.java
index 5b0d8d1..cc2329fc 100644
--- a/core/java/android/window/SystemPerformanceHinter.java
+++ b/core/java/android/window/SystemPerformanceHinter.java
@@ -20,7 +20,7 @@
import static android.view.Surface.FRAME_RATE_CATEGORY_DEFAULT;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN;
-import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF;
+import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_PROPAGATE;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -303,7 +303,7 @@
SurfaceControl displaySurfaceControl = mDisplayRootProvider.getRootForDisplay(
session.displayId);
mTransaction.setFrameRateSelectionStrategy(displaySurfaceControl,
- FRAME_RATE_SELECTION_STRATEGY_SELF);
+ FRAME_RATE_SELECTION_STRATEGY_PROPAGATE);
// smoothSwitchOnly is false to request a higher framerate, even if it means switching
// the display mode will cause would jank on non-VRR devices because keeping a lower
// refresh rate would mean a poorer user experience.
diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java
index 35ce726..34c6399 100644
--- a/core/java/android/window/WindowInfosListenerForTest.java
+++ b/core/java/android/window/WindowInfosListenerForTest.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -87,6 +88,38 @@
@NonNull
public final Matrix transform;
+ /**
+ * True if the window is touchable.
+ */
+ @SuppressLint("UnflaggedApi") // The API is only used for tests.
+ public final boolean isTouchable;
+
+ /**
+ * True if the window is focusable.
+ */
+ @SuppressLint("UnflaggedApi") // The API is only used for tests.
+ public final boolean isFocusable;
+
+ /**
+ * True if the window is preventing splitting
+ */
+ @SuppressLint("UnflaggedApi") // The API is only used for tests.
+ public final boolean isPreventSplitting;
+
+ /**
+ * True if the window duplicates touches received to wallpaper.
+ */
+ @SuppressLint("UnflaggedApi") // The API is only used for tests.
+ public final boolean isDuplicateTouchToWallpaper;
+
+ /**
+ * True if the window is listening for when there is a touch DOWN event
+ * occurring outside its touchable bounds. When such an event occurs,
+ * this window will receive a MotionEvent with ACTION_OUTSIDE.
+ */
+ @SuppressLint("UnflaggedApi") // The API is only used for tests.
+ public final boolean isWatchOutsideTouch;
+
WindowInfo(@NonNull IBinder windowToken, @NonNull String name, int displayId,
@NonNull Rect bounds, int inputConfig, @NonNull Matrix transform) {
this.windowToken = windowToken;
@@ -96,6 +129,14 @@
this.isTrustedOverlay = (inputConfig & InputConfig.TRUSTED_OVERLAY) != 0;
this.isVisible = (inputConfig & InputConfig.NOT_VISIBLE) == 0;
this.transform = transform;
+ this.isTouchable = (inputConfig & InputConfig.NOT_TOUCHABLE) == 0;
+ this.isFocusable = (inputConfig & InputConfig.NOT_FOCUSABLE) == 0;
+ this.isPreventSplitting = (inputConfig
+ & InputConfig.PREVENT_SPLITTING) != 0;
+ this.isDuplicateTouchToWallpaper = (inputConfig
+ & InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER) != 0;
+ this.isWatchOutsideTouch = (inputConfig
+ & InputConfig.WATCH_OUTSIDE_TOUCH) != 0;
}
@Override
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 7f93213..29932f3 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -48,3 +48,11 @@
is_fixed_read_only: true
bug: "262477923"
}
+
+flag {
+ namespace: "window_surfaces"
+ name: "secure_window_state"
+ description: "Move SC secure flag to WindowState level"
+ is_fixed_read_only: true
+ bug: "308662081"
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 4705dc5..31a3ebd 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -8,6 +8,13 @@
}
flag {
+ name: "edge_to_edge_by_default"
+ namespace: "windowing_frontend"
+ description: "Make app go edge-to-edge by default when targeting SDK 35 or greater"
+ bug: "309578419"
+}
+
+flag {
name: "defer_display_updates"
namespace: "window_manager"
description: "Feature flag for deferring DisplayManager updates to WindowManager if Shell transition is running"
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index 7c4252e..6b074a6 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -33,6 +33,7 @@
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TWO_FINGER_TRIPLE_TAP;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_ALL;
@@ -131,6 +132,18 @@
}
/**
+ * Logs magnification that is assigned to the two finger triple tap shortcut. Calls this when
+ * triggering the magnification two finger triple tap shortcut.
+ */
+ public static void logMagnificationTwoFingerTripleTap(boolean enabled) {
+ FrameworkStatsLog.write(FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED,
+ MAGNIFICATION_COMPONENT_NAME.flattenToString(),
+ // jean update
+ ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TWO_FINGER_TRIPLE_TAP,
+ convertToLoggingServiceStatus(enabled));
+ }
+
+ /**
* Logs accessibility feature name that is assigned to the long pressed accessibility button
* shortcut. Calls this when clicking the long pressed accessibility button shortcut.
*
diff --git a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
index f5fe12e..e55c641 100644
--- a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
+++ b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
@@ -37,22 +37,11 @@
* @return The highest refresh rate
*/
public static float findHighestRefreshRateForDefaultDisplay(Context context) {
- return findHighestRefreshRate(context, Display.DEFAULT_DISPLAY);
- }
-
- /**
- * Find the highest refresh rate among all the modes of the specified display.
- *
- * @param context The context
- * @param displayId The display ID
- * @return The highest refresh rate
- */
- public static float findHighestRefreshRate(Context context, int displayId) {
final DisplayManager dm = context.getSystemService(DisplayManager.class);
- final Display display = dm.getDisplay(displayId);
+ final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
if (display == null) {
- Log.w(TAG, "No valid display device with ID = " + displayId);
+ Log.w(TAG, "No valid default display device");
return DEFAULT_REFRESH_RATE;
}
diff --git a/core/java/com/android/internal/os/MonotonicClock.java b/core/java/com/android/internal/os/MonotonicClock.java
index d0d2354..661628a 100644
--- a/core/java/com/android/internal/os/MonotonicClock.java
+++ b/core/java/com/android/internal/os/MonotonicClock.java
@@ -50,6 +50,8 @@
private final Clock mClock;
private long mTimeshift;
+ public static final long UNDEFINED = -1;
+
public MonotonicClock(File file) {
mFile = new AtomicFile(file);
mClock = Clock.SYSTEM_CLOCK;
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageHidden.java b/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageHidden.java
similarity index 86%
rename from services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageHidden.java
rename to core/java/com/android/internal/pm/parsing/pkg/AndroidPackageHidden.java
index 1fafdf9..0d7b433 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageHidden.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageHidden.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.parsing.pkg;
+package com.android.internal.pm.parsing.pkg;
import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
@@ -22,16 +22,18 @@
/**
* Methods that normal consumers should not have access to. This usually means the field is stateful
- * or deprecated and should be access through {@link AndroidPackageUtils} or a system manager
- * class.
+ * or deprecated and should be access through
+ * {@link com.android.server.pm.parsing.pkg.AndroidPackageUtils} or a system manager class.
* <p>
* This is a separate interface, not implemented by the base {@link AndroidPackage} because Java
* doesn't support non-public interface methods. The class must be cast to this interface.
* <p>
* Because they exist in different packages, some methods are duplicated from
* android.content.pm.parsing.ParsingPackageHidden.
+ * @hide
*/
-interface AndroidPackageHidden {
+// TODO: remove public after moved PackageImpl and AndroidPackageUtils
+public interface AndroidPackageHidden {
/**
* @see ApplicationInfo#primaryCpuAbi
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageInternal.java b/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageInternal.java
similarity index 96%
rename from services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageInternal.java
rename to core/java/com/android/internal/pm/parsing/pkg/AndroidPackageInternal.java
index 9eca7d6..6f8e658 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageInternal.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageInternal.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.parsing.pkg;
+package com.android.internal.pm.parsing.pkg;
import android.annotation.NonNull;
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java b/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java
similarity index 98%
rename from services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
rename to core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java
index 85f8f76..7ef0b48 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/ParsedPackage.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.parsing.pkg;
+package com.android.internal.pm.parsing.pkg;
import android.content.pm.SigningDetails;
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackageSplitImpl.java b/core/java/com/android/internal/pm/pkg/AndroidPackageSplitImpl.java
similarity index 94%
rename from services/core/java/com/android/server/pm/pkg/AndroidPackageSplitImpl.java
rename to core/java/com/android/internal/pm/pkg/AndroidPackageSplitImpl.java
index c0f2c25..3c564e9 100644
--- a/services/core/java/com/android/server/pm/pkg/AndroidPackageSplitImpl.java
+++ b/core/java/com/android/internal/pm/pkg/AndroidPackageSplitImpl.java
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg;
+package com.android.internal.pm.pkg;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
+import com.android.server.pm.pkg.AndroidPackageSplit;
+
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -94,8 +96,8 @@
if (this == o) return true;
if (!(o instanceof AndroidPackageSplitImpl)) return false;
AndroidPackageSplitImpl that = (AndroidPackageSplitImpl) o;
- var fieldsEqual = mRevisionCode == that.mRevisionCode && mFlags == that.mFlags && Objects.equals(
- mName, that.mName) && Objects.equals(mPath, that.mPath)
+ var fieldsEqual = mRevisionCode == that.mRevisionCode && mFlags == that.mFlags
+ && Objects.equals(mName, that.mName) && Objects.equals(mPath, that.mPath)
&& Objects.equals(mClassLoaderName, that.mClassLoaderName);
if (!fieldsEqual) return false;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
similarity index 98%
rename from services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
rename to core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
index 099c676..4ed361f 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.parsing;
+package com.android.internal.pm.pkg.parsing;
import android.annotation.CallSuper;
import android.annotation.NonNull;
@@ -32,6 +32,7 @@
import android.util.SparseIntArray;
import com.android.internal.R;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.pm.pkg.component.ParsedApexSystemService;
import com.android.internal.pm.pkg.component.ParsedAttribution;
@@ -43,7 +44,6 @@
import com.android.internal.pm.pkg.component.ParsedProvider;
import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.pm.pkg.component.ParsedUsesPermission;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.security.PublicKey;
import java.util.List;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageHidden.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageHidden.java
similarity index 96%
rename from services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageHidden.java
rename to core/java/com/android/internal/pm/pkg/parsing/ParsingPackageHidden.java
index 9c1c9ac..5758fd7 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageHidden.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageHidden.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.pm.pkg.parsing;
+package com.android.internal.pm.pkg.parsing;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 942aa8c..dd310dc 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -175,6 +175,14 @@
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
private static final long NAV_BAR_COLOR_DEFAULT_TRANSPARENT = 232195501L;
+ /**
+ * Make app go edge-to-edge by default if the target SDK is
+ * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} or above.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private static final long EDGE_TO_EDGE_BY_DEFAULT = 309578419;
+
private static final int CUSTOM_TITLE_COMPATIBLE_FEATURES = DEFAULT_FEATURES |
(1 << FEATURE_CUSTOM_TITLE) |
(1 << FEATURE_CONTENT_TRANSITIONS) |
@@ -387,7 +395,9 @@
mAllowFloatingWindowsFillScreen = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowFloatingWindowsFillScreen);
mDefaultEdgeToEdge =
- context.getApplicationInfo().targetSdkVersion >= DEFAULT_EDGE_TO_EDGE_SDK_VERSION;
+ context.getApplicationInfo().targetSdkVersion >= DEFAULT_EDGE_TO_EDGE_SDK_VERSION
+ || (CompatChanges.isChangeEnabled(EDGE_TO_EDGE_BY_DEFAULT)
+ && Flags.edgeToEdgeByDefault());
if (mDefaultEdgeToEdge) {
mDecorFitsSystemWindows = false;
}
@@ -2558,11 +2568,11 @@
mNavigationBarColor =
navBarColor == navBarDefaultColor
+ && !mDefaultEdgeToEdge
&& !(CompatChanges.isChangeEnabled(NAV_BAR_COLOR_DEFAULT_TRANSPARENT)
&& Flags.navBarTransparentByDefault())
&& !context.getResources().getBoolean(
R.bool.config_navBarDefaultTransparent)
- && !mDefaultEdgeToEdge
? navBarCompatibleColor
: navBarColor;
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index dadeb2b..aab2242 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -60,7 +60,8 @@
@UnsupportedAppUsage(maxTargetSdk = 28)
void notifyCallForwardingChanged(boolean cfi);
void notifyCallForwardingChangedForSubscriber(in int subId, boolean cfi);
- void notifyDataActivityForSubscriber(int phoneId, int subId, int state);
+ void notifyDataActivityForSubscriber(int subId, int state);
+ void notifyDataActivityForSubscriberWithSlot(int phoneId, int subId, int state);
void notifyDataConnectionForSubscriber(
int phoneId, int subId, in PreciseDataConnectionState preciseState);
// Uses CellIdentity which is Parcelable here; will convert to CellLocation in client.
diff --git a/core/java/com/android/server/pm/OWNERS b/core/java/com/android/server/pm/OWNERS
new file mode 100644
index 0000000..6ef34e2
--- /dev/null
+++ b/core/java/com/android/server/pm/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36137
+
+file:/PACKAGE_MANAGER_OWNERS
+
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java
similarity index 98%
rename from services/core/java/com/android/server/pm/pkg/AndroidPackage.java
rename to core/java/com/android/server/pm/pkg/AndroidPackage.java
index 99819c8..4e4f26c 100644
--- a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -58,7 +58,6 @@
import com.android.internal.pm.pkg.component.ParsedProvider;
import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.pm.pkg.component.ParsedUsesPermission;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import java.security.PublicKey;
import java.util.List;
@@ -691,7 +690,7 @@
/**
* The names of packages to adopt ownership of permissions from, parsed under {@link
- * ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}.
+ * com.android.server.pm.pkg.parsing.ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}.
*
* @see R.styleable#AndroidManifestOriginalPackage_name
* @hide
@@ -796,7 +795,7 @@
/**
* For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in {@link
- * ParsingPackageUtils#TAG_KEY_SETS}.
+ * com.android.server.pm.pkg.parsing.ParsingPackageUtils#TAG_KEY_SETS}.
*
* @see R.styleable#AndroidManifestKeySet
* @see R.styleable#AndroidManifestPublicKey
@@ -905,7 +904,7 @@
* For system use to migrate from an old package name to a new one, moving over data if
* available.
*
- * @see R.styleable#AndroidManifestOriginalPackage}
+ * @see R.styleable#AndroidManifestOriginalPackage
* @hide
*/
@NonNull
@@ -1267,7 +1266,7 @@
/**
* For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in {@link
- * ParsingPackageUtils#TAG_KEY_SETS}.
+ * com.android.server.pm.pkg.parsing.ParsingPackageUtils#TAG_KEY_SETS}.
*
* @see R.styleable#AndroidManifestUpgradeKeySet
* @hide
@@ -1417,6 +1416,7 @@
* @see ApplicationInfo#FLAG_IS_GAME
* @see R.styleable#AndroidManifestApplication_isGame
* @hide
+ * @deprecated
*/
@Deprecated
boolean isGame();
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackageSplit.java b/core/java/com/android/server/pm/pkg/AndroidPackageSplit.java
similarity index 100%
rename from services/core/java/com/android/server/pm/pkg/AndroidPackageSplit.java
rename to core/java/com/android/server/pm/pkg/AndroidPackageSplit.java
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 440a332..f365dbb 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -462,9 +462,4 @@
],
},
},
-
- // Workaround Clang LTO crash.
- lto: {
- never: true,
- },
}
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 262f5e8..239c626 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -81,7 +81,8 @@
deviceInfo.getId(), deviceInfo.getGeneration(),
deviceInfo.getControllerNumber(), nameObj.get(),
static_cast<int32_t>(ident.vendor),
- static_cast<int32_t>(ident.product), descriptorObj.get(),
+ static_cast<int32_t>(ident.product),
+ static_cast<int32_t>(ident.bus), descriptorObj.get(),
deviceInfo.isExternal(), deviceInfo.getSources(),
deviceInfo.getKeyboardType(), kcmObj.get(),
keyboardLanguageTagObj.get(), keyboardLayoutTypeObj.get(),
@@ -111,7 +112,7 @@
gInputDeviceClassInfo.clazz = MakeGlobalRefOrDie(env, gInputDeviceClassInfo.clazz);
gInputDeviceClassInfo.ctor = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>",
- "(IIILjava/lang/String;IILjava/lang/"
+ "(IIILjava/lang/String;IIILjava/lang/"
"String;ZIILandroid/view/KeyCharacterMap;Ljava/"
"lang/String;Ljava/lang/String;ZZZZZIII)V");
diff --git a/core/proto/android/os/batteryusagestats.proto b/core/proto/android/os/batteryusagestats.proto
index 2b74220..11b367b 100644
--- a/core/proto/android/os/batteryusagestats.proto
+++ b/core/proto/android/os/batteryusagestats.proto
@@ -92,8 +92,24 @@
message UidBatteryConsumer {
optional int32 uid = 1;
optional BatteryConsumerData battery_consumer_data = 2;
- optional int64 time_in_foreground_millis = 3;
- optional int64 time_in_background_millis = 4;
+ // DEPRECATED Use time_in_state instead.
+ optional int64 time_in_foreground_millis = 3 [deprecated = true];
+ // DEPRECATED Use time_in_state instead.
+ optional int64 time_in_background_millis = 4 [deprecated = true];
+
+ message TimeInState {
+ enum ProcessState {
+ UNSPECIFIED = 0;
+ FOREGROUND = 1;
+ BACKGROUND = 2;
+ FOREGROUND_SERVICE = 3;
+ }
+
+ optional ProcessState process_state = 1;
+ optional int64 time_in_state_millis = 2;
+ }
+
+ repeated TimeInState time_in_state = 5;
}
repeated UidBatteryConsumer uid_battery_consumers = 5;
diff --git a/core/proto/android/server/usagestatsservice_v2.proto b/core/proto/android/server/usagestatsservice_v2.proto
index d5cf60d..c242c9c 100644
--- a/core/proto/android/server/usagestatsservice_v2.proto
+++ b/core/proto/android/server/usagestatsservice_v2.proto
@@ -110,6 +110,7 @@
optional int32 task_root_package_token = 11;
optional int32 task_root_class_token = 12;
optional int32 locus_id_token = 13;
+ optional ObfuscatedUserInteractionExtrasProto interaction_extras = 14;
}
/**
@@ -129,6 +130,7 @@
optional string task_root_package = 11;
optional string task_root_class = 12;
optional string locus_id = 13 [(.android.privacy).dest = DEST_EXPLICIT];
+ optional bytes extras = 14;
}
/**
@@ -145,3 +147,11 @@
// Stores the mappings for every package
repeated PackagesMap packages_map = 2;
}
+
+/**
+ * Store the relevant information from extra details for user interaction event.
+ */
+message ObfuscatedUserInteractionExtrasProto {
+ optional int32 category_token = 1;
+ optional int32 action_token = 2;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4d208c6..0021640 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7817,6 +7817,13 @@
<permission android:name="android.permission.RESET_HOTWORD_TRAINING_DATA_EGRESS_COUNT"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows an app to track all preparations for a complete factory reset.
+ <p>Protection level: signature|privileged
+ @FlaggedApi("android.permission.flags.factory_reset_prep_permission_apis")
+ @hide -->
+ <permission android:name="android.permission.PREPARE_FACTORY_RESET"
+ android:protectionLevel="signature|privileged" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/OWNERS b/core/res/OWNERS
index f24c3f5..332ad2a 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -47,6 +47,10 @@
# Wear
per-file res/*-watch/* = file:/WEAR_OWNERS
+# Peformance
+per-file res/values/config.xml = file:/PERFORMANCE_OWNERS
+per-file res/values/symbols.xml = file:/PERFORMANCE_OWNERS
+
# PowerProfile
per-file res/xml/power_profile.xml = file:/BATTERY_STATS_OWNERS
per-file res/xml/power_profile_test.xml = file:/BATTERY_STATS_OWNERS
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6cd6eb4..8bbc809 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1522,6 +1522,10 @@
config_screenBrightnessSettingDefaultFloat instead -->
<integer name="config_screenBrightnessSettingDefault">102</integer>
+ <!-- Maximum screen brightness setting when screen brightness capped in Wear Bedtime mode.
+ The value must be in the range [0, 255]. -->
+ <integer name="config_screenBrightnessCapForWearBedtimeMode">20</integer>
+
<!-- Minimum screen brightness setting allowed by power manager.
-2 is invalid so setting will resort to int value specified above.
Set this to 0.0 to allow screen to go to minimal brightness.
@@ -4336,6 +4340,9 @@
<!-- True if assistant app should be pinned via Pinner Service -->
<bool name="config_pinnerAssistantApp">false</bool>
+ <!-- Bytes that the PinnerService will pin for WebView -->
+ <integer name="config_pinnerWebviewPinBytes">0</integer>
+
<!-- Number of days preloaded file cache should be preserved on a device before it can be
deleted -->
<integer name="config_keepPreloadsMinDays">7</integer>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index eed186a..73a7e42 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6350,4 +6350,20 @@
<string name="keyboard_layout_notification_multiple_selected_title">Physical keyboards configured</string>
<!-- Notification message when multiple keyboards with selected layouts have been connected the first time simultaneously [CHAR LIMIT=NOTIF_BODY] -->
<string name="keyboard_layout_notification_multiple_selected_message">Tap to view keyboards</string>
+
+ <!-- Private profile label on a screen. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
+ <string name="profile_label_private">Private</string>
+ <!-- Clone profile label on a screen. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
+ <string name="profile_label_clone">Clone</string>
+ <!-- Work profile label on a screen. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
+ <string name="profile_label_work">Work</string>
+ <!-- 2nd Work profile label on a screen in case a device has more than one work profiles. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
+ <string name="profile_label_work_2">Work 2</string>
+ <!-- 3rd Work profile label on a screen in case a device has more than two work profiles. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
+ <string name="profile_label_work_3">Work 3</string>
+ <!-- Test profile label on a screen. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
+ <string name="profile_label_test">Test</string>
+ <!-- Communal profile label on a screen. This can be used as a tab label for this profile in tabbed views and can be used to represent the profile in sharing surfaces, etc. [CHAR LIMIT=20] -->
+ <string name="profile_label_communal">Communal</string>
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 24b39bc..3d43004 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1086,6 +1086,13 @@
<java-symbol type="string" name="managed_profile_label_badge_3" />
<java-symbol type="string" name="clone_profile_label_badge" />
<java-symbol type="string" name="private_profile_label_badge" />
+ <java-symbol type="string" name="profile_label_private" />
+ <java-symbol type="string" name="profile_label_clone" />
+ <java-symbol type="string" name="profile_label_work" />
+ <java-symbol type="string" name="profile_label_work_2" />
+ <java-symbol type="string" name="profile_label_work_3" />
+ <java-symbol type="string" name="profile_label_test" />
+ <java-symbol type="string" name="profile_label_communal" />
<java-symbol type="string" name="mediasize_unknown_portrait" />
<java-symbol type="string" name="mediasize_unknown_landscape" />
<java-symbol type="string" name="mediasize_iso_a0" />
@@ -2071,6 +2078,7 @@
<java-symbol type="integer" name="config_screenBrightnessSettingMinimum" />
<java-symbol type="integer" name="config_screenBrightnessSettingMaximum" />
<java-symbol type="integer" name="config_screenBrightnessSettingDefault" />
+ <java-symbol type="integer" name="config_screenBrightnessCapForWearBedtimeMode" />
<java-symbol type="dimen" name="config_screenBrightnessSettingMinimumFloat" />
<java-symbol type="dimen" name="config_screenBrightnessSettingMaximumFloat" />
<java-symbol type="dimen" name="config_screenBrightnessSettingDefaultFloat" />
@@ -3378,6 +3386,7 @@
<java-symbol type="bool" name="config_pinnerCameraApp" />
<java-symbol type="bool" name="config_pinnerHomeApp" />
<java-symbol type="bool" name="config_pinnerAssistantApp" />
+ <java-symbol type="integer" name="config_pinnerWebviewPinBytes" />
<java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
diff --git a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
index 5a202c5..9dce899 100644
--- a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
+++ b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java
@@ -28,6 +28,7 @@
import android.app.usage.UsageEvents.Event;
import android.content.res.Configuration;
import android.os.Parcel;
+import android.os.PersistableBundle;
import android.test.suitebuilder.annotation.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -140,6 +141,12 @@
case Event.LOCUS_ID_SET:
event.mLocusId = anyString();
break;
+ case Event.USER_INTERACTION:
+ PersistableBundle extras = new PersistableBundle();
+ extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, anyString());
+ extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, anyString());
+ event.mExtras = extras;
+ break;
}
event.mFlags = anyInt();
@@ -176,6 +183,14 @@
case Event.LOCUS_ID_SET:
assertEquals(ue1.mLocusId, ue2.mLocusId);
break;
+ case Event.USER_INTERACTION:
+ final PersistableBundle extras1 = ue1.getExtras();
+ final PersistableBundle extras2 = ue2.getExtras();
+ assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY),
+ extras2.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY));
+ assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_ACTION),
+ extras2.getString(UsageStatsManager.EXTRA_EVENT_ACTION));
+ break;
}
assertEquals(ue1.mFlags, ue2.mFlags);
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
index 083e37a..fae7148 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsPersistenceTest.java
@@ -72,7 +72,7 @@
"mShortcutId", "mShortcutIdToken", "mBucketAndReason", "mInstanceId",
"mNotificationChannelId", "mNotificationChannelIdToken", "mTaskRootPackage",
"mTaskRootPackageToken", "mTaskRootClass", "mTaskRootClassToken", "mLocusId",
- "mLocusIdToken"};
+ "mLocusIdToken", "mExtras", "mUserInteractionExtrasToken"};
// All fields in this list are defined in UsageEvents.Event but not persisted
private static final String[] USAGEEVENTS_IGNORED_FIELDS = {"mAction", "mContentAnnotations",
"mContentType", "DEVICE_EVENT_PACKAGE_NAME", "FLAG_IS_PACKAGE_INSTANT_APP",
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 87c167c..4a9cb71 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -83,6 +83,12 @@
}
@Override
+ protected AssetManager createAssetManager(@NonNull final ResourcesKey key,
+ ResourcesManager.ApkAssetsSupplier apkSupplier) {
+ return createAssetManager(key);
+ }
+
+ @Override
protected DisplayMetrics getDisplayMetrics(int displayId, DisplayAdjustments daj) {
return mDisplayMetricsMap.get(displayId);
}
@@ -100,7 +106,7 @@
null, APP_ONE_RES_DIR, null, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(newResources);
- assertSame(resources, newResources);
+ assertSame(resources.getImpl(), newResources.getImpl());
}
@SmallTest
diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
index d47d789..1cdcb37 100644
--- a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
@@ -17,11 +17,15 @@
package android.view.contentcapture;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
+import static android.view.contentcapture.ContentCaptureSession.FLUSH_REASON_VIEW_TREE_APPEARED;
+import static android.view.contentcapture.ContentCaptureSession.FLUSH_REASON_VIEW_TREE_APPEARING;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -29,14 +33,20 @@
import android.content.ContentCaptureOptions;
import android.content.Context;
import android.content.pm.ParceledListSlice;
+import android.graphics.Insets;
import android.os.Handler;
-import android.os.Looper;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.autofill.AutofillId;
import android.view.contentprotection.ContentProtectionEventProcessor;
import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,8 +66,9 @@
* <p>Run with: {@code atest
* FrameworksCoreTests:android.view.contentcapture.MainContentCaptureSessionTest}
*/
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
@SmallTest
+@TestableLooper.RunWithLooper
public class MainContentCaptureSessionTest {
private static final int BUFFER_SIZE = 100;
@@ -75,6 +86,8 @@
private static final ContentCaptureManager.StrippedContext sStrippedContext =
new ContentCaptureManager.StrippedContext(sContext);
+ private TestableLooper mTestableLooper;
+
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock private IContentCaptureManager mMockSystemServerInterface;
@@ -83,12 +96,18 @@
@Mock private IContentCaptureDirectManager mMockContentCaptureDirectManager;
+ @Before
+ public void setup() {
+ mTestableLooper = TestableLooper.get(this);
+ }
+
@Test
public void onSessionStarted_contentProtectionEnabled_processorCreated() {
MainContentCaptureSession session = createSession();
assertThat(session.mContentProtectionEventProcessor).isNull();
session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+ mTestableLooper.processAllMessages();
assertThat(session.mContentProtectionEventProcessor).isNotNull();
}
@@ -102,6 +121,7 @@
session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+ mTestableLooper.processAllMessages();
assertThat(session.mContentProtectionEventProcessor).isNull();
verifyZeroInteractions(mMockContentProtectionEventProcessor);
@@ -122,6 +142,7 @@
session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+ mTestableLooper.processAllMessages();
assertThat(session.mContentProtectionEventProcessor).isNull();
verifyZeroInteractions(mMockContentProtectionEventProcessor);
@@ -142,6 +163,7 @@
session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+ mTestableLooper.processAllMessages();
assertThat(session.mContentProtectionEventProcessor).isNull();
verifyZeroInteractions(mMockContentProtectionEventProcessor);
@@ -153,6 +175,7 @@
session.mComponentName = null;
session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+ mTestableLooper.processAllMessages();
assertThat(session.mContentProtectionEventProcessor).isNull();
}
@@ -166,6 +189,7 @@
session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
session.sendEvent(EVENT);
+ mTestableLooper.processAllMessages();
verifyZeroInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
@@ -180,6 +204,7 @@
session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
session.sendEvent(EVENT);
+ mTestableLooper.processAllMessages();
verify(mMockContentProtectionEventProcessor).processEvent(EVENT);
assertThat(session.mEvents).isNull();
@@ -194,6 +219,7 @@
session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
session.sendEvent(EVENT);
+ mTestableLooper.processAllMessages();
verifyZeroInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNotNull();
@@ -206,6 +232,7 @@
session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
session.sendEvent(EVENT);
+ mTestableLooper.processAllMessages();
verify(mMockContentProtectionEventProcessor).processEvent(EVENT);
assertThat(session.mEvents).isNotNull();
@@ -220,6 +247,7 @@
/* enableContentProtectionReceiver= */ true);
session.sendEvent(EVENT);
+ mTestableLooper.processAllMessages();
verifyZeroInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isNull();
@@ -236,6 +264,7 @@
session.mDirectServiceInterface = mMockContentCaptureDirectManager;
session.flush(REASON);
+ mTestableLooper.processAllMessages();
verifyZeroInteractions(mMockContentProtectionEventProcessor);
verifyZeroInteractions(mMockContentCaptureDirectManager);
@@ -252,6 +281,7 @@
session.mDirectServiceInterface = mMockContentCaptureDirectManager;
session.flush(REASON);
+ mTestableLooper.processAllMessages();
verifyZeroInteractions(mMockContentProtectionEventProcessor);
verifyZeroInteractions(mMockContentCaptureDirectManager);
@@ -269,6 +299,7 @@
session.mDirectServiceInterface = mMockContentCaptureDirectManager;
session.flush(REASON);
+ mTestableLooper.processAllMessages();
verifyZeroInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isEmpty();
@@ -286,6 +317,7 @@
session.mDirectServiceInterface = mMockContentCaptureDirectManager;
session.flush(REASON);
+ mTestableLooper.processAllMessages();
verifyZeroInteractions(mMockContentProtectionEventProcessor);
assertThat(session.mEvents).isEmpty();
@@ -298,6 +330,7 @@
session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
session.destroySession();
+ mTestableLooper.processAllMessages();
verify(mMockSystemServerInterface).finishSession(anyInt());
verifyZeroInteractions(mMockContentProtectionEventProcessor);
@@ -311,6 +344,7 @@
session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
session.resetSession(/* newState= */ 0);
+ mTestableLooper.processAllMessages();
verifyZeroInteractions(mMockSystemServerInterface);
verifyZeroInteractions(mMockContentProtectionEventProcessor);
@@ -318,6 +352,111 @@
assertThat(session.mContentProtectionEventProcessor).isNull();
}
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void notifyContentCaptureEvents_notStarted_ContentCaptureDisabled_ProtectionDisabled() {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ false,
+ /* enableContentProtectionReceiver= */ false);
+ MainContentCaptureSession session = createSession(options);
+
+ notifyContentCaptureEvents(session);
+ mTestableLooper.processAllMessages();
+
+ verifyZeroInteractions(mMockContentCaptureDirectManager);
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ assertThat(session.mEvents).isNull();
+ }
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void notifyContentCaptureEvents_started_ContentCaptureDisabled_ProtectionDisabled() {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ false,
+ /* enableContentProtectionReceiver= */ false);
+ MainContentCaptureSession session = createSession(options);
+
+ session.onSessionStarted(0x2, null);
+ notifyContentCaptureEvents(session);
+ mTestableLooper.processAllMessages();
+
+ verifyZeroInteractions(mMockContentCaptureDirectManager);
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ assertThat(session.mEvents).isNull();
+ }
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void notifyContentCaptureEvents_notStarted_ContentCaptureEnabled_ProtectionEnabled() {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ true,
+ /* enableContentProtectionReceiver= */ true);
+ MainContentCaptureSession session = createSession(options);
+ session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+ session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+ notifyContentCaptureEvents(session);
+ mTestableLooper.processAllMessages();
+
+ verifyZeroInteractions(mMockContentCaptureDirectManager);
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ assertThat(session.mEvents).isNull();
+ }
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void notifyContentCaptureEvents_started_ContentCaptureEnabled_ProtectionEnabled()
+ throws RemoteException {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ true,
+ /* enableContentProtectionReceiver= */ true);
+ MainContentCaptureSession session = createSession(options);
+ session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+
+ session.onSessionStarted(0x2, null);
+ // Override the processor for interaction verification.
+ session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+ notifyContentCaptureEvents(session);
+ mTestableLooper.processAllMessages();
+
+ // Force flush will happen twice.
+ verify(mMockContentCaptureDirectManager, times(1))
+ .sendEvents(any(), eq(FLUSH_REASON_VIEW_TREE_APPEARING), any());
+ verify(mMockContentCaptureDirectManager, times(1))
+ .sendEvents(any(), eq(FLUSH_REASON_VIEW_TREE_APPEARED), any());
+ // Other than the five view events, there will be two additional tree appearing events.
+ verify(mMockContentProtectionEventProcessor, times(7)).processEvent(any());
+ assertThat(session.mEvents).isEmpty();
+ }
+
+ /** Simulates the regular content capture events sequence. */
+ private void notifyContentCaptureEvents(final MainContentCaptureSession session) {
+ final ArrayList<Object> events = new ArrayList<>(
+ List.of(
+ prepareView(session),
+ prepareView(session),
+ new AutofillId(0),
+ prepareView(session),
+ Insets.of(0, 0, 0, 0)
+ )
+ );
+
+ final SparseArray<ArrayList<Object>> contentCaptureEvents = new SparseArray<>();
+ contentCaptureEvents.set(session.getId(), events);
+
+ session.notifyContentCaptureEvents(contentCaptureEvents);
+ }
+
+ private View prepareView(final MainContentCaptureSession session) {
+ final View view = new View(sContext);
+ view.setContentCaptureSession(session);
+ return view;
+ }
+
private static ContentCaptureOptions createOptions(
boolean enableContentCaptureReceiver,
ContentCaptureOptions.ContentProtectionOptions contentProtectionOptions) {
@@ -354,7 +493,7 @@
new MainContentCaptureSession(
sStrippedContext,
manager,
- new Handler(Looper.getMainLooper()),
+ Handler.createAsync(mTestableLooper.getLooper()),
mMockSystemServerInterface);
session.mComponentName = COMPONENT_NAME;
return session;
diff --git a/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java b/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
index 6229530..3147eac 100644
--- a/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
+++ b/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
@@ -21,7 +21,7 @@
import static android.view.Surface.FRAME_RATE_CATEGORY_DEFAULT;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN;
-import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF;
+import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_PROPAGATE;
import static android.window.SystemPerformanceHinter.HINT_ADPF;
import static android.window.SystemPerformanceHinter.HINT_ALL;
import static android.window.SystemPerformanceHinter.HINT_SF_EARLY_WAKEUP;
@@ -170,7 +170,7 @@
// Verify we call SF
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ eq(FRAME_RATE_SELECTION_STRATEGY_PROPAGATE));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_CATEGORY_DEFAULT),
@@ -262,7 +262,7 @@
// Verify we call SF and perf manager to clean up
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ eq(FRAME_RATE_SELECTION_STRATEGY_PROPAGATE));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_CATEGORY_DEFAULT),
@@ -283,7 +283,7 @@
// Verify we call SF and perf manager to clean up
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ eq(FRAME_RATE_SELECTION_STRATEGY_PROPAGATE));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_CATEGORY_DEFAULT),
@@ -334,7 +334,7 @@
// Verify we call SF and perf manager to clean up
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ eq(FRAME_RATE_SELECTION_STRATEGY_PROPAGATE));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_CATEGORY_DEFAULT),
@@ -385,7 +385,7 @@
session1.close();
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
- eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ eq(FRAME_RATE_SELECTION_STRATEGY_PROPAGATE));
verify(mTransaction).setFrameRateCategory(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_CATEGORY_DEFAULT),
@@ -410,7 +410,7 @@
anyInt());
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mSecondaryDisplayRoot),
- eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ eq(FRAME_RATE_SELECTION_STRATEGY_PROPAGATE));
verify(mTransaction).setFrameRateCategory(
eq(mSecondaryDisplayRoot),
eq(FRAME_RATE_CATEGORY_DEFAULT),
diff --git a/core/tests/timetests/TEST_MAPPING b/core/tests/timetests/TEST_MAPPING
index 5748044..0796d5a 100644
--- a/core/tests/timetests/TEST_MAPPING
+++ b/core/tests/timetests/TEST_MAPPING
@@ -1,6 +1,5 @@
{
- // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
- "postsubmit": [
+ "presubmit": [
{
"name": "FrameworksTimeCoreTests"
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index f19acbe..2237ba1 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -529,6 +529,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ActivityStarter.java"
},
+ "-1582845629": {
+ "message": "Starting animation on %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_DIMMER",
+ "at": "com\/android\/server\/wm\/DimmerAnimationHelper.java"
+ },
"-1575977269": {
"message": "Skipping %s: mismatch root %s",
"level": "DEBUG",
@@ -925,6 +931,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "-1243510456": {
+ "message": "Dim animation requested: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_DIMMER",
+ "at": "com\/android\/server\/wm\/DimmerAnimationHelper.java"
+ },
"-1237827119": {
"message": "Schedule remove starting %s startingWindow=%s animate=%b Callers=%s",
"level": "VERBOSE",
@@ -991,12 +1003,6 @@
"group": "WM_DEBUG_WINDOW_INSETS",
"at": "com\/android\/server\/wm\/InsetsSourceProvider.java"
},
- "-1176488860": {
- "message": "SURFACE isSecure=%b: %s",
- "level": "INFO",
- "group": "WM_SHOW_TRANSACTIONS",
- "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
- },
"-1164930508": {
"message": "Moving to RESUMED: %s (starting new instance) callers=%s",
"level": "VERBOSE",
@@ -1177,6 +1183,12 @@
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
+ "-1028213464": {
+ "message": "%s skipping animation and directly setting alpha=%f, blur=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_DIMMER",
+ "at": "com\/android\/server\/wm\/DimmerAnimationHelper.java"
+ },
"-1022146708": {
"message": "Skipping %s: mismatch activity type",
"level": "DEBUG",
@@ -1801,12 +1813,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-504637678": {
- "message": "Starting animation on dim layer %s, requested by %s, alpha: %f -> %f, blur: %d -> %d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_DIMMER",
- "at": "com\/android\/server\/wm\/SmoothDimmer.java"
- },
"-503656156": {
"message": "Update process config of %s to new config %s",
"level": "VERBOSE",
@@ -3277,6 +3283,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "810599500": {
+ "message": "SURFACE isSecure=%b: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"829434921": {
"message": "Draw state now committed in %s",
"level": "VERBOSE",
@@ -4027,12 +4039,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "1620751818": {
- "message": "Dim %s skipping animation and directly setting alpha=%f, blur=%d",
- "level": "DEBUG",
- "group": "WM_DEBUG_DIMMER",
- "at": "com\/android\/server\/wm\/SmoothDimmer.java"
- },
"1621562070": {
"message": " startWCT=%s",
"level": "VERBOSE",
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 5e16bce..dd703f5 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -33,7 +33,6 @@
import android.util.Log;
import java.util.Calendar;
-import java.util.Objects;
/**
* @hide This should not be made public in its present form because it
@@ -139,13 +138,25 @@
return new KeyStore2();
}
+ /**
+ * Gets the {@link IKeystoreService} that should be started in early_hal in Android.
+ *
+ * @throws IllegalStateException if the KeystoreService is not available or has not
+ * been initialized when called. This is a state that should not happen and indicates
+ * and error somewhere in the stack or with the calling processes access permissions.
+ */
@NonNull private synchronized IKeystoreService getService(boolean retryLookup) {
if (mBinder == null || retryLookup) {
mBinder = IKeystoreService.Stub.asInterface(ServiceManager
- .getService(KEYSTORE2_SERVICE_NAME));
- Binder.allowBlocking(mBinder.asBinder());
+ .getService(KEYSTORE2_SERVICE_NAME));
}
- return Objects.requireNonNull(mBinder);
+ if (mBinder == null) {
+ throw new IllegalStateException(
+ "Could not connect to Keystore service. Keystore may have crashed or not been"
+ + " initialized");
+ }
+ Binder.allowBlocking(mBinder.asBinder());
+ return mBinder;
}
void delete(KeyDescriptor descriptor) throws KeyStoreException {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 3e11327..e74e578d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -788,7 +788,7 @@
mLayerView.setOnApplyWindowInsetsListener((view, windowInsets) -> {
if (!windowInsets.equals(mWindowInsets) && mLayerView != null) {
mWindowInsets = windowInsets;
- mBubblePositioner.update();
+ mBubblePositioner.update(DeviceConfig.create(mContext, mWindowManager));
mLayerView.onDisplaySizeChanged();
}
return windowInsets;
@@ -798,7 +798,7 @@
mStackView.setOnApplyWindowInsetsListener((view, windowInsets) -> {
if (!windowInsets.equals(mWindowInsets) && mStackView != null) {
mWindowInsets = windowInsets;
- mBubblePositioner.update();
+ mBubblePositioner.update(DeviceConfig.create(mContext, mWindowManager));
mStackView.onDisplaySizeChanged();
}
return windowInsets;
@@ -980,7 +980,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (mBubblePositioner != null) {
- mBubblePositioner.update();
+ mBubblePositioner.update(DeviceConfig.create(mContext, mWindowManager));
}
if (mStackView != null && newConfig != null) {
if (newConfig.densityDpi != mDensityDpi
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 09ae84a..1efd9df 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
@@ -16,10 +16,7 @@
package com.android.wm.shell.bubbles;
-import static android.view.View.LAYOUT_DIRECTION_RTL;
-
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Point;
@@ -28,9 +25,7 @@
import android.graphics.RectF;
import android.util.Log;
import android.view.Surface;
-import android.view.WindowInsets;
import android.view.WindowManager;
-import android.view.WindowMetrics;
import androidx.annotation.VisibleForTesting;
@@ -68,15 +63,12 @@
private static final float EXPANDED_VIEW_BUBBLE_BAR_LANDSCAPE_WIDTH_PERCENT = 0.4f;
private Context mContext;
- private WindowManager mWindowManager;
+ private DeviceConfig mDeviceConfig;
private Rect mScreenRect;
private @Surface.Rotation int mRotation = Surface.ROTATION_0;
private Insets mInsets;
private boolean mImeVisible;
private int mImeHeight;
- private boolean mIsLargeScreen;
- private boolean mIsSmallTablet;
-
private Rect mPositionRect;
private int mDefaultMaxBubbles;
private int mMaxBubbles;
@@ -110,44 +102,27 @@
public BubblePositioner(Context context, WindowManager windowManager) {
mContext = context;
- mWindowManager = windowManager;
- update();
+ mDeviceConfig = DeviceConfig.create(context, windowManager);
+ update(mDeviceConfig);
}
/**
* Available space and inset information. Call this when config changes
* occur or when added to a window.
*/
- public void update() {
- WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
- if (windowMetrics == null) {
- return;
- }
- WindowInsets metricInsets = windowMetrics.getWindowInsets();
- Insets insets = metricInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()
- | WindowInsets.Type.statusBars()
- | WindowInsets.Type.displayCutout());
-
- final Rect bounds = windowMetrics.getBounds();
- Configuration config = mContext.getResources().getConfiguration();
- mIsLargeScreen = config.smallestScreenWidthDp >= 600;
- if (mIsLargeScreen) {
- float largestEdgeDp = Math.max(config.screenWidthDp, config.screenHeightDp);
- mIsSmallTablet = largestEdgeDp < 960;
- } else {
- mIsSmallTablet = false;
- }
+ public void update(DeviceConfig deviceConfig) {
+ mDeviceConfig = deviceConfig;
if (BubbleDebugConfig.DEBUG_POSITIONER) {
Log.w(TAG, "update positioner:"
+ " rotation: " + mRotation
- + " insets: " + insets
- + " isLargeScreen: " + mIsLargeScreen
- + " isSmallTablet: " + mIsSmallTablet
+ + " insets: " + deviceConfig.getInsets()
+ + " isLargeScreen: " + deviceConfig.isLargeScreen()
+ + " isSmallTablet: " + deviceConfig.isSmallTablet()
+ " showingInBubbleBar: " + mShowingInBubbleBar
- + " bounds: " + bounds);
+ + " bounds: " + deviceConfig.getWindowBounds());
}
- updateInternal(mRotation, insets, bounds);
+ updateInternal(mRotation, deviceConfig.getInsets(), deviceConfig.getWindowBounds());
}
@VisibleForTesting
@@ -175,15 +150,15 @@
mExpandedViewLargeScreenWidth = isLandscape()
? (int) (bounds.width() * EXPANDED_VIEW_BUBBLE_BAR_LANDSCAPE_WIDTH_PERCENT)
: (int) (bounds.width() * EXPANDED_VIEW_BUBBLE_BAR_PORTRAIT_WIDTH_PERCENT);
- } else if (mIsSmallTablet) {
+ } else if (mDeviceConfig.isSmallTablet()) {
mExpandedViewLargeScreenWidth = (int) (bounds.width()
* EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT);
} else {
mExpandedViewLargeScreenWidth =
res.getDimensionPixelSize(R.dimen.bubble_expanded_view_largescreen_width);
}
- if (mIsLargeScreen) {
- if (mIsSmallTablet) {
+ if (mDeviceConfig.isLargeScreen()) {
+ if (mDeviceConfig.isSmallTablet()) {
final int centeredInset = (bounds.width() - mExpandedViewLargeScreenWidth) / 2;
mExpandedViewLargeScreenInsetClosestEdge = centeredInset;
mExpandedViewLargeScreenInsetFurthestEdge = centeredInset;
@@ -264,13 +239,12 @@
/** @return whether the device is in landscape orientation. */
public boolean isLandscape() {
- return mContext.getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
+ return mDeviceConfig.isLandscape();
}
/** @return whether the screen is considered large. */
public boolean isLargeScreen() {
- return mIsLargeScreen;
+ return mDeviceConfig.isLargeScreen();
}
/**
@@ -281,7 +255,7 @@
* to the left or right side.
*/
public boolean showBubblesVertically() {
- return isLandscape() || mIsLargeScreen;
+ return isLandscape() || mDeviceConfig.isLargeScreen();
}
/** Size of the bubble. */
@@ -334,7 +308,7 @@
}
private int getExpandedViewLargeScreenInsetFurthestEdge(boolean isOverflow) {
- if (isOverflow && mIsLargeScreen) {
+ if (isOverflow && mDeviceConfig.isLargeScreen()) {
return mScreenRect.width()
- mExpandedViewLargeScreenInsetClosestEdge
- mOverflowWidth;
@@ -358,7 +332,7 @@
final int pointerTotalHeight = getPointerSize();
final int expandedViewLargeScreenInsetFurthestEdge =
getExpandedViewLargeScreenInsetFurthestEdge(isOverflow);
- if (mIsLargeScreen) {
+ if (mDeviceConfig.isLargeScreen()) {
// Note:
// If we're in portrait OR if we're a small tablet, then the two insets values will
// be equal. If we're landscape and a large tablet, the two values will be different.
@@ -439,12 +413,12 @@
*/
public float getExpandedViewHeight(BubbleViewProvider bubble) {
boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey());
- if (isOverflow && showBubblesVertically() && !mIsLargeScreen) {
+ if (isOverflow && showBubblesVertically() && !mDeviceConfig.isLargeScreen()) {
// overflow in landscape on phone is max
return MAX_HEIGHT;
}
- if (mIsLargeScreen && !mIsSmallTablet && !isOverflow) {
+ if (mDeviceConfig.isLargeScreen() && !mDeviceConfig.isSmallTablet() && !isOverflow) {
// the expanded view height on large tablets is calculated based on the shortest screen
// size and is the same in both portrait and landscape
int maxVerticalInset = Math.max(mInsets.top, mInsets.bottom);
@@ -529,11 +503,9 @@
*/
public PointF getExpandedBubbleXY(int index, BubbleStackView.StackViewState state) {
boolean showBubblesVertically = showBubblesVertically();
- boolean isRtl = mContext.getResources().getConfiguration().getLayoutDirection()
- == LAYOUT_DIRECTION_RTL;
int onScreenIndex;
- if (showBubblesVertically || !isRtl) {
+ if (showBubblesVertically || !mDeviceConfig.isRtl()) {
onScreenIndex = index;
} else {
// If bubbles are shown horizontally, check if RTL language is used.
@@ -554,10 +526,10 @@
if (showBubblesVertically) {
int inset = mExpandedViewLargeScreenInsetClosestEdge;
y = rowStart + positionInRow;
- int left = mIsLargeScreen
+ int left = mDeviceConfig.isLargeScreen()
? inset - mExpandedViewPadding - mBubbleSize
: mPositionRect.left;
- int right = mIsLargeScreen
+ int right = mDeviceConfig.isLargeScreen()
? mPositionRect.right - inset + mExpandedViewPadding
: mPositionRect.right - mBubbleSize;
x = state.onLeft
@@ -693,13 +665,10 @@
* @param isAppBubble whether this start position is for an app bubble or not.
*/
public PointF getDefaultStartPosition(boolean isAppBubble) {
- final int layoutDirection = mContext.getResources().getConfiguration().getLayoutDirection();
// Normal bubbles start on the left if we're in LTR, right otherwise.
// TODO (b/294284894): update language around "app bubble" here
// App bubbles start on the right in RTL, left otherwise.
- final boolean startOnLeft = isAppBubble
- ? layoutDirection == LAYOUT_DIRECTION_RTL
- : layoutDirection != LAYOUT_DIRECTION_RTL;
+ final boolean startOnLeft = isAppBubble ? mDeviceConfig.isRtl() : !mDeviceConfig.isRtl();
return getStartPosition(startOnLeft ? StackPinnedEdge.LEFT : StackPinnedEdge.RIGHT);
}
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 2cee675..45c948b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -61,6 +61,7 @@
import android.view.ViewOutlineProvider;
import android.view.ViewPropertyAnimator;
import android.view.ViewTreeObserver;
+import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -536,8 +537,8 @@
return;
}
- final boolean clickedBubbleIsCurrentlyExpandedBubble =
- clickedBubble.getKey().equals(mExpandedBubble.getKey());
+ final boolean clickedBubbleIsCurrentlyExpandedBubble = mExpandedBubble != null
+ && clickedBubble.getKey().equals(mExpandedBubble.getKey());
if (isExpanded()) {
mExpandedAnimationController.onGestureFinished();
@@ -1001,7 +1002,8 @@
mOrientationChangedListener =
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
- mPositioner.update();
+ mPositioner.update(DeviceConfig.create(mContext, mContext.getSystemService(
+ WindowManager.class)));
onDisplaySizeChanged();
mExpandedAnimationController.updateResources();
mStackAnimationController.updateResources();
@@ -1522,7 +1524,8 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mPositioner.update();
+ WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+ mPositioner.update(DeviceConfig.create(mContext, Objects.requireNonNull(windowManager)));
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
getViewTreeObserver().addOnDrawListener(mSystemGestureExcludeUpdater);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DeviceConfig.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DeviceConfig.kt
new file mode 100644
index 0000000..9293309
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DeviceConfig.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles
+
+import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.graphics.Insets
+import android.graphics.Rect
+import android.view.View.LAYOUT_DIRECTION_RTL
+import android.view.WindowInsets
+import android.view.WindowManager
+import kotlin.math.max
+
+/** Contains device configuration used for positioning bubbles on the screen. */
+data class DeviceConfig(
+ val isLargeScreen: Boolean,
+ val isSmallTablet: Boolean,
+ val isLandscape: Boolean,
+ val isRtl: Boolean,
+ val windowBounds: Rect,
+ val insets: Insets
+) {
+ companion object {
+
+ private const val LARGE_SCREEN_MIN_EDGE_DP = 600
+ private const val SMALL_TABLET_MAX_EDGE_DP = 960
+
+ @JvmStatic
+ fun create(context: Context, windowManager: WindowManager): DeviceConfig {
+ val windowMetrics = windowManager.currentWindowMetrics
+ val metricInsets = windowMetrics.windowInsets
+ val insets = metricInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()
+ or WindowInsets.Type.statusBars()
+ or WindowInsets.Type.displayCutout())
+ val windowBounds = windowMetrics.bounds
+ val config: Configuration = context.resources.configuration
+ val isLargeScreen = config.smallestScreenWidthDp >= LARGE_SCREEN_MIN_EDGE_DP
+ val largestEdgeDp = max(config.screenWidthDp, config.screenHeightDp)
+ val isSmallTablet = isLargeScreen && largestEdgeDp < SMALL_TABLET_MAX_EDGE_DP
+ val isLandscape = context.resources.configuration.orientation == ORIENTATION_LANDSCAPE
+ val isRtl = context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
+ return DeviceConfig(
+ isLargeScreen = isLargeScreen,
+ isSmallTablet = isSmallTablet,
+ isLandscape = isLandscape,
+ isRtl = isRtl,
+ windowBounds = windowBounds,
+ insets = insets
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index e788341..92cb436 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -28,13 +28,16 @@
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewTreeObserver;
+import android.view.WindowManager;
import android.widget.FrameLayout;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleViewProvider;
+import com.android.wm.shell.bubbles.DeviceConfig;
+import java.util.Objects;
import java.util.function.Consumer;
import kotlin.Unit;
@@ -104,7 +107,8 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mPositioner.update();
+ WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+ mPositioner.update(DeviceConfig.create(mContext, Objects.requireNonNull(windowManager)));
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 4a9ea6f..144555d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -21,7 +21,6 @@
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
-import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.app.WindowConfiguration.WindowingMode
import android.content.Context
@@ -321,24 +320,10 @@
}
/** Move a task with given `taskId` to fullscreen */
- fun moveToFullscreen(taskId: Int) {
- shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> moveToFullscreen(task) }
- }
-
- /** Move a task to fullscreen */
- fun moveToFullscreen(task: RunningTaskInfo) {
- KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: moveToFullscreen taskId=%d",
- task.taskId
- )
-
- val wct = WindowContainerTransaction()
- addMoveToFullscreenChanges(wct, task)
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
- } else {
- shellTaskOrganizer.applyTransaction(wct)
+ fun moveToFullscreen(taskId: Int, windowDecor: DesktopModeWindowDecoration) {
+ shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task ->
+ windowDecor.incrementRelayoutBlock()
+ moveToFullscreenWithAnimation(task, task.positionInParent)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index ef8393c..35a1fa0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -151,7 +151,14 @@
@Override
public void setResizeBgColor(SurfaceControl.Transaction t, int bgColor) {
- runOnViewThread(() -> setResizeBackgroundColor(t, bgColor));
+ if (mHandler.getLooper().isCurrentThread()) {
+ // We can only use the transaction if it can updated synchronously, otherwise the tx
+ // will be applied immediately after but also used/updated on the view thread which
+ // will lead to a race and/or crash
+ runOnViewThread(() -> setResizeBackgroundColor(t, bgColor));
+ } else {
+ runOnViewThread(() -> setResizeBackgroundColor(bgColor));
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index dd6ca8d..03006f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -428,7 +428,8 @@
if (isTaskInSplitScreen(mTaskId)) {
mSplitScreenController.moveTaskToFullscreen(mTaskId);
} else {
- mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId));
+ mDesktopTasksController.ifPresent(c ->
+ c.moveToFullscreen(mTaskId, mWindowDecorByTaskId.get(mTaskId)));
}
} else if (id == R.id.split_screen_button) {
decoration.closeHandleMenu();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 8cbcde3..53ec201 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -49,7 +49,6 @@
import android.view.ViewConfiguration;
import android.view.WindowManagerGlobal;
-import com.android.internal.view.BaseIWindow;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
@@ -70,7 +69,9 @@
private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
private final int mDisplayId;
- private final BaseIWindow mFakeWindow;
+
+ private final IBinder mClientToken;
+
private final IBinder mFocusGrantToken;
private final SurfaceControl mDecorationSurface;
private final InputChannel mInputChannel;
@@ -78,7 +79,7 @@
private final DragPositioningCallback mCallback;
private final SurfaceControl mInputSinkSurface;
- private final BaseIWindow mFakeSinkWindow;
+ private final IBinder mSinkClientToken;
private final InputChannel mSinkInputChannel;
private final DisplayController mDisplayController;
@@ -116,17 +117,14 @@
mTaskCornerRadius = taskCornerRadius;
mDecorationSurface = decorationSurface;
mDisplayController = displayController;
- // Use a fake window as the backing surface is a container layer, and we don't want to
- // create a buffer layer for it, so we can't use ViewRootImpl.
- mFakeWindow = new BaseIWindow();
- mFakeWindow.setSession(mWindowSession);
+ mClientToken = new Binder();
mFocusGrantToken = new Binder();
mInputChannel = new InputChannel();
try {
mWindowSession.grantInputChannel(
mDisplayId,
mDecorationSurface,
- mFakeWindow.asBinder(),
+ mClientToken,
null /* hostInputToken */,
FLAG_NOT_FOCUSABLE,
PRIVATE_FLAG_TRUSTED_OVERLAY,
@@ -155,13 +153,13 @@
.setLayer(mInputSinkSurface, WindowDecoration.INPUT_SINK_Z_ORDER)
.show(mInputSinkSurface)
.apply();
- mFakeSinkWindow = new BaseIWindow();
+ mSinkClientToken = new Binder();
mSinkInputChannel = new InputChannel();
try {
mWindowSession.grantInputChannel(
mDisplayId,
mInputSinkSurface,
- mFakeSinkWindow.asBinder(),
+ mSinkClientToken,
null /* hostInputToken */,
FLAG_NOT_FOCUSABLE,
0 /* privateFlags */,
@@ -324,14 +322,14 @@
mInputEventReceiver.dispose();
mInputChannel.dispose();
try {
- mWindowSession.remove(mFakeWindow.asBinder());
+ mWindowSession.remove(mClientToken);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
mSinkInputChannel.dispose();
try {
- mWindowSession.remove(mFakeSinkWindow.asBinder());
+ mWindowSession.remove(mSinkClientToken);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
index 744e8c2..181474f 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
@@ -57,25 +57,18 @@
resetLetterboxStyle()
_letterboxStyle = mapLetterboxStyle()
val isLetterboxEducationEnabled = _letterboxStyle.getValue("Is education enabled")
- var hasLetterboxEducationStateChanged = false
if ("$withLetterboxEducationEnabled" != isLetterboxEducationEnabled) {
- hasLetterboxEducationStateChanged = true
execAdb("wm set-letterbox-style --isEducationEnabled " + withLetterboxEducationEnabled)
}
- return try {
- object : Statement() {
- @Throws(Throwable::class)
- override fun evaluate() {
+ return object : Statement() {
+ @Throws(Throwable::class)
+ override fun evaluate() {
+ try {
base!!.evaluate()
+ } finally {
+ resetLetterboxStyle()
}
}
- } finally {
- if (hasLetterboxEducationStateChanged) {
- execAdb(
- "wm set-letterbox-style --isEducationEnabled " + isLetterboxEducationEnabled
- )
- }
- resetLetterboxStyle()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
index 89ecc29..fafd37b 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
@@ -45,12 +45,15 @@
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="run-command" value="settings put global package_verifier_user_consent -1"/>
<option name="teardown-command"
value="settings delete secure show_ime_with_hard_keyboard"/>
<option name="teardown-command" value="settings delete system show_touches"/>
<option name="teardown-command" value="settings delete system pointer_location"/>
<option name="teardown-command"
value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ <option name="teardown-command"
+ value="settings put global package_verifier_user_consent 1"/>
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
@@ -76,6 +79,19 @@
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"
+ value="am start -n com.netflix.mediaclient/com.netflix.mediaclient.ui.login.LoginActivity"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="set-option" value="package-name:com.netflix.mediaclient"/>
+ <option name="set-option" value="ui-automator-mode:true"/>
+ <option name="class" value="com.android.csuite.tests.AppCrawlTest" />
+ </test>
+
<!-- Needed for pushing the trace config file -->
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
index fdda597..05f937a 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
@@ -95,6 +95,8 @@
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto
index b55f4ec..67316d2 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto
@@ -63,6 +63,7 @@
atrace_categories: "sched_process_exit"
atrace_apps: "com.android.server.wm.flicker.testapp"
atrace_apps: "com.android.systemui"
+ atrace_apps: "com.android.wm.shell.flicker.service"
atrace_apps: "com.android.wm.shell.flicker.splitscreen"
atrace_apps: "com.google.android.apps.nexuslauncher"
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestHandler.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestHandler.java
new file mode 100644
index 0000000..b91d6f9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestHandler.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+
+/**
+ * Basic test handler that immediately executes anything that is posted on it.
+ */
+public class TestHandler extends Handler {
+ public TestHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ dispatchMessage(msg);
+ return true;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 26c7394..4bca96b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -192,7 +192,7 @@
mMainExecutor);
mPositioner = new TestableBubblePositioner(mContext,
- mock(WindowManager.class));
+ mContext.getSystemService(WindowManager.class));
mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner, mEducationController,
mMainExecutor);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
index cb29a21..f5b0174 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
@@ -53,7 +53,8 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mPositioner = new TestableBubblePositioner(mContext, mock(WindowManager.class));
+ mPositioner = new TestableBubblePositioner(mContext,
+ mContext.getSystemService(WindowManager.class));
when(mBubbleController.getPositioner()).thenReturn(mPositioner);
when(mBubbleController.getStackView()).thenReturn(mock(BubbleStackView.class));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
index 287a97c..835ebe2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
@@ -16,33 +16,17 @@
package com.android.wm.shell.bubbles;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.View.LAYOUT_DIRECTION_LTR;
-import static android.view.View.LAYOUT_DIRECTION_RTL;
-
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
import android.content.Intent;
-import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableResources;
-import android.util.DisplayMetrics;
-import android.view.WindowInsets;
import android.view.WindowManager;
-import android.view.WindowMetrics;
import androidx.test.filters.SmallTest;
@@ -52,36 +36,20 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
/**
* Tests operations and the resulting state managed by {@link BubblePositioner}.
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class BubblePositionerTest extends ShellTestCase {
- private static final int MIN_WIDTH_FOR_TABLET = 600;
-
private BubblePositioner mPositioner;
- private Configuration mConfiguration;
-
- @Mock
- private WindowManager mWindowManager;
- @Mock
- private WindowMetrics mWindowMetrics;
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mConfiguration = spy(new Configuration());
- TestableResources testableResources = mContext.getOrCreateTestableResources();
- testableResources.overrideConfiguration(mConfiguration);
-
- mPositioner = new BubblePositioner(mContext, mWindowManager);
+ WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+ mPositioner = new BubblePositioner(mContext, windowManager);
}
@Test
@@ -91,11 +59,11 @@
Rect availableRect = new Rect(screenBounds);
availableRect.inset(insets);
- new WindowManagerConfig()
+ DeviceConfig deviceConfig = new ConfigBuilder()
.setInsets(insets)
.setScreenBounds(screenBounds)
- .setUpConfig();
- mPositioner.update();
+ .build();
+ mPositioner.update(deviceConfig);
assertThat(mPositioner.getAvailableRect()).isEqualTo(availableRect);
assertThat(mPositioner.isLandscape()).isFalse();
@@ -105,16 +73,16 @@
@Test
public void testShowBubblesVertically_phonePortrait() {
- new WindowManagerConfig().setOrientation(ORIENTATION_PORTRAIT).setUpConfig();
- mPositioner.update();
+ DeviceConfig deviceConfig = new ConfigBuilder().build();
+ mPositioner.update(deviceConfig);
assertThat(mPositioner.showBubblesVertically()).isFalse();
}
@Test
public void testShowBubblesVertically_phoneLandscape() {
- new WindowManagerConfig().setOrientation(ORIENTATION_LANDSCAPE).setUpConfig();
- mPositioner.update();
+ DeviceConfig deviceConfig = new ConfigBuilder().setLandscape().build();
+ mPositioner.update(deviceConfig);
assertThat(mPositioner.isLandscape()).isTrue();
assertThat(mPositioner.showBubblesVertically()).isTrue();
@@ -122,8 +90,8 @@
@Test
public void testShowBubblesVertically_tablet() {
- new WindowManagerConfig().setLargeScreen().setUpConfig();
- mPositioner.update();
+ DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build();
+ mPositioner.update(deviceConfig);
assertThat(mPositioner.showBubblesVertically()).isTrue();
}
@@ -131,8 +99,8 @@
/** If a resting position hasn't been set, calling it will return the default position. */
@Test
public void testGetRestingPosition_returnsDefaultPosition() {
- new WindowManagerConfig().setUpConfig();
- mPositioner.update();
+ DeviceConfig deviceConfig = new ConfigBuilder().build();
+ mPositioner.update(deviceConfig);
PointF restingPosition = mPositioner.getRestingPosition();
PointF defaultPosition = mPositioner.getDefaultStartPosition();
@@ -143,8 +111,8 @@
/** If a resting position has been set, it'll return that instead of the default position. */
@Test
public void testGetRestingPosition_returnsRestingPosition() {
- new WindowManagerConfig().setUpConfig();
- mPositioner.update();
+ DeviceConfig deviceConfig = new ConfigBuilder().build();
+ mPositioner.update(deviceConfig);
PointF restingPosition = new PointF(100, 100);
mPositioner.setRestingPosition(restingPosition);
@@ -155,8 +123,8 @@
/** Test that the default resting position on phone is in upper left. */
@Test
public void testGetRestingPosition_bubble_onPhone() {
- new WindowManagerConfig().setUpConfig();
- mPositioner.update();
+ DeviceConfig deviceConfig = new ConfigBuilder().build();
+ mPositioner.update(deviceConfig);
RectF allowableStackRegion =
mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
@@ -168,8 +136,8 @@
@Test
public void testGetRestingPosition_bubble_onPhone_RTL() {
- new WindowManagerConfig().setLayoutDirection(LAYOUT_DIRECTION_RTL).setUpConfig();
- mPositioner.update();
+ DeviceConfig deviceConfig = new ConfigBuilder().setRtl().build();
+ mPositioner.update(deviceConfig);
RectF allowableStackRegion =
mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
@@ -182,8 +150,8 @@
/** Test that the default resting position on tablet is middle left. */
@Test
public void testGetRestingPosition_chatBubble_onTablet() {
- new WindowManagerConfig().setLargeScreen().setUpConfig();
- mPositioner.update();
+ DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build();
+ mPositioner.update(deviceConfig);
RectF allowableStackRegion =
mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
@@ -195,9 +163,8 @@
@Test
public void testGetRestingPosition_chatBubble_onTablet_RTL() {
- new WindowManagerConfig().setLargeScreen().setLayoutDirection(
- LAYOUT_DIRECTION_RTL).setUpConfig();
- mPositioner.update();
+ DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build();
+ mPositioner.update(deviceConfig);
RectF allowableStackRegion =
mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
@@ -210,8 +177,8 @@
/** Test that the default resting position on tablet is middle right. */
@Test
public void testGetDefaultPosition_appBubble_onTablet() {
- new WindowManagerConfig().setLargeScreen().setUpConfig();
- mPositioner.update();
+ DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build();
+ mPositioner.update(deviceConfig);
RectF allowableStackRegion =
mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
@@ -223,9 +190,8 @@
@Test
public void testGetRestingPosition_appBubble_onTablet_RTL() {
- new WindowManagerConfig().setLargeScreen().setLayoutDirection(
- LAYOUT_DIRECTION_RTL).setUpConfig();
- mPositioner.update();
+ DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build();
+ mPositioner.update(deviceConfig);
RectF allowableStackRegion =
mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
@@ -237,9 +203,8 @@
@Test
public void testHasUserModifiedDefaultPosition_false() {
- new WindowManagerConfig().setLargeScreen().setLayoutDirection(
- LAYOUT_DIRECTION_RTL).setUpConfig();
- mPositioner.update();
+ DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build();
+ mPositioner.update(deviceConfig);
assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
@@ -250,9 +215,8 @@
@Test
public void testHasUserModifiedDefaultPosition_true() {
- new WindowManagerConfig().setLargeScreen().setLayoutDirection(
- LAYOUT_DIRECTION_RTL).setUpConfig();
- mPositioner.update();
+ DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build();
+ mPositioner.update(deviceConfig);
assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
@@ -266,12 +230,12 @@
Insets insets = Insets.of(10, 20, 5, 15);
Rect screenBounds = new Rect(0, 0, 1800, 2600);
- new WindowManagerConfig()
+ DeviceConfig deviceConfig = new ConfigBuilder()
.setLargeScreen()
.setInsets(insets)
.setScreenBounds(screenBounds)
- .setUpConfig();
- mPositioner.update();
+ .build();
+ mPositioner.update(deviceConfig);
Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName());
Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor());
@@ -311,58 +275,47 @@
* Sets up window manager to return config values based on what you need for the test.
* By default it sets up a portrait phone without any insets.
*/
- private class WindowManagerConfig {
+ private static class ConfigBuilder {
private Rect mScreenBounds = new Rect(0, 0, 1000, 2000);
private boolean mIsLargeScreen = false;
- private int mOrientation = ORIENTATION_PORTRAIT;
- private int mLayoutDirection = LAYOUT_DIRECTION_LTR;
+ private boolean mIsSmallTablet = false;
+ private boolean mIsLandscape = false;
+ private boolean mIsRtl = false;
private Insets mInsets = Insets.of(0, 0, 0, 0);
- public WindowManagerConfig setScreenBounds(Rect screenBounds) {
+ public ConfigBuilder setScreenBounds(Rect screenBounds) {
mScreenBounds = screenBounds;
return this;
}
- public WindowManagerConfig setLargeScreen() {
+ public ConfigBuilder setLargeScreen() {
mIsLargeScreen = true;
return this;
}
- public WindowManagerConfig setOrientation(int orientation) {
- mOrientation = orientation;
+ public ConfigBuilder setSmallTablet() {
+ mIsSmallTablet = true;
return this;
}
- public WindowManagerConfig setLayoutDirection(int layoutDirection) {
- mLayoutDirection = layoutDirection;
+ public ConfigBuilder setLandscape() {
+ mIsLandscape = true;
return this;
}
- public WindowManagerConfig setInsets(Insets insets) {
+ public ConfigBuilder setRtl() {
+ mIsRtl = true;
+ return this;
+ }
+
+ public ConfigBuilder setInsets(Insets insets) {
mInsets = insets;
return this;
}
- public void setUpConfig() {
- mConfiguration.smallestScreenWidthDp = mIsLargeScreen
- ? MIN_WIDTH_FOR_TABLET
- : MIN_WIDTH_FOR_TABLET - 1;
- mConfiguration.orientation = mOrientation;
- mConfiguration.screenWidthDp = pxToDp(mScreenBounds.width());
- mConfiguration.screenHeightDp = pxToDp(mScreenBounds.height());
-
- when(mConfiguration.getLayoutDirection()).thenReturn(mLayoutDirection);
- WindowInsets windowInsets = mock(WindowInsets.class);
- when(windowInsets.getInsetsIgnoringVisibility(anyInt())).thenReturn(mInsets);
- when(mWindowMetrics.getWindowInsets()).thenReturn(windowInsets);
- when(mWindowMetrics.getBounds()).thenReturn(mScreenBounds);
- when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
- }
-
- private int pxToDp(float px) {
- int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
- float dp = px / ((float) dpi / DisplayMetrics.DENSITY_DEFAULT);
- return (int) dp;
+ private DeviceConfig build() {
+ return new DeviceConfig(mIsLargeScreen, mIsSmallTablet, mIsLandscape, mIsRtl,
+ mScreenBounds, mInsets);
}
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
index 44ff354..c4b9c9b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
@@ -55,8 +55,6 @@
private BubblesNavBarMotionEventHandler mMotionEventHandler;
@Mock
- private WindowManager mWindowManager;
- @Mock
private Runnable mInterceptTouchRunnable;
@Mock
private MotionEventListener mMotionEventListener;
@@ -66,7 +64,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
TestableBubblePositioner positioner = new TestableBubblePositioner(getContext(),
- mWindowManager);
+ getContext().getSystemService(WindowManager.class));
mMotionEventHandler = new BubblesNavBarMotionEventHandler(getContext(), positioner,
mInterceptTouchRunnable, mMotionEventListener);
mMotionEventTime = SystemClock.uptimeMillis();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
index 335222e..6403e79 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -66,7 +66,8 @@
public void setUp() throws Exception {
super.setUp();
- mPositioner = new BubblePositioner(getContext(), mock(WindowManager.class));
+ mPositioner = new BubblePositioner(getContext(),
+ getContext().getSystemService(WindowManager.class));
mPositioner.updateInternal(Configuration.ORIENTATION_PORTRAIT,
Insets.of(0, 0, 0, 0),
new Rect(0, 0, mDisplayWidth, mDisplayHeight));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerTest.java
index 991913a..f660987 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerTest.java
@@ -50,9 +50,6 @@
private ExpandedViewAnimationController mController;
@Mock
- private WindowManager mWindowManager;
-
- @Mock
private BubbleExpandedView mMockExpandedView;
@Before
@@ -60,7 +57,7 @@
MockitoAnnotations.initMocks(this);
TestableBubblePositioner positioner = new TestableBubblePositioner(getContext(),
- mWindowManager);
+ getContext().getSystemService(WindowManager.class));
mController = new ExpandedViewAnimationControllerImpl(getContext(), positioner);
mController.setExpandedView(mMockExpandedView);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java
index 31fafca..0c22908 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java
@@ -313,7 +313,8 @@
bubbleCountSupplier,
onBubbleAnimatedOutAction,
onStackAnimationFinished,
- new TestableBubblePositioner(mContext, mock(WindowManager.class)));
+ new TestableBubblePositioner(mContext,
+ mContext.getSystemService(WindowManager.class)));
}
@Override
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index fde6acb..94c862b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -63,6 +63,7 @@
import com.android.wm.shell.transition.OneShotRemoteHandler
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS
+import com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_DESKTOP_MODE
import com.android.wm.shell.transition.Transitions.TransitionHandler
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
import com.google.common.truth.Truth.assertThat
@@ -392,8 +393,8 @@
fun moveToFullscreen_displayFullscreen_windowingModeSetToUndefined() {
val task = setUpFreeformTask()
task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
- controller.moveToFullscreen(task)
- val wct = getLatestWct(type = TRANSIT_CHANGE)
+ controller.moveToFullscreen(task.taskId, desktopModeWindowDecoration)
+ val wct = getLatestExitDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_UNDEFINED)
}
@@ -402,15 +403,15 @@
fun moveToFullscreen_displayFreeform_windowingModeSetToFullscreen() {
val task = setUpFreeformTask()
task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM
- controller.moveToFullscreen(task)
- val wct = getLatestWct(type = TRANSIT_CHANGE)
+ controller.moveToFullscreen(task.taskId, desktopModeWindowDecoration)
+ val wct = getLatestExitDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_FULLSCREEN)
}
@Test
fun moveToFullscreen_nonExistentTask_doesNothing() {
- controller.moveToFullscreen(999)
+ controller.moveToFullscreen(999, desktopModeWindowDecoration)
verifyWCTNotExecuted()
}
@@ -419,9 +420,9 @@
val taskDefaultDisplay = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
val taskSecondDisplay = setUpFreeformTask(displayId = SECOND_DISPLAY)
- controller.moveToFullscreen(taskDefaultDisplay)
+ controller.moveToFullscreen(taskDefaultDisplay.taskId, desktopModeWindowDecoration)
- with(getLatestWct(type = TRANSIT_CHANGE)) {
+ with(getLatestExitDesktopWct()) {
assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder())
assertThat(changes.keys).doesNotContain(taskSecondDisplay.token.asBinder())
}
@@ -808,6 +809,17 @@
return arg.value
}
+ private fun getLatestExitDesktopWct(): WindowContainerTransaction {
+ val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ if (ENABLE_SHELL_TRANSITIONS) {
+ verify(exitDesktopTransitionHandler)
+ .startTransition(eq(TRANSIT_EXIT_DESKTOP_MODE), arg.capture(), any(), any())
+ } else {
+ verify(shellTaskOrganizer).applyTransaction(arg.capture())
+ }
+ return arg.value
+ }
+
private fun verifyWCTNotExecuted() {
if (ENABLE_SHELL_TRANSITIONS) {
verify(transitions, never()).startTransition(anyInt(), any(), isNull())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index 4afb29e..d7c4610 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -40,6 +40,7 @@
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
@@ -58,6 +59,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestHandler;
import com.android.wm.shell.common.HandlerExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SyncTransactionQueue.TransactionRunnable;
@@ -92,8 +94,7 @@
Transitions mTransitions;
@Mock
Looper mViewLooper;
- @Mock
- Handler mViewHandler;
+ TestHandler mViewHandler;
SurfaceSession mSession;
SurfaceControl mLeash;
@@ -112,7 +113,7 @@
mContext = getContext();
doReturn(true).when(mViewLooper).isCurrentThread();
- doReturn(mViewLooper).when(mViewHandler).getLooper();
+ mViewHandler = spy(new TestHandler(mViewLooper));
mTaskInfo = new ActivityManager.RunningTaskInfo();
mTaskInfo.token = mToken;
@@ -668,4 +669,24 @@
mTaskViewTaskController.onTaskInfoChanged(mTaskInfo);
verify(mViewHandler).post(any());
}
+
+ @Test
+ public void testSetResizeBgOnSameUiThread_expectUsesTransaction() {
+ SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class);
+ mTaskView = spy(mTaskView);
+ mTaskView.setResizeBgColor(tx, Color.BLUE);
+ verify(mViewHandler, never()).post(any());
+ verify(mTaskView, never()).setResizeBackgroundColor(eq(Color.BLUE));
+ verify(mTaskView).setResizeBackgroundColor(eq(tx), eq(Color.BLUE));
+ }
+
+ @Test
+ public void testSetResizeBgOnDifferentUiThread_expectDoesNotUseTransaction() {
+ doReturn(false).when(mViewLooper).isCurrentThread();
+ SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class);
+ mTaskView = spy(mTaskView);
+ mTaskView.setResizeBgColor(tx, Color.BLUE);
+ verify(mViewHandler).post(any());
+ verify(mTaskView).setResizeBackgroundColor(eq(Color.BLUE));
+ }
}
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index a1d6ab5..b1cf96d 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -113,13 +113,13 @@
}
public final class GnssMeasurementRequest implements android.os.Parcelable {
- method @NonNull public android.os.WorkSource getWorkSource();
+ method @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE) @NonNull public android.os.WorkSource getWorkSource();
method public boolean isCorrelationVectorOutputsEnabled();
}
public static final class GnssMeasurementRequest.Builder {
method @NonNull public android.location.GnssMeasurementRequest.Builder setCorrelationVectorOutputsEnabled(boolean);
- method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.GnssMeasurementRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
+ method @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE) @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.GnssMeasurementRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
}
public final class GnssReflectingPlane implements android.os.Parcelable {
diff --git a/location/java/android/location/GnssMeasurementRequest.java b/location/java/android/location/GnssMeasurementRequest.java
index 65af392..2f0835a 100644
--- a/location/java/android/location/GnssMeasurementRequest.java
+++ b/location/java/android/location/GnssMeasurementRequest.java
@@ -17,11 +17,13 @@
package android.location;
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.location.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.WorkSource;
@@ -121,6 +123,7 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE)
@SystemApi
public @NonNull WorkSource getWorkSource() {
return mWorkSource;
@@ -298,6 +301,7 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE)
@SystemApi
@RequiresPermission(Manifest.permission.UPDATE_DEVICE_STATS)
public @NonNull Builder setWorkSource(@Nullable WorkSource workSource) {
diff --git a/location/java/android/location/flags/gnss.aconfig b/location/java/android/location/flags/gnss.aconfig
index c471a27..b6055e8 100644
--- a/location/java/android/location/flags/gnss.aconfig
+++ b/location/java/android/location/flags/gnss.aconfig
@@ -5,4 +5,18 @@
namespace: "location"
description: "Flag for GNSS API for NavIC L1"
bug: "302199306"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "gnss_call_stop_before_set_position_mode"
+ namespace: "location"
+ description: "Flag for calling stop() before setPositionMode()"
+ bug: "306874828"
+}
+
+flag {
+ name: "gnss_api_measurement_request_work_source"
+ namespace: "location"
+ description: "Flag for GnssMeasurementRequest WorkSource API"
+ bug: "295235160"
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9ae6f8d..1e32349 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -19,6 +19,7 @@
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
import static android.content.Context.DEVICE_ID_DEFAULT;
+import static android.media.audio.Flags.autoPublicVolumeApiHardening;
import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;
import android.Manifest;
@@ -49,6 +50,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.media.AudioAttributes.AttributeSystemUsage;
import android.media.CallbackUtil.ListenerInfo;
import android.media.audiopolicy.AudioPolicy;
@@ -901,6 +903,7 @@
@UnsupportedAppUsage
public AudioManager(Context context) {
setContext(context);
+ initPlatform();
}
private Context getContext() {
@@ -914,6 +917,9 @@
}
private void setContext(Context context) {
+ if (context == null) {
+ return;
+ }
mOriginalContextDeviceId = context.getDeviceId();
mApplicationContext = context.getApplicationContext();
if (mApplicationContext != null) {
@@ -1063,8 +1069,17 @@
* @see #isVolumeFixed()
*/
public void adjustVolume(int direction, @PublicVolumeFlags int flags) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
- helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
+ if (applyAutoHardening()) {
+ final IAudioService service = getService();
+ try {
+ service.adjustVolume(direction, flags);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
+ helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
+ }
}
/**
@@ -1093,8 +1108,17 @@
*/
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType,
@PublicVolumeFlags int flags) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
- helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
+ if (applyAutoHardening()) {
+ final IAudioService service = getService();
+ try {
+ service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
+ helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
+ }
}
/** @hide */
@@ -9969,6 +9993,30 @@
}
}
+ //====================================================================
+ // Flag related utilities
+
+ private boolean mIsAutomotive = false;
+
+ private void initPlatform() {
+ try {
+ final Context context = getContext();
+ if (context != null) {
+ mIsAutomotive = context.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error querying system feature for AUTOMOTIVE", e);
+ }
+ }
+
+ private boolean applyAutoHardening() {
+ if (mIsAutomotive && autoPublicVolumeApiHardening()) {
+ return true;
+ }
+ return false;
+ }
+
//---------------------------------------------------------
// Inner classes
//--------------------
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 42400d1..d14775f 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -500,6 +500,10 @@
in String packageName, int uid, int pid, in UserHandle userHandle,
int targetSdkVersion);
+ oneway void adjustVolume(int direction, int flags);
+
+ oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags);
+
boolean isMusicActive(in boolean remotely);
int getDeviceMaskForStream(in int streamType);
diff --git a/media/java/android/media/LoudnessCodecDispatcher.java b/media/java/android/media/LoudnessCodecDispatcher.java
index be881b1..5237cae 100644
--- a/media/java/android/media/LoudnessCodecDispatcher.java
+++ b/media/java/android/media/LoudnessCodecDispatcher.java
@@ -27,9 +27,11 @@
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.Entry;
import java.util.Objects;
@@ -52,6 +54,9 @@
private final CallbackUtil.LazyListenerManager<OnLoudnessCodecUpdateListener>
mLoudnessListenerMgr = new CallbackUtil.LazyListenerManager<>();
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
private final HashMap<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator>
mConfiguratorListener = new HashMap<>();
@@ -66,7 +71,8 @@
@Override
public void dispatchLoudnessCodecParameterChange(int piid, PersistableBundle params) {
- mLoudnessListenerMgr.callListeners(listener ->
+ mLoudnessListenerMgr.callListeners(listener -> {
+ synchronized (mLock) {
mConfiguratorListener.computeIfPresent(listener, (l, lcConfig) -> {
// send the appropriate bundle for the user to update
if (lcConfig.getAssignedTrackPiid() == piid) {
@@ -95,9 +101,10 @@
}
}
}
-
return lcConfig;
- }));
+ });
+ }
+ });
}
private static Bundle filterLoudnessParams(Bundle bundle) {
@@ -130,21 +137,33 @@
mLoudnessListenerMgr.addListener(
executor, listener, "addLoudnessCodecListener",
() -> dispatcher);
- mConfiguratorListener.put(listener, configurator);
+ synchronized (mLock) {
+ mConfiguratorListener.put(listener, configurator);
+ }
}
void removeLoudnessCodecListener(@NonNull LoudnessCodecConfigurator configurator) {
Objects.requireNonNull(configurator);
- for (Entry<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator> e :
- mConfiguratorListener.entrySet()) {
- if (e.getValue() == configurator) {
- final OnLoudnessCodecUpdateListener listener = e.getKey();
- mConfiguratorListener.remove(listener);
- mLoudnessListenerMgr.removeListener(listener, "removeLoudnessCodecListener");
- break;
+ OnLoudnessCodecUpdateListener listenerToRemove = null;
+ synchronized (mLock) {
+ Iterator<Entry<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator>> iterator =
+ mConfiguratorListener.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Entry<OnLoudnessCodecUpdateListener, LoudnessCodecConfigurator> e =
+ iterator.next();
+ if (e.getValue() == configurator) {
+ final OnLoudnessCodecUpdateListener listener = e.getKey();
+ iterator.remove();
+ listenerToRemove = listener;
+ break;
+ }
}
}
+ if (listenerToRemove != null) {
+ mLoudnessListenerMgr.removeListener(listenerToRemove,
+ "removeLoudnessCodecListener");
+ }
}
}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 6031ef7..94fce79 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -122,11 +122,6 @@
"-Wunused",
"-Wunreachable-code",
],
-
- // Workaround Clang LTO crash.
- lto: {
- never: true,
- },
}
cc_library_shared {
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index 8b5b726..cf5059c 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -44,9 +44,4 @@
"-Wunreachable-code",
"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
-
- // Workaround Clang LTO crash.
- lto: {
- never: true,
- },
}
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
index fd785a4..b795560 100644
--- a/mime/java-res/android.mime.types
+++ b/mime/java-res/android.mime.types
@@ -55,6 +55,8 @@
?application/vnd.android.haptics.vibration+xml ahv
?application/vnd.android.ota ota
?application/vnd.apple.mpegurl m3u8
+?application/vnd.apple.pkpass pkpass
+?application/vnd.apple.pkpasses pkpasses
?application/vnd.ms-pki.stl stl
?application/vnd.ms-powerpoint pot
?application/vnd.ms-wpl wpl
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
index fe26dc3..991fe41 100644
--- a/packages/CredentialManager/Android.bp
+++ b/packages/CredentialManager/Android.bp
@@ -16,10 +16,12 @@
dex_preopt: {
profile_guided: true,
+ //TODO: b/312357299 - Update baseline profile
profile: "profile.txt.prof",
},
static_libs: [
+ "CredentialManagerShared",
"PlatformComposeCore",
"androidx.activity_activity-compose",
"androidx.appcompat_appcompat",
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
index 42f1207..325d3f8 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
@@ -16,6 +16,7 @@
package com.android.credentialmanager
+import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.credentials.ui.RequestInfo
@@ -27,10 +28,10 @@
import com.android.credentialmanager.model.Request
fun Intent.parse(
- packageManager: PackageManager,
+ context: Context,
): Request {
- return parseCancelUiRequest(packageManager)
- ?: parseRequestInfo()
+ return parseCancelUiRequest(context.packageManager)
+ ?: parseRequestInfo(context)
}
fun Intent.parseCancelUiRequest(packageManager: PackageManager): Request? =
@@ -51,11 +52,11 @@
}
}
-fun Intent.parseRequestInfo(): Request =
+fun Intent.parseRequestInfo(context: Context): Request =
requestInfo.let{ info ->
when (info?.type) {
RequestInfo.TYPE_CREATE -> Request.Create(info.token)
- RequestInfo.TYPE_GET -> toGet()
+ RequestInfo.TYPE_GET -> toGet(context)
else -> {
throw IllegalStateException("Unrecognized request type: ${info?.type}")
}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
index 83183b5..3ef65b0 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
@@ -16,8 +16,8 @@
package com.android.credentialmanager.client.impl
+import android.content.Context
import android.content.Intent
-import android.content.pm.PackageManager
import android.credentials.ui.BaseDialogResult
import android.credentials.ui.UserSelectionDialogResult
import android.os.Bundle
@@ -26,12 +26,13 @@
import com.android.credentialmanager.model.Request
import com.android.credentialmanager.parse
import com.android.credentialmanager.client.CredentialManagerClient
+import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
class CredentialManagerClientImpl @Inject constructor(
- private val packageManager: PackageManager,
+ @ApplicationContext private val context: Context,
) : CredentialManagerClient {
private val _requests = MutableStateFlow<Request?>(null)
@@ -40,7 +41,7 @@
override fun updateRequest(intent: Intent) {
val request = intent.parse(
- packageManager = packageManager,
+ context = context,
)
Log.d(TAG, "Request parsed: $request, client instance: $this")
if (request is Request.Cancel || request is Request.Close) {
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
new file mode 100644
index 0000000..f063074
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.ktx
+
+import android.app.slice.Slice
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.credentials.Credential
+import android.credentials.flags.Flags
+import android.credentials.ui.AuthenticationEntry
+import android.credentials.ui.Entry
+import android.credentials.ui.GetCredentialProviderData
+import android.graphics.drawable.Drawable
+import android.text.TextUtils
+import android.util.Log
+import androidx.activity.result.IntentSenderRequest
+import androidx.credentials.PublicKeyCredential
+import androidx.credentials.provider.Action
+import androidx.credentials.provider.AuthenticationAction
+import androidx.credentials.provider.CredentialEntry
+import androidx.credentials.provider.CustomCredentialEntry
+import androidx.credentials.provider.PasswordCredentialEntry
+import androidx.credentials.provider.PublicKeyCredentialEntry
+import androidx.credentials.provider.RemoteEntry
+import com.android.credentialmanager.IS_AUTO_SELECTED_KEY
+import com.android.credentialmanager.model.get.ActionEntryInfo
+import com.android.credentialmanager.model.get.AuthenticationEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.get.ProviderInfo
+import com.android.credentialmanager.model.get.RemoteEntryInfo
+import com.android.credentialmanager.TAG
+
+fun CredentialEntryInfo.getIntentSenderRequest(
+ isAutoSelected: Boolean = false
+): IntentSenderRequest? {
+ val entryIntent = fillInIntent?.putExtra(IS_AUTO_SELECTED_KEY, isAutoSelected)
+
+ return pendingIntent?.let{
+ IntentSenderRequest
+ .Builder(pendingIntent = it)
+ .setFillInIntent(entryIntent)
+ .build()
+ }
+}
+
+// Returns the list (potentially empty) of enabled provider.
+fun List<GetCredentialProviderData>.toProviderList(
+ context: Context,
+): List<ProviderInfo> {
+ val providerList: MutableList<ProviderInfo> = mutableListOf()
+ this.forEach {
+ val providerLabelAndIcon = getServiceLabelAndIcon(
+ context.packageManager,
+ it.providerFlattenedComponentName
+ ) ?: return@forEach
+ val (providerLabel, providerIcon) = providerLabelAndIcon
+ providerList.add(
+ ProviderInfo(
+ id = it.providerFlattenedComponentName,
+ icon = providerIcon,
+ displayName = providerLabel,
+ credentialEntryList = getCredentialOptionInfoList(
+ providerId = it.providerFlattenedComponentName,
+ providerLabel = providerLabel,
+ credentialEntries = it.credentialEntries,
+ context = context
+ ),
+ authenticationEntryList = getAuthenticationEntryList(
+ it.providerFlattenedComponentName,
+ providerLabel,
+ providerIcon,
+ it.authenticationEntries),
+ remoteEntry = getRemoteEntry(
+ it.providerFlattenedComponentName,
+ it.remoteEntry
+ ),
+ actionEntryList = getActionEntryList(
+ it.providerFlattenedComponentName, it.actionChips, providerIcon
+ ),
+ )
+ )
+ }
+ return providerList
+}
+
+/**
+ * Note: caller required handle empty list due to parsing error.
+ */
+private fun getCredentialOptionInfoList(
+ providerId: String,
+ providerLabel: String,
+ credentialEntries: List<Entry>,
+ context: Context,
+): List<CredentialEntryInfo> {
+ val result: MutableList<CredentialEntryInfo> = mutableListOf()
+ credentialEntries.forEach {
+ val credentialEntry = it.slice.credentialEntry
+ when (credentialEntry) {
+ is PasswordCredentialEntry -> {
+ result.add(
+ CredentialEntryInfo(
+ providerId = providerId,
+ providerDisplayName = providerLabel,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = credentialEntry.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ credentialType = CredentialType.PASSWORD,
+ credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
+ userName = credentialEntry.username.toString(),
+ displayName = credentialEntry.displayName?.toString(),
+ icon = credentialEntry.icon.loadDrawable(context),
+ shouldTintIcon = credentialEntry.isDefaultIcon,
+ lastUsedTimeMillis = credentialEntry.lastUsedTime,
+ isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+ credentialEntry.autoSelectAllowedFromOption,
+ )
+ )
+ }
+ is PublicKeyCredentialEntry -> {
+ result.add(
+ CredentialEntryInfo(
+ providerId = providerId,
+ providerDisplayName = providerLabel,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = credentialEntry.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ credentialType = CredentialType.PASSKEY,
+ credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
+ userName = credentialEntry.username.toString(),
+ displayName = credentialEntry.displayName?.toString(),
+ icon = credentialEntry.icon.loadDrawable(context),
+ shouldTintIcon = credentialEntry.isDefaultIcon,
+ lastUsedTimeMillis = credentialEntry.lastUsedTime,
+ isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+ credentialEntry.autoSelectAllowedFromOption,
+ )
+ )
+ }
+ is CustomCredentialEntry -> {
+ result.add(
+ CredentialEntryInfo(
+ providerId = providerId,
+ providerDisplayName = providerLabel,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = credentialEntry.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ credentialType = CredentialType.UNKNOWN,
+ credentialTypeDisplayName =
+ credentialEntry.typeDisplayName?.toString().orEmpty(),
+ userName = credentialEntry.title.toString(),
+ displayName = credentialEntry.subtitle?.toString(),
+ icon = credentialEntry.icon.loadDrawable(context),
+ shouldTintIcon = credentialEntry.isDefaultIcon,
+ lastUsedTimeMillis = credentialEntry.lastUsedTime,
+ isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+ credentialEntry.autoSelectAllowedFromOption,
+ )
+ )
+ }
+ else -> Log.d(
+ TAG,
+ "Encountered unrecognized credential entry ${it.slice.spec?.type}"
+ )
+ }
+ }
+ return result
+}
+val Slice.credentialEntry: CredentialEntry?
+ get() =
+ try {
+ when (spec?.type) {
+ Credential.TYPE_PASSWORD_CREDENTIAL -> PasswordCredentialEntry.fromSlice(this)!!
+ PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL ->
+ PublicKeyCredentialEntry.fromSlice(this)!!
+
+ else -> CustomCredentialEntry.fromSlice(this)!!
+ }
+ } catch (e: Exception) {
+ // Try CustomCredentialEntry.fromSlice one last time in case the cause was a failed
+ // password / passkey parsing attempt.
+ CustomCredentialEntry.fromSlice(this)
+ }
+
+
+/**
+ * Note: caller required handle empty list due to parsing error.
+ */
+private fun getAuthenticationEntryList(
+ providerId: String,
+ providerDisplayName: String,
+ providerIcon: Drawable,
+ authEntryList: List<AuthenticationEntry>,
+): List<AuthenticationEntryInfo> {
+ val result: MutableList<AuthenticationEntryInfo> = mutableListOf()
+ authEntryList.forEach { entry ->
+ val structuredAuthEntry =
+ AuthenticationAction.fromSlice(entry.slice) ?: return@forEach
+
+ val title: String =
+ structuredAuthEntry.title.toString().ifEmpty { providerDisplayName }
+
+ result.add(
+ AuthenticationEntryInfo(
+ providerId = providerId,
+ entryKey = entry.key,
+ entrySubkey = entry.subkey,
+ pendingIntent = structuredAuthEntry.pendingIntent,
+ fillInIntent = entry.frameworkExtrasIntent,
+ title = title,
+ providerDisplayName = providerDisplayName,
+ icon = providerIcon,
+ isUnlockedAndEmpty = entry.status != AuthenticationEntry.STATUS_LOCKED,
+ isLastUnlocked =
+ entry.status == AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT
+ )
+ )
+ }
+ return result
+}
+
+private fun getRemoteEntry(providerId: String, remoteEntry: Entry?): RemoteEntryInfo? {
+ if (remoteEntry == null) {
+ return null
+ }
+ val structuredRemoteEntry = RemoteEntry.fromSlice(remoteEntry.slice)
+ ?: return null
+ return RemoteEntryInfo(
+ providerId = providerId,
+ entryKey = remoteEntry.key,
+ entrySubkey = remoteEntry.subkey,
+ pendingIntent = structuredRemoteEntry.pendingIntent,
+ fillInIntent = remoteEntry.frameworkExtrasIntent,
+ )
+}
+
+/**
+ * Note: caller required handle empty list due to parsing error.
+ */
+private fun getActionEntryList(
+ providerId: String,
+ actionEntries: List<Entry>,
+ providerIcon: Drawable,
+): List<ActionEntryInfo> {
+ val result: MutableList<ActionEntryInfo> = mutableListOf()
+ actionEntries.forEach {
+ val actionEntryUi = Action.fromSlice(it.slice) ?: return@forEach
+ result.add(
+ ActionEntryInfo(
+ providerId = providerId,
+ entryKey = it.key,
+ entrySubkey = it.subkey,
+ pendingIntent = actionEntryUi.pendingIntent,
+ fillInIntent = it.frameworkExtrasIntent,
+ title = actionEntryUi.title.toString(),
+ icon = providerIcon,
+ subTitle = actionEntryUi.subtitle?.toString(),
+ )
+ )
+ }
+ return result
+}
+
+
+
+private fun getServiceLabelAndIcon(
+ pm: PackageManager,
+ providerFlattenedComponentName: String
+): Pair<String, Drawable>? {
+ var providerLabel: String? = null
+ var providerIcon: Drawable? = null
+ val component = ComponentName.unflattenFromString(providerFlattenedComponentName)
+ if (component == null) {
+ // Test data has only package name not component name.
+ // For test data usage only.
+ try {
+ val pkgInfo = if (Flags.instantAppsEnabled()) {
+ getPackageInfo(pm, providerFlattenedComponentName)
+ } else {
+ pm.getPackageInfo(
+ providerFlattenedComponentName,
+ PackageManager.PackageInfoFlags.of(0)
+ )
+ }
+ val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
+ providerLabel =
+ applicationInfo.loadSafeLabel(
+ pm, 0f,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ ).toString()
+ providerIcon = applicationInfo.loadIcon(pm)
+ } catch (e: Exception) {
+ Log.e(TAG, "Provider package info not found", e)
+ }
+ } else {
+ try {
+ val si = pm.getServiceInfo(component, PackageManager.ComponentInfoFlags.of(0))
+ providerLabel = si.loadSafeLabel(
+ pm, 0f,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ ).toString()
+ providerIcon = si.loadIcon(pm)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(TAG, "Provider service info not found", e)
+ // Added for mdoc use case where the provider may not need to register a service and
+ // instead only relies on the registration api.
+ try {
+ val pkgInfo = if (Flags.instantAppsEnabled()) {
+ getPackageInfo(pm, providerFlattenedComponentName)
+ } else {
+ pm.getPackageInfo(
+ component.packageName,
+ PackageManager.PackageInfoFlags.of(0)
+ )
+ }
+ val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
+ providerLabel =
+ applicationInfo.loadSafeLabel(
+ pm, 0f,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ ).toString()
+ providerIcon = applicationInfo.loadIcon(pm)
+ } catch (e: Exception) {
+ Log.e(TAG, "Provider package info not found", e)
+ }
+ }
+ }
+ return if (providerLabel == null || providerIcon == null) {
+ Log.d(
+ TAG,
+ "Failed to load provider label/icon for provider $providerFlattenedComponentName"
+ )
+ null
+ } else {
+ Pair(providerLabel, providerIcon)
+ }
+}
+
+private fun getPackageInfo(
+ pm: PackageManager,
+ packageName: String
+): PackageInfo {
+ val packageManagerFlags = PackageManager.MATCH_INSTANT
+
+ return pm.getPackageInfo(
+ packageName,
+ PackageManager.PackageInfoFlags.of(
+ (packageManagerFlags).toLong())
+ )
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.kt
deleted file mode 100644
index 3471070..0000000
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0N
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.ktx
-
-import androidx.activity.result.IntentSenderRequest
-import com.android.credentialmanager.IS_AUTO_SELECTED_KEY
-import com.android.credentialmanager.model.Password
-
-fun Password.getIntentSenderRequest(
- isAutoSelected: Boolean = false
-): IntentSenderRequest {
- val entryIntent = entry.frameworkExtrasIntent
- entryIntent?.putExtra(IS_AUTO_SELECTED_KEY, isAutoSelected)
-
- return IntentSenderRequest.Builder(
- pendingIntent = passwordCredentialEntry.pendingIntent
- ).setFillInIntent(entryIntent).build()
-}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
index d4bca2a..f1f1f7c 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
@@ -16,48 +16,18 @@
package com.android.credentialmanager.mapper
+import android.content.Context
import android.content.Intent
-import android.credentials.ui.Entry
-import androidx.credentials.provider.PasswordCredentialEntry
-import com.android.credentialmanager.factory.fromSlice
import com.android.credentialmanager.ktx.getCredentialProviderDataList
import com.android.credentialmanager.ktx.requestInfo
import com.android.credentialmanager.ktx.resultReceiver
-import com.android.credentialmanager.model.Password
+import com.android.credentialmanager.ktx.toProviderList
import com.android.credentialmanager.model.Request
-import com.google.common.collect.ImmutableList
-import com.google.common.collect.ImmutableMap
-fun Intent.toGet(): Request.Get {
- val credentialEntries = mutableListOf<Pair<String, Entry>>()
- for (providerData in getCredentialProviderDataList) {
- for (credentialEntry in providerData.credentialEntries) {
- credentialEntries.add(
- Pair(providerData.providerFlattenedComponentName, credentialEntry)
- )
- }
- }
-
- val passwordEntries = mutableListOf<Password>()
- for ((providerId, entry) in credentialEntries) {
- val slice = fromSlice(entry.slice)
- if (slice is PasswordCredentialEntry) {
- passwordEntries.add(
- Password(
- providerId = providerId,
- entry = entry,
- passwordCredentialEntry = slice
- )
- )
- }
- }
-
+fun Intent.toGet(context: Context): Request.Get {
return Request.Get(
token = requestInfo?.token,
- resultReceiver = this.resultReceiver,
- providers = ImmutableMap.copyOf(
- getCredentialProviderDataList.associateBy { it.providerFlattenedComponentName }
- ),
- passwordEntries = ImmutableList.copyOf(passwordEntries)
+ resultReceiver = resultReceiver,
+ providerInfos = getCredentialProviderDataList.toProviderList(context)
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/CredentialType.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/CredentialType.kt
similarity index 93%
rename from packages/CredentialManager/src/com/android/credentialmanager/common/CredentialType.kt
rename to packages/CredentialManager/shared/src/com/android/credentialmanager/model/CredentialType.kt
index cc92f60..3f85192 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/CredentialType.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/CredentialType.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.credentialmanager.model
enum class CredentialType {
UNKNOWN, PASSKEY, PASSWORD,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/EntryInfo.kt
similarity index 92%
rename from packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
rename to packages/CredentialManager/shared/src/com/android/credentialmanager/model/EntryInfo.kt
index ee36989..6d59f11 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/EntryInfo.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.credentialmanager.model
import android.app.PendingIntent
import android.content.Intent
-open class BaseEntry (
+open class EntryInfo (
val providerId: String,
val entryKey: String,
val entrySubkey: String,
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt
deleted file mode 100644
index 2fe4fd5..0000000
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0N
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.model
-
-import android.credentials.ui.Entry
-import androidx.credentials.provider.PasswordCredentialEntry
-
-data class Password(
- val providerId: String,
- val entry: Entry,
- val passwordCredentialEntry: PasswordCredentialEntry,
-)
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
index 2289ed7..7636462 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
@@ -16,11 +16,9 @@
package com.android.credentialmanager.model
-import android.credentials.ui.ProviderData
import android.os.IBinder
import android.os.ResultReceiver
-import com.google.common.collect.ImmutableList
-import com.google.common.collect.ImmutableMap
+import com.android.credentialmanager.model.get.ProviderInfo
/**
* Represents the request made by the CredentialManager API.
@@ -51,8 +49,7 @@
data class Get(
override val token: IBinder?,
val resultReceiver: ResultReceiver?,
- val providers: ImmutableMap<String, ProviderData>,
- val passwordEntries: ImmutableList<Password>,
+ val providerInfos: List<ProviderInfo>,
) : Request(token)
/**
* Request to start the create credentials flow.
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/CreateOptionInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/CreateOptionInfo.kt
new file mode 100644
index 0000000..d6189eb
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/CreateOptionInfo.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.model.creation
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.EntryInfo
+import java.time.Instant
+
+class CreateOptionInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+ val userProviderDisplayName: String,
+ val profileIcon: Drawable?,
+ val passwordCount: Int?,
+ val passkeyCount: Int?,
+ val totalCredentialCount: Int?,
+ val lastUsedTime: Instant,
+ val footerDescription: String?,
+ val allowAutoSelect: Boolean,
+) : EntryInfo(
+ providerId,
+ entryKey,
+ entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt
similarity index 61%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
copy to packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt
index ee36989..7ee50d7 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/creation/RemoteInfo.kt
@@ -14,16 +14,23 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.credentialmanager.model.creation
import android.app.PendingIntent
import android.content.Intent
+import com.android.credentialmanager.model.EntryInfo
-open class BaseEntry (
- val providerId: String,
- val entryKey: String,
- val entrySubkey: String,
- val pendingIntent: PendingIntent?,
- val fillInIntent: Intent?,
- val shouldTerminateUiUponSuccessfulProviderResult: Boolean,
+class RemoteInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+) : EntryInfo(
+ providerId,
+ entryKey,
+ entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
)
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ActionEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ActionEntryInfo.kt
new file mode 100644
index 0000000..d9eee86
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ActionEntryInfo.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.model.get
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.EntryInfo
+
+class ActionEntryInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+ val title: String,
+ val icon: Drawable,
+ val subTitle: String?,
+) : EntryInfo(
+ providerId,
+ entryKey,
+ entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/AuthenticationEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/AuthenticationEntryInfo.kt
new file mode 100644
index 0000000..01c394f
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/AuthenticationEntryInfo.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.model.get
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.EntryInfo
+
+class AuthenticationEntryInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+ val title: String,
+ val providerDisplayName: String,
+ val icon: Drawable,
+ // The entry had been unlocked and turned out to be empty. Used to determine whether to
+ // show "Tap to unlock" or "No sign-in info" for this entry.
+ val isUnlockedAndEmpty: Boolean,
+ // True if the entry was the last one unlocked. Used to show the no sign-in info snackbar.
+ val isLastUnlocked: Boolean,
+) : EntryInfo(
+ providerId,
+ entryKey, entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = false,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt
new file mode 100644
index 0000000..9725881
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/CredentialEntryInfo.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.model.get
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.EntryInfo
+import java.time.Instant
+
+class CredentialEntryInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+ /** Type of this credential used for sorting. Not localized so must not be directly displayed. */
+ val credentialType: CredentialType,
+ /** Localized type value of this credential used for display purpose. */
+ val credentialTypeDisplayName: String,
+ val providerDisplayName: String,
+ val userName: String,
+ val displayName: String?,
+ val icon: Drawable?,
+ val shouldTintIcon: Boolean,
+ val lastUsedTimeMillis: Instant?,
+ val isAutoSelectable: Boolean,
+) : EntryInfo(
+ providerId,
+ entryKey,
+ entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ProviderInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ProviderInfo.kt
new file mode 100644
index 0000000..6da4146
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/ProviderInfo.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.model.get
+
+import android.graphics.drawable.Drawable
+
+data class ProviderInfo(
+ /**
+ * Unique id (component name) of this provider.
+ * Not for display purpose - [displayName] should be used for ui rendering.
+ */
+ val id: String,
+ val icon: Drawable,
+ val displayName: String,
+ val credentialEntryList: List<CredentialEntryInfo>,
+ val authenticationEntryList: List<AuthenticationEntryInfo>,
+ val remoteEntry: RemoteEntryInfo?,
+ val actionEntryList: List<ActionEntryInfo>,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt
similarity index 61%
copy from packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
copy to packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt
index ee36989..a68bf74 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BaseEntry.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/get/RemoteEntryInfo.kt
@@ -14,16 +14,23 @@
* limitations under the License.
*/
-package com.android.credentialmanager.common
+package com.android.credentialmanager.model.get
import android.app.PendingIntent
import android.content.Intent
+import com.android.credentialmanager.model.EntryInfo
-open class BaseEntry (
- val providerId: String,
- val entryKey: String,
- val entrySubkey: String,
- val pendingIntent: PendingIntent?,
- val fillInIntent: Intent?,
- val shouldTerminateUiUponSuccessfulProviderResult: Boolean,
+class RemoteEntryInfo(
+ providerId: String,
+ entryKey: String,
+ entrySubkey: String,
+ pendingIntent: PendingIntent?,
+ fillInIntent: Intent?,
+) : EntryInfo(
+ providerId,
+ entryKey,
+ entrySubkey,
+ pendingIntent,
+ fillInIntent,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
)
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index bce86c4..6c5a984 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -28,7 +28,7 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
-import com.android.credentialmanager.common.BaseEntry
+import com.android.credentialmanager.model.EntryInfo
import com.android.credentialmanager.common.Constants
import com.android.credentialmanager.common.DialogState
import com.android.credentialmanager.common.ProviderActivityResult
@@ -47,7 +47,7 @@
data class UiState(
val createCredentialUiState: CreateCredentialUiState?,
val getCredentialUiState: GetCredentialUiState?,
- val selectedEntry: BaseEntry? = null,
+ val selectedEntry: EntryInfo? = null,
val providerActivityState: ProviderActivityState = ProviderActivityState.NOT_APPLICABLE,
val dialogState: DialogState = DialogState.ACTIVE,
// True if the UI has one and only one auto selectable entry. Its provider activity will be
@@ -115,12 +115,13 @@
launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
) {
val entry = uiState.selectedEntry
- if (entry != null && entry.pendingIntent != null) {
+ val pendingIntent = entry?.pendingIntent
+ if (pendingIntent != null) {
Log.d(Constants.LOG_TAG, "Launching provider activity")
uiState = uiState.copy(providerActivityState = ProviderActivityState.PENDING)
val entryIntent = entry.fillInIntent
entryIntent?.putExtra(Constants.IS_AUTO_SELECTED_KEY, uiState.isAutoSelectFlow)
- val intentSenderRequest = IntentSenderRequest.Builder(entry.pendingIntent)
+ val intentSenderRequest = IntentSenderRequest.Builder(pendingIntent)
.setFillInIntent(entryIntent).build()
try {
launcher.launch(intentSenderRequest)
@@ -201,7 +202,7 @@
/**************************************************************************/
/***** Get Flow Callbacks *****/
/**************************************************************************/
- fun getFlowOnEntrySelected(entry: BaseEntry) {
+ fun getFlowOnEntrySelected(entry: EntryInfo) {
Log.d(Constants.LOG_TAG, "credential selected: {provider=${entry.providerId}" +
", key=${entry.entryKey}, subkey=${entry.entrySubkey}}")
uiState = if (entry.pendingIntent != null) {
@@ -363,7 +364,7 @@
)
}
- fun createFlowOnEntrySelected(selectedEntry: BaseEntry) {
+ fun createFlowOnEntrySelected(selectedEntry: EntryInfo) {
val providerId = selectedEntry.providerId
val entryKey = selectedEntry.entryKey
val entrySubkey = selectedEntry.entrySubkey
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index f0fa6c5..fc3970d 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -16,13 +16,10 @@
package com.android.credentialmanager
-import android.app.slice.Slice
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
-import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
-import android.credentials.ui.AuthenticationEntry
import android.credentials.ui.CreateCredentialProviderData
import android.credentials.ui.DisabledProviderData
import android.credentials.ui.Entry
@@ -32,36 +29,26 @@
import android.text.TextUtils
import android.util.Log
import com.android.credentialmanager.common.Constants
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.CredentialType
import com.android.credentialmanager.createflow.ActiveEntry
import com.android.credentialmanager.createflow.CreateCredentialUiState
-import com.android.credentialmanager.createflow.CreateOptionInfo
+import com.android.credentialmanager.model.creation.CreateOptionInfo
import com.android.credentialmanager.createflow.CreateScreenState
import com.android.credentialmanager.createflow.DisabledProviderInfo
import com.android.credentialmanager.createflow.EnabledProviderInfo
-import com.android.credentialmanager.createflow.RemoteInfo
+import com.android.credentialmanager.model.creation.RemoteInfo
import com.android.credentialmanager.createflow.RequestDisplayInfo
-import com.android.credentialmanager.getflow.ActionEntryInfo
-import com.android.credentialmanager.getflow.AuthenticationEntryInfo
-import com.android.credentialmanager.getflow.CredentialEntryInfo
-import com.android.credentialmanager.getflow.ProviderInfo
-import com.android.credentialmanager.getflow.RemoteEntryInfo
-import com.android.credentialmanager.getflow.TopBrandingContent
+import com.android.credentialmanager.model.get.ProviderInfo
+import com.android.credentialmanager.ktx.toProviderList
import androidx.credentials.CreateCredentialRequest
import androidx.credentials.CreateCustomCredentialRequest
import androidx.credentials.CreatePasswordRequest
import androidx.credentials.CreatePublicKeyCredentialRequest
-import androidx.credentials.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
-import androidx.credentials.provider.Action
-import androidx.credentials.provider.AuthenticationAction
import androidx.credentials.provider.CreateEntry
-import androidx.credentials.provider.CredentialEntry
-import androidx.credentials.provider.CustomCredentialEntry
-import androidx.credentials.provider.PasswordCredentialEntry
-import androidx.credentials.provider.PublicKeyCredentialEntry
import androidx.credentials.provider.RemoteEntry
import org.json.JSONObject
import android.credentials.flags.Flags
+import com.android.credentialmanager.getflow.TopBrandingContent
import java.time.Instant
@@ -179,43 +166,7 @@
fun toProviderList(
providerDataList: List<GetCredentialProviderData>,
context: Context,
- ): List<ProviderInfo> {
- val providerList: MutableList<ProviderInfo> = mutableListOf()
- providerDataList.forEach {
- val providerLabelAndIcon = getServiceLabelAndIcon(
- context.packageManager,
- it.providerFlattenedComponentName
- ) ?: return@forEach
- val (providerLabel, providerIcon) = providerLabelAndIcon
- providerList.add(
- ProviderInfo(
- id = it.providerFlattenedComponentName,
- icon = providerIcon,
- displayName = providerLabel,
- credentialEntryList = getCredentialOptionInfoList(
- providerId = it.providerFlattenedComponentName,
- providerLabel = providerLabel,
- credentialEntries = it.credentialEntries,
- context = context
- ),
- authenticationEntryList = getAuthenticationEntryList(
- it.providerFlattenedComponentName,
- providerLabel,
- providerIcon,
- it.authenticationEntries),
- remoteEntry = getRemoteEntry(
- it.providerFlattenedComponentName,
- it.remoteEntry
- ),
- actionEntryList = getActionEntryList(
- it.providerFlattenedComponentName, it.actionChips, providerIcon
- ),
- )
- )
- }
- return providerList
- }
-
+ ): List<ProviderInfo> = providerDataList.toProviderList(context)
fun toRequestDisplayInfo(
requestInfo: RequestInfo?,
context: Context,
@@ -254,178 +205,6 @@
preferTopBrandingContent = preferTopBrandingContent,
)
}
-
-
- /**
- * Note: caller required handle empty list due to parsing error.
- */
- private fun getCredentialOptionInfoList(
- providerId: String,
- providerLabel: String,
- credentialEntries: List<Entry>,
- context: Context,
- ): List<CredentialEntryInfo> {
- val result: MutableList<CredentialEntryInfo> = mutableListOf()
- credentialEntries.forEach {
- val credentialEntry = parseCredentialEntryFromSlice(it.slice)
- when (credentialEntry) {
- is PasswordCredentialEntry -> {
- result.add(CredentialEntryInfo(
- providerId = providerId,
- providerDisplayName = providerLabel,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = credentialEntry.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- credentialType = CredentialType.PASSWORD,
- credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
- userName = credentialEntry.username.toString(),
- displayName = credentialEntry.displayName?.toString(),
- icon = credentialEntry.icon.loadDrawable(context),
- shouldTintIcon = credentialEntry.isDefaultIcon,
- lastUsedTimeMillis = credentialEntry.lastUsedTime,
- isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
- credentialEntry.autoSelectAllowedFromOption,
- ))
- }
- is PublicKeyCredentialEntry -> {
- result.add(CredentialEntryInfo(
- providerId = providerId,
- providerDisplayName = providerLabel,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = credentialEntry.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- credentialType = CredentialType.PASSKEY,
- credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
- userName = credentialEntry.username.toString(),
- displayName = credentialEntry.displayName?.toString(),
- icon = credentialEntry.icon.loadDrawable(context),
- shouldTintIcon = credentialEntry.isDefaultIcon,
- lastUsedTimeMillis = credentialEntry.lastUsedTime,
- isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
- credentialEntry.autoSelectAllowedFromOption,
- ))
- }
- is CustomCredentialEntry -> {
- result.add(CredentialEntryInfo(
- providerId = providerId,
- providerDisplayName = providerLabel,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = credentialEntry.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- credentialType = CredentialType.UNKNOWN,
- credentialTypeDisplayName =
- credentialEntry.typeDisplayName?.toString().orEmpty(),
- userName = credentialEntry.title.toString(),
- displayName = credentialEntry.subtitle?.toString(),
- icon = credentialEntry.icon.loadDrawable(context),
- shouldTintIcon = credentialEntry.isDefaultIcon,
- lastUsedTimeMillis = credentialEntry.lastUsedTime,
- isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
- credentialEntry.autoSelectAllowedFromOption,
- ))
- }
- else -> Log.d(
- Constants.LOG_TAG,
- "Encountered unrecognized credential entry ${it.slice.spec?.type}"
- )
- }
- }
- return result
- }
-
- /**
- * @hide
- */
- fun parseCredentialEntryFromSlice(slice: Slice): CredentialEntry? {
- try {
- when (slice.spec?.type) {
- TYPE_PASSWORD_CREDENTIAL -> return PasswordCredentialEntry.fromSlice(slice)!!
- TYPE_PUBLIC_KEY_CREDENTIAL -> return PublicKeyCredentialEntry.fromSlice(slice)!!
- else -> return CustomCredentialEntry.fromSlice(slice)!!
- }
- } catch (e: Exception) {
- // Try CustomCredentialEntry.fromSlice one last time in case the cause was a failed
- // password / passkey parsing attempt.
- return CustomCredentialEntry.fromSlice(slice)
- }
- }
-
- /**
- * Note: caller required handle empty list due to parsing error.
- */
- private fun getAuthenticationEntryList(
- providerId: String,
- providerDisplayName: String,
- providerIcon: Drawable,
- authEntryList: List<AuthenticationEntry>,
- ): List<AuthenticationEntryInfo> {
- val result: MutableList<AuthenticationEntryInfo> = mutableListOf()
- authEntryList.forEach { entry ->
- val structuredAuthEntry =
- AuthenticationAction.fromSlice(entry.slice) ?: return@forEach
-
- val title: String =
- structuredAuthEntry.title.toString().ifEmpty { providerDisplayName }
-
- result.add(AuthenticationEntryInfo(
- providerId = providerId,
- entryKey = entry.key,
- entrySubkey = entry.subkey,
- pendingIntent = structuredAuthEntry.pendingIntent,
- fillInIntent = entry.frameworkExtrasIntent,
- title = title,
- providerDisplayName = providerDisplayName,
- icon = providerIcon,
- isUnlockedAndEmpty = entry.status != AuthenticationEntry.STATUS_LOCKED,
- isLastUnlocked =
- entry.status == AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT
- ))
- }
- return result
- }
-
- private fun getRemoteEntry(providerId: String, remoteEntry: Entry?): RemoteEntryInfo? {
- if (remoteEntry == null) {
- return null
- }
- val structuredRemoteEntry = RemoteEntry.fromSlice(remoteEntry.slice)
- ?: return null
- return RemoteEntryInfo(
- providerId = providerId,
- entryKey = remoteEntry.key,
- entrySubkey = remoteEntry.subkey,
- pendingIntent = structuredRemoteEntry.pendingIntent,
- fillInIntent = remoteEntry.frameworkExtrasIntent,
- )
- }
-
- /**
- * Note: caller required handle empty list due to parsing error.
- */
- private fun getActionEntryList(
- providerId: String,
- actionEntries: List<Entry>,
- providerIcon: Drawable,
- ): List<ActionEntryInfo> {
- val result: MutableList<ActionEntryInfo> = mutableListOf()
- actionEntries.forEach {
- val actionEntryUi = Action.fromSlice(it.slice) ?: return@forEach
- result.add(ActionEntryInfo(
- providerId = providerId,
- entryKey = it.key,
- entrySubkey = it.subkey,
- pendingIntent = actionEntryUi.pendingIntent,
- fillInIntent = it.frameworkExtrasIntent,
- title = actionEntryUi.title.toString(),
- icon = providerIcon,
- subTitle = actionEntryUi.subtitle?.toString(),
- ))
- }
- return result
- }
}
}
@@ -686,7 +465,8 @@
val result: MutableList<CreateOptionInfo> = mutableListOf()
creationEntries.forEach {
val createEntry = CreateEntry.fromSlice(it.slice) ?: return@forEach
- result.add(CreateOptionInfo(
+ result.add(
+ CreateOptionInfo(
providerId = providerId,
entryKey = it.key,
entrySubkey = it.subkey,
@@ -705,7 +485,8 @@
it.hasHint("androidx.credentials.provider.createEntry.SLICE_HINT_AUTO_" +
"SELECT_ALLOWED")
}?.text == "true",
- ))
+ )
+ )
}
return result.sortedWith(
compareByDescending { it.lastUsedTime }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 281696d..20d2f09 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -48,10 +48,11 @@
import androidx.credentials.provider.PasswordCredentialEntry
import androidx.credentials.provider.PublicKeyCredentialEntry
import com.android.credentialmanager.GetFlowUtils
-import com.android.credentialmanager.getflow.CredentialEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
import com.android.credentialmanager.getflow.ProviderDisplayInfo
-import com.android.credentialmanager.getflow.ProviderInfo
+import com.android.credentialmanager.model.get.ProviderInfo
import com.android.credentialmanager.getflow.toProviderDisplayInfo
+import com.android.credentialmanager.ktx.credentialEntry
import org.json.JSONObject
import java.util.concurrent.Executors
@@ -122,8 +123,7 @@
val entryIconMap: MutableMap<String, Icon> = mutableMapOf()
candidateProviderDataList.forEach { provider ->
provider.credentialEntries.forEach { entry ->
- val credentialEntry = GetFlowUtils.parseCredentialEntryFromSlice(entry.slice)
- when (credentialEntry) {
+ when (val credentialEntry = entry.slice.credentialEntry) {
is PasswordCredentialEntry -> {
entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
}
@@ -172,11 +172,11 @@
}
private fun processProvidersForAutofillId(
- filLRequest: FillRequest,
- autofillId: AutofillId,
- providerList: List<ProviderInfo>,
- entryIconMap: Map<String, Icon>,
- fillResponseBuilder: FillResponse.Builder
+ filLRequest: FillRequest,
+ autofillId: AutofillId,
+ providerList: List<ProviderInfo>,
+ entryIconMap: Map<String, Icon>,
+ fillResponseBuilder: FillResponse.Builder
): Boolean {
if (providerList.isEmpty()) {
return false
@@ -200,7 +200,8 @@
providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@ {
val primaryEntry = it.sortedCredentialEntryList.first()
val pendingIntent = primaryEntry.pendingIntent
- if (pendingIntent == null || primaryEntry.fillInIntent == null) {
+ val fillInIntent = primaryEntry.fillInIntent
+ if (pendingIntent == null || fillInIntent == null) {
// FillInIntent will not be null because autofillId was retrieved from it.
Log.e(TAG, "PendingIntent was missing from the entry.")
return@usernameLoop
@@ -245,7 +246,7 @@
presentationBuilder.build())
.build())
.setAuthentication(pendingIntent.intentSender)
- .setAuthenticationExtras(primaryEntry.fillInIntent.extras)
+ .setAuthenticationExtras(fillInIntent.extras)
.build())
datasetAdded = true
}
@@ -322,8 +323,8 @@
}
private fun copyProviderInfo(
- providerInfo: ProviderInfo,
- credentialList: List<CredentialEntryInfo>
+ providerInfo: ProviderInfo,
+ credentialList: List<CredentialEntryInfo>
): ProviderInfo {
return ProviderInfo(
providerInfo.id,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index d45b6f6..14a9165 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -48,8 +48,8 @@
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.CredentialSelectorViewModel
import com.android.credentialmanager.R
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.common.material.ModalBottomSheetDefaults
import com.android.credentialmanager.common.ui.ActionButton
@@ -68,6 +68,8 @@
import com.android.credentialmanager.common.ui.PasskeyBenefitRow
import com.android.credentialmanager.common.ui.HeadlineText
import com.android.credentialmanager.logging.CreateCredentialEvent
+import com.android.credentialmanager.model.creation.CreateOptionInfo
+import com.android.credentialmanager.model.creation.RemoteInfo
import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
import com.android.internal.logging.UiEventLogger.UiEventEnum
@@ -259,15 +261,15 @@
@Composable
fun MoreOptionsSelectionCard(
- requestDisplayInfo: RequestDisplayInfo,
- enabledProviderList: List<EnabledProviderInfo>,
- disabledProviderList: List<DisabledProviderInfo>?,
- sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
- onBackCreationSelectionButtonSelected: () -> Unit,
- onOptionSelected: (ActiveEntry) -> Unit,
- onDisabledProvidersSelected: () -> Unit,
- onRemoteEntrySelected: (BaseEntry) -> Unit,
- onLog: @Composable (UiEventEnum) -> Unit,
+ requestDisplayInfo: RequestDisplayInfo,
+ enabledProviderList: List<EnabledProviderInfo>,
+ disabledProviderList: List<DisabledProviderInfo>?,
+ sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
+ onBackCreationSelectionButtonSelected: () -> Unit,
+ onOptionSelected: (ActiveEntry) -> Unit,
+ onDisabledProvidersSelected: () -> Unit,
+ onRemoteEntrySelected: (EntryInfo) -> Unit,
+ onLog: @Composable (UiEventEnum) -> Unit,
) {
SheetContainerCard(topAppBar = {
MoreOptionTopAppBar(
@@ -378,14 +380,14 @@
@Composable
fun CreationSelectionCard(
- requestDisplayInfo: RequestDisplayInfo,
- enabledProviderList: List<EnabledProviderInfo>,
- providerInfo: EnabledProviderInfo,
- createOptionInfo: CreateOptionInfo,
- onOptionSelected: (BaseEntry) -> Unit,
- onConfirm: () -> Unit,
- onMoreOptionsSelected: () -> Unit,
- onLog: @Composable (UiEventEnum) -> Unit,
+ requestDisplayInfo: RequestDisplayInfo,
+ enabledProviderList: List<EnabledProviderInfo>,
+ providerInfo: EnabledProviderInfo,
+ createOptionInfo: CreateOptionInfo,
+ onOptionSelected: (EntryInfo) -> Unit,
+ onConfirm: () -> Unit,
+ onMoreOptionsSelected: () -> Unit,
+ onLog: @Composable (UiEventEnum) -> Unit,
) {
SheetContainerCard {
item {
@@ -474,11 +476,11 @@
@Composable
fun ExternalOnlySelectionCard(
- requestDisplayInfo: RequestDisplayInfo,
- activeRemoteEntry: BaseEntry,
- onOptionSelected: (BaseEntry) -> Unit,
- onConfirm: () -> Unit,
- onLog: @Composable (UiEventEnum) -> Unit,
+ requestDisplayInfo: RequestDisplayInfo,
+ activeRemoteEntry: EntryInfo,
+ onOptionSelected: (EntryInfo) -> Unit,
+ onConfirm: () -> Unit,
+ onLog: @Composable (UiEventEnum) -> Unit,
) {
SheetContainerCard {
item { HeadlineIcon(imageVector = Icons.Outlined.QrCodeScanner) }
@@ -575,17 +577,14 @@
@Composable
fun PrimaryCreateOptionRow(
requestDisplayInfo: RequestDisplayInfo,
- entryInfo: BaseEntry,
- onOptionSelected: (BaseEntry) -> Unit
+ entryInfo: EntryInfo,
+ onOptionSelected: (EntryInfo) -> Unit
) {
Entry(
onClick = { onOptionSelected(entryInfo) },
- iconImageBitmap =
- if (entryInfo is CreateOptionInfo && entryInfo.profileIcon != null) {
- entryInfo.profileIcon.toBitmap().asImageBitmap()
- } else {
- requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()
- },
+ iconImageBitmap = ((entryInfo as? CreateOptionInfo)?.profileIcon
+ ?: requestDisplayInfo.typeIcon)
+ .toBitmap().asImageBitmap(),
shouldApplyIconImageBitmapTint = !(entryInfo is CreateOptionInfo &&
entryInfo.profileIcon != null),
entryHeadlineText = requestDisplayInfo.title,
@@ -627,32 +626,33 @@
entryThirdLineText =
if (requestDisplayInfo.type == CredentialType.PASSKEY ||
requestDisplayInfo.type == CredentialType.PASSWORD) {
- if (createOptionInfo.passwordCount != null &&
- createOptionInfo.passkeyCount != null
- ) {
+ val passwordCount = createOptionInfo.passwordCount
+ val passkeyCount = createOptionInfo.passkeyCount
+ if (passwordCount != null && passkeyCount != null) {
stringResource(
R.string.more_options_usage_passwords_passkeys,
- createOptionInfo.passwordCount,
- createOptionInfo.passkeyCount
+ passwordCount,
+ passkeyCount
)
- } else if (createOptionInfo.passwordCount != null) {
+ } else if (passwordCount != null) {
stringResource(
R.string.more_options_usage_passwords,
- createOptionInfo.passwordCount
+ passwordCount
)
- } else if (createOptionInfo.passkeyCount != null) {
+ } else if (passkeyCount != null) {
stringResource(
R.string.more_options_usage_passkeys,
- createOptionInfo.passkeyCount
+ passkeyCount
)
} else {
null
}
} else {
- if (createOptionInfo.totalCredentialCount != null) {
+ val totalCredentialCount = createOptionInfo.totalCredentialCount
+ if (totalCredentialCount != null) {
stringResource(
R.string.more_options_usage_credentials,
- createOptionInfo.totalCredentialCount
+ totalCredentialCount
)
} else {
null
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index e9e8c2e..8b0ba87 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -16,22 +16,21 @@
package com.android.credentialmanager.createflow
-import android.app.PendingIntent
-import android.content.Intent
import android.graphics.drawable.Drawable
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
-import java.time.Instant
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.creation.CreateOptionInfo
+import com.android.credentialmanager.model.creation.RemoteInfo
data class CreateCredentialUiState(
- val enabledProviders: List<EnabledProviderInfo>,
- val disabledProviders: List<DisabledProviderInfo>? = null,
- val currentScreenState: CreateScreenState,
- val requestDisplayInfo: RequestDisplayInfo,
- val sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
- val activeEntry: ActiveEntry? = null,
- val remoteEntry: RemoteInfo? = null,
- val foundCandidateFromUserDefaultProvider: Boolean,
+ val enabledProviders: List<EnabledProviderInfo>,
+ val disabledProviders: List<DisabledProviderInfo>? = null,
+ val currentScreenState: CreateScreenState,
+ val requestDisplayInfo: RequestDisplayInfo,
+ val sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
+ val activeEntry: ActiveEntry? = null,
+ val remoteEntry: RemoteInfo? = null,
+ val foundCandidateFromUserDefaultProvider: Boolean,
)
internal fun isFlowAutoSelectable(
@@ -75,44 +74,6 @@
displayName: String,
) : ProviderInfo(icon, id, displayName)
-class CreateOptionInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
- val userProviderDisplayName: String,
- val profileIcon: Drawable?,
- val passwordCount: Int?,
- val passkeyCount: Int?,
- val totalCredentialCount: Int?,
- val lastUsedTime: Instant,
- val footerDescription: String?,
- val allowAutoSelect: Boolean,
-) : BaseEntry(
- providerId,
- entryKey,
- entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
-class RemoteInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
-) : BaseEntry(
- providerId,
- entryKey,
- entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
data class RequestDisplayInfo(
val title: String,
val subtitle: String?,
@@ -131,8 +92,8 @@
* user selects a different entry on the more option page.
*/
data class ActiveEntry (
- val activeProvider: EnabledProviderInfo,
- val activeEntryInfo: BaseEntry,
+ val activeProvider: EnabledProviderInfo,
+ val activeEntryInfo: EntryInfo,
)
/** The name of the current screen. */
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 72d030b..4ed84b9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -48,8 +48,9 @@
import androidx.core.graphics.drawable.toBitmap
import com.android.credentialmanager.CredentialSelectorViewModel
import com.android.credentialmanager.R
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.get.ProviderInfo
import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.common.material.ModalBottomSheetDefaults
import com.android.credentialmanager.common.ui.ActionButton
@@ -68,6 +69,10 @@
import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant
import com.android.credentialmanager.common.ui.Snackbar
import com.android.credentialmanager.logging.GetCredentialEvent
+import com.android.credentialmanager.model.get.ActionEntryInfo
+import com.android.credentialmanager.model.get.AuthenticationEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.get.RemoteEntryInfo
import com.android.credentialmanager.userAndDisplayNameForPasskey
import com.android.internal.logging.UiEventLogger.UiEventEnum
@@ -175,8 +180,8 @@
requestDisplayInfo: RequestDisplayInfo,
providerDisplayInfo: ProviderDisplayInfo,
providerInfoList: List<ProviderInfo>,
- activeEntry: BaseEntry?,
- onEntrySelected: (BaseEntry) -> Unit,
+ activeEntry: EntryInfo?,
+ onEntrySelected: (EntryInfo) -> Unit,
onConfirm: () -> Unit,
onMoreOptionSelected: () -> Unit,
onLog: @Composable (UiEventEnum) -> Unit,
@@ -358,7 +363,7 @@
fun AllSignInOptionCard(
providerInfoList: List<ProviderInfo>,
providerDisplayInfo: ProviderDisplayInfo,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
onBackButtonClicked: () -> Unit,
onCancel: () -> Unit,
onLog: @Composable (UiEventEnum) -> Unit,
@@ -436,7 +441,7 @@
@Composable
fun ActionChips(
providerInfoList: List<ProviderInfo>,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
isFirstSection: Boolean,
) {
val actionChips = providerInfoList.flatMap { it.actionEntryList }
@@ -460,7 +465,7 @@
@Composable
fun RemoteEntryCard(
remoteEntry: RemoteEntryInfo,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
isFirstSection: Boolean,
) {
CredentialListSectionHeader(
@@ -486,7 +491,7 @@
@Composable
fun LockedCredentials(
authenticationEntryList: List<AuthenticationEntryInfo>,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
isFirstSection: Boolean,
) {
CredentialListSectionHeader(
@@ -508,7 +513,7 @@
@Composable
fun PerUserNameCredentials(
perUserNameCredentialEntryList: PerUserNameCredentialEntryList,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
isFirstSection: Boolean,
) {
CredentialListSectionHeader(
@@ -532,7 +537,7 @@
@Composable
fun CredentialEntryRow(
credentialEntryInfo: CredentialEntryInfo,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
enforceOneLine: Boolean = false,
onTextLayout: (TextLayoutResult) -> Unit = {},
) {
@@ -571,7 +576,7 @@
@Composable
fun AuthenticationEntryRow(
authenticationEntryInfo: AuthenticationEntryInfo,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
enforceOneLine: Boolean = false,
) {
Entry(
@@ -596,7 +601,7 @@
@Composable
fun ActionEntryRow(
actionEntryInfo: ActionEntryInfo,
- onEntrySelected: (BaseEntry) -> Unit,
+ onEntrySelected: (EntryInfo) -> Unit,
) {
ActionEntry(
iconImageBitmap = actionEntryInfo.icon.toBitmap().asImageBitmap(),
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 447a9d2..46bebc4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -16,21 +16,21 @@
package com.android.credentialmanager.getflow
-import android.app.PendingIntent
-import android.content.Intent
import android.graphics.drawable.Drawable
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.CredentialType
+import com.android.credentialmanager.model.get.ProviderInfo
+import com.android.credentialmanager.model.EntryInfo
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.get.AuthenticationEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.get.RemoteEntryInfo
import com.android.internal.util.Preconditions
-import java.time.Instant
-
data class GetCredentialUiState(
val providerInfoList: List<ProviderInfo>,
val requestDisplayInfo: RequestDisplayInfo,
val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
val currentScreenState: GetScreenState = toGetScreenState(providerDisplayInfo),
- val activeEntry: BaseEntry? = toActiveEntry(providerDisplayInfo),
+ val activeEntry: EntryInfo? = toActiveEntry(providerDisplayInfo),
val isNoAccount: Boolean = false,
)
@@ -58,20 +58,6 @@
return null
}
-data class ProviderInfo(
- /**
- * Unique id (component name) of this provider.
- * Not for display purpose - [displayName] should be used for ui rendering.
- */
- val id: String,
- val icon: Drawable,
- val displayName: String,
- val credentialEntryList: List<CredentialEntryInfo>,
- val authenticationEntryList: List<AuthenticationEntryInfo>,
- val remoteEntry: RemoteEntryInfo?,
- val actionEntryList: List<ActionEntryInfo>,
-)
-
/** Display-centric data structure derived from the [ProviderInfo]. This abstraction is not grouping
* by the provider id but instead focuses on structures convenient for display purposes. */
data class ProviderDisplayInfo(
@@ -84,87 +70,6 @@
val remoteEntry: RemoteEntryInfo?
)
-class CredentialEntryInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
- /** Type of this credential used for sorting. Not localized so must not be directly displayed. */
- val credentialType: CredentialType,
- /** Localized type value of this credential used for display purpose. */
- val credentialTypeDisplayName: String,
- val providerDisplayName: String,
- val userName: String,
- val displayName: String?,
- val icon: Drawable?,
- val shouldTintIcon: Boolean,
- val lastUsedTimeMillis: Instant?,
- val isAutoSelectable: Boolean,
-) : BaseEntry(
- providerId,
- entryKey,
- entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
-class AuthenticationEntryInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
- val title: String,
- val providerDisplayName: String,
- val icon: Drawable,
- // The entry had been unlocked and turned out to be empty. Used to determine whether to
- // show "Tap to unlock" or "No sign-in info" for this entry.
- val isUnlockedAndEmpty: Boolean,
- // True if the entry was the last one unlocked. Used to show the no sign-in info snackbar.
- val isLastUnlocked: Boolean,
-) : BaseEntry(
- providerId,
- entryKey, entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = false,
-)
-
-class RemoteEntryInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
-) : BaseEntry(
- providerId,
- entryKey,
- entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
-class ActionEntryInfo(
- providerId: String,
- entryKey: String,
- entrySubkey: String,
- pendingIntent: PendingIntent?,
- fillInIntent: Intent?,
- val title: String,
- val icon: Drawable,
- val subTitle: String?,
-) : BaseEntry(
- providerId,
- entryKey,
- entrySubkey,
- pendingIntent,
- fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = true,
-)
-
data class RequestDisplayInfo(
val appName: String,
val preferImmediatelyAvailableCredentials: Boolean,
@@ -218,8 +123,8 @@
val remoteEntryList = mutableListOf<RemoteEntryInfo>()
providerInfoList.forEach { providerInfo ->
authenticationEntryList.addAll(providerInfo.authenticationEntryList)
- if (providerInfo.remoteEntry != null) {
- remoteEntryList.add(providerInfo.remoteEntry)
+ providerInfo.remoteEntry?.let {
+ remoteEntryList.add(it)
}
// There can only be at most one remote entry
Preconditions.checkState(remoteEntryList.size <= 1)
@@ -260,11 +165,11 @@
private fun toActiveEntry(
providerDisplayInfo: ProviderDisplayInfo,
-): BaseEntry? {
+): EntryInfo? {
val sortedUserNameToCredentialEntryList =
providerDisplayInfo.sortedUserNameToCredentialEntryList
val authenticationEntryList = providerDisplayInfo.authenticationEntryList
- var activeEntry: BaseEntry? = null
+ var activeEntry: EntryInfo? = null
if (sortedUserNameToCredentialEntryList
.size == 1 && authenticationEntryList.isEmpty()
) {
@@ -302,17 +207,18 @@
return 1
}
}
-
+ val p0LastUsedTimeMillis = p0.lastUsedTimeMillis
+ val p1LastUsedTimeMillis = p1.lastUsedTimeMillis
// Then order by last used timestamp
- if (p0.lastUsedTimeMillis != null && p1.lastUsedTimeMillis != null) {
- if (p0.lastUsedTimeMillis < p1.lastUsedTimeMillis) {
+ if (p0LastUsedTimeMillis != null && p1LastUsedTimeMillis != null) {
+ if (p0LastUsedTimeMillis < p1LastUsedTimeMillis) {
return 1
- } else if (p0.lastUsedTimeMillis > p1.lastUsedTimeMillis) {
+ } else if (p0LastUsedTimeMillis > p1LastUsedTimeMillis) {
return -1
}
- } else if (p0.lastUsedTimeMillis != null) {
+ } else if (p0LastUsedTimeMillis != null) {
return -1
- } else if (p1.lastUsedTimeMillis != null) {
+ } else if (p1LastUsedTimeMillis != null) {
return 1
}
return 0
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
index 6ededf3..d8a6019 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
@@ -1,29 +1,20 @@
package com.android.credentialmanager.di
-import android.content.Context
-import android.content.pm.PackageManager
import com.android.credentialmanager.client.CredentialManagerClient
import com.android.credentialmanager.client.impl.CredentialManagerClientImpl
+import dagger.Binds
import dagger.Module
-import dagger.Provides
import dagger.hilt.InstallIn
-import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
-internal object AppModule {
- @Provides
+abstract class AppModule {
+ @Binds
@Singleton
- @JvmStatic
- fun providePackageManager(@ApplicationContext context: Context): PackageManager =
- context.packageManager
-
- @Provides
- @Singleton
- @JvmStatic
- fun provideCredentialManagerClient(packageManager: PackageManager): CredentialManagerClient =
- CredentialManagerClientImpl(packageManager)
+ abstract fun provideCredentialManagerClient(
+ client: CredentialManagerClientImpl
+ ): CredentialManagerClient
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
index f2f878e..14b992a 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
@@ -23,8 +23,8 @@
// TODO: b/301206470 returning a hard coded state for MVP
if (true) return CredentialSelectorUiState.Get.SingleProviderSinglePassword
- return if (providers.size == 1) {
- if (passwordEntries.size == 1) {
+ return if (providerInfos.size == 1) {
+ if (providerInfos.first().credentialEntryList.size == 1) {
CredentialSelectorUiState.Get.SingleProviderSinglePassword
} else {
TODO() // b/301206470 - Implement other get flows
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
index c28df3e8..b64f581 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -76,7 +76,9 @@
}
SideEffect {
- launcher.launch(state.intentSenderRequest)
+ state.intentSenderRequest?.let {
+ launcher.launch(it)
+ }
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
index fb72c54..26bee1f 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
@@ -26,9 +26,9 @@
import androidx.lifecycle.viewModelScope
import com.android.credentialmanager.TAG
import com.android.credentialmanager.ktx.getIntentSenderRequest
-import com.android.credentialmanager.model.Password
import com.android.credentialmanager.model.Request
import com.android.credentialmanager.client.CredentialManagerClient
+import com.android.credentialmanager.model.get.CredentialEntryInfo
import com.android.credentialmanager.ui.model.PasswordUiModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -44,7 +44,7 @@
private var initializeCalled = false
private lateinit var requestGet: Request.Get
- private lateinit var password: Password
+ private lateinit var entryInfo: CredentialEntryInfo
private val _uiState =
MutableStateFlow<SinglePasswordScreenUiState>(SinglePasswordScreenUiState.Idle)
@@ -63,14 +63,15 @@
_uiState.value = SinglePasswordScreenUiState.Error
} else {
requestGet = request
- if (requestGet.passwordEntries.isEmpty()) {
+
+ if (requestGet.providerInfos.all { it.credentialEntryList.isEmpty() }) {
Log.d(TAG, "Empty passwordEntries")
_uiState.value = SinglePasswordScreenUiState.Error
} else {
- password = requestGet.passwordEntries.first()
+ entryInfo = requestGet.providerInfos.first().credentialEntryList.first()
_uiState.value = SinglePasswordScreenUiState.Loaded(
PasswordUiModel(
- email = password.passwordCredentialEntry.username.toString(),
+ email = entryInfo.userName,
)
)
}
@@ -84,7 +85,7 @@
fun onOKClick() {
_uiState.value = SinglePasswordScreenUiState.PasswordSelected(
- intentSenderRequest = password.getIntentSenderRequest()
+ intentSenderRequest = entryInfo.getIntentSenderRequest()
)
}
@@ -94,9 +95,9 @@
) {
val userSelectionDialogResult = UserSelectionDialogResult(
requestGet.token,
- password.providerId,
- password.entry.key,
- password.entry.subkey,
+ entryInfo.providerId,
+ entryInfo.entryKey,
+ entryInfo.entrySubkey,
if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
)
credentialManagerClient.sendResult(userSelectionDialogResult)
@@ -108,7 +109,7 @@
data object Idle : SinglePasswordScreenUiState()
data class Loaded(val passwordUiModel: PasswordUiModel) : SinglePasswordScreenUiState()
data class PasswordSelected(
- val intentSenderRequest: IntentSenderRequest
+ val intentSenderRequest: IntentSenderRequest?
) : SinglePasswordScreenUiState()
data object Cancel : SinglePasswordScreenUiState()
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 38bd7d5..6213b34 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -49,6 +49,7 @@
"androidx.fragment_fragment",
"androidx.lifecycle_lifecycle-livedata",
"androidx.lifecycle_lifecycle-extensions",
+ "android.content.pm.flags-aconfig-java",
],
lint: {
@@ -75,6 +76,7 @@
"androidx.fragment_fragment",
"androidx.lifecycle_lifecycle-livedata",
"androidx.lifecycle_lifecycle-extensions",
+ "android.content.pm.flags-aconfig-java",
],
aaptflags: ["--product tablet"],
@@ -103,6 +105,7 @@
"androidx.fragment_fragment",
"androidx.lifecycle_lifecycle-livedata",
"androidx.lifecycle_lifecycle-extensions",
+ "android.content.pm.flags-aconfig-java",
],
aaptflags: ["--product tv"],
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
index 19d74b3..7b17cbd 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
@@ -16,8 +16,6 @@
package com.android.packageinstaller;
-import static android.content.Intent.CATEGORY_LAUNCHER;
-
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
import android.app.Activity;
@@ -47,9 +45,6 @@
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
setResult(resultCode, data);
finish();
- if (data != null && data.hasCategory(CATEGORY_LAUNCHER)) {
- startActivity(data);
- }
}
@Override
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index 8d8254a..daedb1a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -298,14 +298,7 @@
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
- try {
- session.commit(pendingIntent.getIntentSender());
- } catch (Exception e) {
- Log.e(LOG_TAG, "Cannot install package: ", e);
- launchFailure(PackageInstaller.STATUS_FAILURE,
- PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
- return;
- }
+ session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index e2107eb..4187058 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -16,12 +16,14 @@
package com.android.packageinstaller;
+import static android.content.pm.Flags.usePiaV2;
import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
import android.Manifest;
import android.app.Activity;
import android.app.DialogFragment;
import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -57,14 +59,21 @@
private final boolean mLocalLOGV = false;
- // TODO (sumedhsen): Replace with an Android Feature Flag once implemented
- private static final boolean USE_PIA_V2 = false;
-
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (USE_PIA_V2) {
+ mPackageManager = getPackageManager();
+ if (usePiaV2()) {
+ Log.i(TAG, "Using Pia V2");
+
+ mPackageManager.setComponentEnabledSetting(new ComponentName(this,
+ com.android.packageinstaller.InstallEventReceiver.class),
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
+ mPackageManager.setComponentEnabledSetting(new ComponentName(this,
+ com.android.packageinstaller.v2.model.InstallEventReceiver.class),
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+
Intent piaV2 = new Intent(getIntent());
piaV2.putExtra(InstallLaunch.EXTRA_CALLING_PKG_NAME, getCallingPackage());
piaV2.putExtra(InstallLaunch.EXTRA_CALLING_PKG_UID, getLaunchedFromUid());
@@ -74,7 +83,6 @@
finish();
return;
}
- mPackageManager = getPackageManager();
mUserManager = getSystemService(UserManager.class);
Intent intent = getIntent();
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
index 9af88c3b..215ead3 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -120,7 +121,12 @@
Button launchButton = mDialog.getButton(DialogInterface.BUTTON_POSITIVE);
if (enabled) {
launchButton.setOnClickListener(view -> {
- setResult(Activity.RESULT_OK, mLaunchIntent);
+ try {
+ startActivity(mLaunchIntent.addFlags(
+ Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP));
+ } catch (ActivityNotFoundException | SecurityException e) {
+ Log.e(LOG_TAG, "Could not start activity", e);
+ }
finish();
});
} else {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 34062a4..ba627e9 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -17,8 +17,8 @@
package com.android.packageinstaller;
import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.content.pm.Flags.usePiaV2;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-
import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
import android.Manifest;
@@ -81,9 +81,6 @@
private String mPackageName;
private DialogInfo mDialogInfo;
- // TODO (sumedhsen): Replace with an Android Feature Flag once implemented
- private static final boolean USE_PIA_V2 = false;
-
@Override
public void onCreate(Bundle icicle) {
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
@@ -92,7 +89,18 @@
// be stale, if e.g. the app was uninstalled while the activity was destroyed.
super.onCreate(null);
- if (USE_PIA_V2 && !isTv()) {
+ if (usePiaV2() && !isTv()) {
+ Log.i(TAG, "Using Pia V2");
+
+ PackageManager pm = getPackageManager();
+ pm.setComponentEnabledSetting(
+ new ComponentName(this, com.android.packageinstaller.UninstallEventReceiver.class),
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
+ pm.setComponentEnabledSetting(
+ new ComponentName(this,
+ com.android.packageinstaller.v2.model.UninstallEventReceiver.class),
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+
boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
Intent piaV2 = new Intent(getIntent());
piaV2.putExtra(UninstallLaunch.EXTRA_CALLING_PKG_UID, getLaunchedFromUid());
diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp
index 4871ef3..010a6ce 100644
--- a/packages/SettingsLib/MainSwitchPreference/Android.bp
+++ b/packages/SettingsLib/MainSwitchPreference/Android.bp
@@ -17,7 +17,6 @@
static_libs: [
"androidx.preference_preference",
"SettingsLibSettingsTheme",
- "SettingsLibUtils",
],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
index 4b3acbf..e70114f 100644
--- a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
@@ -17,5 +17,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.widget.mainswitch">
-
+ <uses-sdk android:minSdkVersion="21" />
</manifest>
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 2b5fcd8..e6f61a8 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -18,6 +18,8 @@
import android.content.Context;
import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
@@ -30,7 +32,6 @@
import androidx.annotation.ColorInt;
-import com.android.settingslib.utils.BuildCompatUtils;
import com.android.settingslib.widget.mainswitch.R;
import java.util.ArrayList;
@@ -72,11 +73,18 @@
LayoutInflater.from(context).inflate(R.layout.settingslib_main_switch_bar, this);
- if (!BuildCompatUtils.isAtLeastS()) {
- final TypedArray a = context.obtainStyledAttributes(
- new int[]{android.R.attr.colorAccent});
- mBackgroundActivatedColor = a.getColor(0, 0);
- mBackgroundColor = context.getColor(androidx.appcompat.R.color.material_grey_600);
+ if (Build.VERSION.SDK_INT < VERSION_CODES.S) {
+ TypedArray a;
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
+ a = context.obtainStyledAttributes(
+ new int[]{android.R.attr.colorAccent});
+ mBackgroundActivatedColor = a.getColor(0, 0);
+ mBackgroundColor = context.getColor(androidx.appcompat.R.color.material_grey_600);
+ } else {
+ a = context.obtainStyledAttributes(new int[]{android.R.attr.colorPrimary});
+ mBackgroundActivatedColor = a.getColor(0, 0);
+ mBackgroundColor = a.getColor(0, 0);
+ }
a.recycle();
}
@@ -148,7 +156,7 @@
* Set icon space reserved for title
*/
public void setIconSpaceReserved(boolean iconSpaceReserved) {
- if (mTextView != null && !BuildCompatUtils.isAtLeastS()) {
+ if (mTextView != null && (Build.VERSION.SDK_INT < VERSION_CODES.S)) {
LayoutParams params = (LayoutParams) mTextView.getLayoutParams();
int iconSpace = getContext().getResources().getDimensionPixelSize(
R.dimen.settingslib_switchbar_subsettings_margin_start);
@@ -207,7 +215,7 @@
mTextView.setEnabled(enabled);
mSwitch.setEnabled(enabled);
- if (BuildCompatUtils.isAtLeastS()) {
+ if (Build.VERSION.SDK_INT >= VERSION_CODES.S) {
mFrameView.setEnabled(enabled);
mFrameView.setActivated(isChecked());
}
@@ -222,7 +230,7 @@
}
private void setBackground(boolean isChecked) {
- if (!BuildCompatUtils.isAtLeastS()) {
+ if (Build.VERSION.SDK_INT < VERSION_CODES.S) {
setBackgroundColor(isChecked ? mBackgroundActivatedColor : mBackgroundColor);
} else {
mFrameView.setActivated(isChecked);
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
index 223e99e..5679694 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
@@ -72,7 +72,8 @@
private fun UserManager.showInSettings(userInfo: UserInfo): Int {
val userProperties = getUserProperties(userInfo.userHandle)
- return if (userInfo.isQuietModeEnabled && userProperties.hideInSettingsInQuietMode) {
+ return if (userInfo.isQuietModeEnabled && userProperties.showInQuietMode
+ == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) {
UserProperties.SHOW_IN_SETTINGS_NO
} else {
userProperties.showInSettings
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 1a5acf6..5aa2bfc 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1373,11 +1373,11 @@
<string name="tv_media_transfer_earc_subtitle">Connected via eARC</string>
<!-- TV media output switcher. Title for the default audio output of the device [CHAR LIMIT=NONE] -->
- <string name="tv_media_transfer_default">TV Default</string>
+ <string name="tv_media_transfer_default">TV default</string>
<!-- TV media output switcher. Subtitle for default audio output which is HDMI, e.g. TV dongle [CHAR LIMIT=NONE] -->
- <string name="tv_media_transfer_hdmi">HDMI Output</string>
+ <string name="tv_media_transfer_hdmi">HDMI output</string>
<!-- TV media output switcher. Subtitle for default audio output which is internal speaker, i.e. panel VTs [CHAR LIMIT=NONE] -->
- <string name="tv_media_transfer_internal_speakers">Internal Speakers</string>
+ <string name="tv_media_transfer_internal_speakers">Internal speakers</string>
<!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] -->
<string name="profile_connect_timeout_subtext">Problem connecting. Turn device off & back on</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java
index 83c106b..92db508 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryUtils.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.provider.Settings;
+import android.os.UserManager;
import android.util.ArraySet;
import android.view.accessibility.AccessibilityManager;
@@ -68,4 +69,10 @@
}
return packageNames;
}
+
+ /** Returns true if current user is a work profile user. */
+ public static boolean isWorkProfile(Context context) {
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ return userManager.isManagedProfile() && !userManager.isSystemUser();
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 5dacba5..52b51d7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -413,12 +413,8 @@
*/
@NonNull
List<MediaDevice> getSelectedMediaDevices() {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "getSelectedMediaDevices() package name is null or empty!");
- return Collections.emptyList();
- }
+ RoutingSessionInfo info = getRoutingSessionInfo();
- final RoutingSessionInfo info = getRoutingSessionInfo();
if (info == null) {
Log.w(TAG, "getSelectedMediaDevices() cannot find selectable MediaDevice from : "
+ mPackageName);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
index 3514932..02ec90d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java
@@ -48,6 +48,17 @@
"com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG";
/**
+ * An intent action to launch a media output dialog without any app or playback metadata, which
+ * only controls system routing.
+ *
+ * <p>System routes are those provided by the system, such as built-in speakers, wired headsets,
+ * bluetooth devices, and other outputs that require the app to feed media samples to the
+ * framework.
+ */
+ public static final String ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG =
+ "com.android.systemui.action.LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG";
+
+ /**
* An intent action to launch media output broadcast dialog.
*/
public static final String ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG =
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatteryUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatteryUtilsTest.java
index c3e0c0b..6424352 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatteryUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatteryUtilsTest.java
@@ -29,6 +29,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.UserManager;
import android.provider.Settings;
import android.view.accessibility.AccessibilityManager;
@@ -40,6 +41,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowAccessibilityManager;
import java.util.Arrays;
@@ -99,6 +101,20 @@
.containsExactly(DEFAULT_TTS_PACKAGE, ACCESSIBILITY_PACKAGE);
}
+ @Test
+ public void isWorkProfile_defaultValue_returnFalse() {
+ assertThat(BatteryUtils.isWorkProfile(mContext)).isFalse();
+ }
+
+ @Test
+ public void isWorkProfile_workProfileMode_returnTrue() {
+ final UserManager userManager = mContext.getSystemService(UserManager.class);
+ Shadows.shadowOf(userManager).setManagedProfile(true);
+ Shadows.shadowOf(userManager).setIsSystemUser(false);
+
+ assertThat(BatteryUtils.isWorkProfile(mContext)).isTrue();
+ }
+
private void setTtsPackageName(String defaultTtsPackageName) {
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.TTS_DEFAULT_SYNTH, defaultTtsPackageName);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index efed8c3..85e8769 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -235,6 +235,7 @@
Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR,
Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH,
Settings.Global.DEVICE_DEMO_MODE,
+ Settings.Global.DEVICE_IDLE_CONSTANTS,
Settings.Global.DISABLE_WINDOW_BLURS,
Settings.Global.BATTERY_SAVER_CONSTANTS,
Settings.Global.BATTERY_TIP_CONSTANTS,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e218308..0a71cda 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -1044,6 +1044,7 @@
android:exported="true">
<intent-filter android:priority="1">
<action android:name="com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG" />
+ <action android:name="com.android.systemui.action.LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG" />
<action android:name="com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG" />
<action android:name="com.android.systemui.action.DISMISS_MEDIA_OUTPUT_DIALOG" />
</intent-filter>
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 0480b9d..0c89a5d 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -126,24 +126,6 @@
"exclude-annotation": "android.platform.test.annotations.Postsubmit"
}
]
- },
- {
- // TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+)
- "name": "SystemUIGoogleBiometricsScreenshotTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
- "exclude-annotation": "android.platform.test.annotations.Postsubmit"
- }
- ]
}
],
@@ -170,18 +152,6 @@
"include-annotation": "androidx.test.filters.FlakyTest"
}
]
- },
- {
- // TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+)
- "name": "SystemUIGoogleBiometricsScreenshotTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "include-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
}
]
}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a745ab5..3e84597 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -114,6 +114,13 @@
}
flag {
+ name: "unfold_animation_background_progress"
+ namespace: "systemui"
+ description: "Moves unfold animation progress calculation to a background thread"
+ bug: "277879146"
+}
+
+flag {
name: "qs_new_pipeline"
namespace: "systemui"
description: "Use the new pipeline for Quick Settings. Should have no behavior changes."
@@ -143,8 +150,23 @@
}
flag {
+ name: "theme_overlay_controller_wakefulness_deprecation"
+ namespace: "systemui"
+ description: "Replacing WakefulnessLifecycle by KeyguardTransitionInteractor in "
+ "ThemOverlayController to mitigate flickering when locking the device"
+ bug: "308676488"
+}
+
+flag {
name: "media_in_scene_container"
namespace: "systemui"
description: "Enable media in the scene container framework"
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/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt
index ddc3d3a..1860c9f 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt
@@ -18,8 +18,8 @@
import android.app.AlertDialog
import android.content.Context
+import com.android.systemui.bouncer.ui.composable.BouncerDialogFactory
import com.android.systemui.bouncer.ui.composable.BouncerScene
-import com.android.systemui.bouncer.ui.composable.BouncerSceneDialogFactory
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.shared.model.Scene
@@ -38,8 +38,8 @@
@Provides
@SysUISingleton
- fun bouncerSceneDialogFactory(@Application context: Context): BouncerSceneDialogFactory {
- return object : BouncerSceneDialogFactory {
+ fun bouncerSceneDialogFactory(@Application context: Context): BouncerDialogFactory {
+ return object : BouncerDialogFactory {
override fun invoke(): AlertDialog {
return SystemUIDialog(context)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
new file mode 100644
index 0000000..ba80a8d
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -0,0 +1,734 @@
+/*
+ * 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.bouncer.ui.composable
+
+import android.app.AlertDialog
+import android.app.Dialog
+import android.content.DialogInterface
+import androidx.compose.animation.Crossfade
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.snap
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.KeyboardArrowDown
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.DpOffset
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.times
+import com.android.compose.PlatformButton
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.SceneTransitionLayout
+import com.android.compose.animation.scene.transitions
+import com.android.compose.modifiers.thenIf
+import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
+import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
+import com.android.systemui.common.shared.model.Text.Companion.loadText
+import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.fold.ui.composable.foldPosture
+import com.android.systemui.fold.ui.helper.FoldPosture
+import com.android.systemui.res.R
+import kotlin.math.abs
+import kotlin.math.max
+import kotlin.math.pow
+
+@Composable
+fun BouncerContent(
+ viewModel: BouncerViewModel,
+ dialogFactory: BouncerDialogFactory,
+ modifier: Modifier
+) {
+ val isFullScreenUserSwitcherEnabled = viewModel.isUserSwitcherVisible
+ val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsState()
+ val layout = calculateLayout(isSideBySideSupported = isSideBySideSupported)
+
+ when (layout) {
+ BouncerSceneLayout.STANDARD ->
+ StandardLayout(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ modifier = modifier,
+ )
+ BouncerSceneLayout.SIDE_BY_SIDE ->
+ SideBySideLayout(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
+ modifier = modifier,
+ )
+ BouncerSceneLayout.STACKED ->
+ StackedLayout(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
+ modifier = modifier,
+ )
+ BouncerSceneLayout.SPLIT ->
+ SplitLayout(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ modifier = modifier,
+ )
+ }
+}
+
+/**
+ * Renders the contents of the actual bouncer UI, the area that takes user input to do an
+ * authentication attempt, including all messaging UI (directives, reasoning, errors, etc.).
+ */
+@Composable
+private fun StandardLayout(
+ viewModel: BouncerViewModel,
+ dialogFactory: BouncerDialogFactory,
+ modifier: Modifier = Modifier,
+ outputOnly: Boolean = false,
+) {
+ val foldPosture: FoldPosture by foldPosture()
+ val isSplitAroundTheFoldRequired by viewModel.isFoldSplitRequired.collectAsState()
+ val isSplitAroundTheFold =
+ foldPosture == FoldPosture.Tabletop && !outputOnly && isSplitAroundTheFoldRequired
+ val currentSceneKey =
+ if (isSplitAroundTheFold) SceneKeys.SplitSceneKey else SceneKeys.ContiguousSceneKey
+
+ SceneTransitionLayout(
+ currentScene = currentSceneKey,
+ onChangeScene = {},
+ transitions = SceneTransitions,
+ modifier = modifier,
+ ) {
+ scene(SceneKeys.ContiguousSceneKey) {
+ FoldSplittable(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ outputOnly = outputOnly,
+ isSplit = false,
+ )
+ }
+
+ scene(SceneKeys.SplitSceneKey) {
+ FoldSplittable(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ outputOnly = outputOnly,
+ isSplit = true,
+ )
+ }
+ }
+}
+
+/**
+ * Renders the "standard" layout of the bouncer, where the bouncer is rendered on its own (no user
+ * switcher UI) and laid out vertically, centered horizontally.
+ *
+ * If [isSplit] is `true`, the top and bottom parts of the bouncer are split such that they don't
+ * render across the location of the fold hardware when the device is fully or part-way unfolded
+ * with the fold hinge in a horizontal position.
+ *
+ * If [outputOnly] is `true`, only the "output" part of the UI is shown (where the entered PIN
+ * "shapes" appear), if `false`, the entire UI is shown, including the area where the user can enter
+ * their PIN or pattern.
+ */
+@Composable
+private fun SceneScope.FoldSplittable(
+ viewModel: BouncerViewModel,
+ dialogFactory: BouncerDialogFactory,
+ outputOnly: Boolean,
+ isSplit: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
+ val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
+ var dialog: Dialog? by remember { mutableStateOf(null) }
+ val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsState()
+ val splitRatio =
+ LocalContext.current.resources.getFloat(
+ R.dimen.motion_layout_half_fold_bouncer_height_ratio
+ )
+
+ Column(modifier = modifier.padding(horizontal = 32.dp)) {
+ // Content above the fold, when split on a foldable device in a "table top" posture:
+ Box(
+ modifier =
+ Modifier.element(SceneElements.AboveFold).fillMaxWidth().thenIf(isSplit) {
+ Modifier.weight(splitRatio)
+ },
+ ) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier.fillMaxWidth().padding(top = 92.dp),
+ ) {
+ Crossfade(
+ targetState = message,
+ label = "Bouncer message",
+ animationSpec = if (message.isUpdateAnimated) tween() else snap(),
+ ) { message ->
+ Text(
+ text = message.text,
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.bodyLarge,
+ )
+ }
+
+ Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
+
+ UserInputArea(
+ viewModel = viewModel,
+ visibility = UserInputAreaVisibility.OUTPUT_ONLY,
+ )
+ }
+ }
+
+ // Content below the fold, when split on a foldable device in a "table top" posture:
+ Box(
+ modifier =
+ Modifier.element(SceneElements.BelowFold).fillMaxWidth().thenIf(isSplit) {
+ Modifier.weight(1 - splitRatio)
+ },
+ ) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ if (!outputOnly) {
+ Box(Modifier.weight(1f)) {
+ UserInputArea(
+ viewModel = viewModel,
+ visibility = UserInputAreaVisibility.INPUT_ONLY,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
+ }
+
+ Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
+
+ val actionButtonModifier = Modifier.height(56.dp)
+
+ actionButton.let { actionButtonViewModel ->
+ if (actionButtonViewModel != null) {
+ BouncerActionButton(
+ viewModel = actionButtonViewModel,
+ modifier = actionButtonModifier,
+ )
+ } else {
+ Spacer(modifier = actionButtonModifier)
+ }
+ }
+
+ Spacer(Modifier.height(48.dp))
+ }
+ }
+
+ if (dialogMessage != null) {
+ if (dialog == null) {
+ dialog =
+ dialogFactory().apply {
+ setMessage(dialogMessage)
+ setButton(
+ DialogInterface.BUTTON_NEUTRAL,
+ context.getString(R.string.ok),
+ ) { _, _ ->
+ viewModel.onThrottlingDialogDismissed()
+ }
+ setCancelable(false)
+ setCanceledOnTouchOutside(false)
+ show()
+ }
+ }
+ } else {
+ dialog?.dismiss()
+ dialog = null
+ }
+ }
+}
+
+/**
+ * Renders the user input area, where the user interacts with the UI to enter their credentials.
+ *
+ * For example, this can be the pattern input area, the password text box, or pin pad.
+ */
+@Composable
+private fun UserInputArea(
+ viewModel: BouncerViewModel,
+ visibility: UserInputAreaVisibility,
+ modifier: Modifier = Modifier,
+) {
+ val authMethodViewModel: AuthMethodBouncerViewModel? by
+ viewModel.authMethodViewModel.collectAsState()
+
+ when (val nonNullViewModel = authMethodViewModel) {
+ is PinBouncerViewModel ->
+ when (visibility) {
+ UserInputAreaVisibility.OUTPUT_ONLY ->
+ PinInputDisplay(
+ viewModel = nonNullViewModel,
+ modifier = modifier,
+ )
+ UserInputAreaVisibility.INPUT_ONLY ->
+ PinPad(
+ viewModel = nonNullViewModel,
+ modifier = modifier,
+ )
+ }
+ is PasswordBouncerViewModel ->
+ if (visibility == UserInputAreaVisibility.INPUT_ONLY) {
+ PasswordBouncer(
+ viewModel = nonNullViewModel,
+ modifier = modifier,
+ )
+ }
+ is PatternBouncerViewModel ->
+ if (visibility == UserInputAreaVisibility.INPUT_ONLY) {
+ PatternBouncer(
+ viewModel = nonNullViewModel,
+ modifier = modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
+ )
+ }
+ else -> Unit
+ }
+}
+
+/**
+ * Renders the action button on the bouncer, which triggers either Return to Call or Emergency Call.
+ */
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+private fun BouncerActionButton(
+ viewModel: BouncerActionButtonModel,
+ modifier: Modifier = Modifier,
+) {
+ Button(
+ onClick = viewModel.onClick,
+ modifier =
+ modifier.thenIf(viewModel.onLongClick != null) {
+ Modifier.combinedClickable(
+ onClick = viewModel.onClick,
+ onLongClick = viewModel.onLongClick,
+ )
+ },
+ colors =
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.tertiaryContainer,
+ contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
+ ),
+ ) {
+ Text(
+ text = viewModel.label,
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ }
+}
+
+/** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
+@Composable
+private fun UserSwitcher(
+ viewModel: BouncerViewModel,
+ modifier: Modifier = Modifier,
+) {
+ val selectedUserImage by viewModel.selectedUserImage.collectAsState(null)
+ val dropdownItems by viewModel.userSwitcherDropdown.collectAsState(emptyList())
+
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
+ modifier = modifier,
+ ) {
+ selectedUserImage?.let {
+ Image(
+ bitmap = it.asImageBitmap(),
+ contentDescription = null,
+ modifier = Modifier.size(SelectedUserImageSize),
+ )
+ }
+
+ val (isDropdownExpanded, setDropdownExpanded) = remember { mutableStateOf(false) }
+
+ dropdownItems.firstOrNull()?.let { firstDropdownItem ->
+ Spacer(modifier = Modifier.height(40.dp))
+
+ Box {
+ PlatformButton(
+ modifier =
+ Modifier
+ // Remove the built-in padding applied inside PlatformButton:
+ .padding(vertical = 0.dp)
+ .width(UserSwitcherDropdownWidth)
+ .height(UserSwitcherDropdownHeight),
+ colors =
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.surfaceContainerHighest,
+ contentColor = MaterialTheme.colorScheme.onSurface,
+ ),
+ onClick = { setDropdownExpanded(!isDropdownExpanded) },
+ ) {
+ val context = LocalContext.current
+ Text(
+ text = checkNotNull(firstDropdownItem.text.loadText(context)),
+ style = MaterialTheme.typography.headlineSmall,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+
+ Spacer(modifier = Modifier.weight(1f))
+
+ Icon(
+ imageVector = Icons.Default.KeyboardArrowDown,
+ contentDescription = null,
+ modifier = Modifier.size(32.dp),
+ )
+ }
+
+ UserSwitcherDropdownMenu(
+ isExpanded = isDropdownExpanded,
+ items = dropdownItems,
+ onDismissed = { setDropdownExpanded(false) },
+ )
+ }
+ }
+ }
+}
+
+/**
+ * Renders the dropdown menu that displays the actual users and/or user actions that can be
+ * selected.
+ */
+@Composable
+private fun UserSwitcherDropdownMenu(
+ isExpanded: Boolean,
+ items: List<BouncerViewModel.UserSwitcherDropdownItemViewModel>,
+ onDismissed: () -> Unit,
+) {
+ val context = LocalContext.current
+
+ // TODO(b/303071855): once the FR is fixed, remove this composition local override.
+ MaterialTheme(
+ colorScheme =
+ MaterialTheme.colorScheme.copy(
+ surface = MaterialTheme.colorScheme.surfaceContainerHighest,
+ ),
+ shapes = MaterialTheme.shapes.copy(extraSmall = RoundedCornerShape(28.dp)),
+ ) {
+ DropdownMenu(
+ expanded = isExpanded,
+ onDismissRequest = onDismissed,
+ offset =
+ DpOffset(
+ x = 0.dp,
+ y = -UserSwitcherDropdownHeight,
+ ),
+ modifier = Modifier.width(UserSwitcherDropdownWidth),
+ ) {
+ items.forEach { userSwitcherDropdownItem ->
+ DropdownMenuItem(
+ leadingIcon = {
+ Icon(
+ icon = userSwitcherDropdownItem.icon,
+ tint = MaterialTheme.colorScheme.primary,
+ modifier = Modifier.size(28.dp),
+ )
+ },
+ text = {
+ Text(
+ text = checkNotNull(userSwitcherDropdownItem.text.loadText(context)),
+ style = MaterialTheme.typography.bodyLarge,
+ color = MaterialTheme.colorScheme.onSurface,
+ )
+ },
+ onClick = {
+ onDismissed()
+ userSwitcherDropdownItem.onClick()
+ },
+ )
+ }
+ }
+ }
+}
+
+/**
+ * Renders the bouncer UI in split mode, with half on one side and half on the other side, swappable
+ * by double-tapping on the side.
+ */
+@Composable
+private fun SplitLayout(
+ viewModel: BouncerViewModel,
+ dialogFactory: BouncerDialogFactory,
+ modifier: Modifier = Modifier,
+) {
+ SwappableLayout(
+ startContent = { startContentModifier ->
+ StandardLayout(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ outputOnly = true,
+ modifier = startContentModifier,
+ )
+ },
+ endContent = { endContentModifier ->
+ UserInputArea(
+ viewModel = viewModel,
+ visibility = UserInputAreaVisibility.INPUT_ONLY,
+ modifier = endContentModifier,
+ )
+ },
+ modifier = modifier
+ )
+}
+
+/**
+ * Arranges the given two contents side-by-side, supporting a double tap anywhere on the background
+ * to flip their positions.
+ */
+@Composable
+private fun SwappableLayout(
+ startContent: @Composable (Modifier) -> Unit,
+ endContent: @Composable (Modifier) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val layoutDirection = LocalLayoutDirection.current
+ val isLeftToRight = layoutDirection == LayoutDirection.Ltr
+ val (isSwapped, setSwapped) = rememberSaveable(isLeftToRight) { mutableStateOf(!isLeftToRight) }
+
+ Row(
+ modifier =
+ modifier.pointerInput(Unit) {
+ detectTapGestures(
+ onDoubleTap = { offset ->
+ // Depending on where the user double tapped, switch the elements such that
+ // the endContent is closer to the side that was double tapped.
+ setSwapped(offset.x < size.width / 2)
+ }
+ )
+ },
+ ) {
+ val animatedOffset by
+ animateFloatAsState(
+ targetValue =
+ if (!isSwapped) {
+ // When startContent is first, both elements have their natural placement so
+ // they are not offset in any way.
+ 0f
+ } else if (isLeftToRight) {
+ // Since startContent is not first, the elements have to be swapped
+ // horizontally. In the case of LTR locales, this means pushing startContent
+ // to the right, hence the positive number.
+ 1f
+ } else {
+ // Since startContent is not first, the elements have to be swapped
+ // horizontally. In the case of RTL locales, this means pushing startContent
+ // to the left, hence the negative number.
+ -1f
+ },
+ label = "offset",
+ )
+
+ startContent(
+ Modifier.fillMaxHeight().weight(1f).graphicsLayer {
+ translationX = size.width * animatedOffset
+ alpha = animatedAlpha(animatedOffset)
+ }
+ )
+
+ Box(
+ modifier =
+ Modifier.fillMaxHeight().weight(1f).graphicsLayer {
+ // A negative sign is used to make sure this is offset in the direction that's
+ // opposite of the direction that the user switcher is pushed in.
+ translationX = -size.width * animatedOffset
+ alpha = animatedAlpha(animatedOffset)
+ }
+ ) {
+ endContent(Modifier.widthIn(max = 400.dp).align(Alignment.BottomCenter))
+ }
+ }
+}
+
+/**
+ * Arranges the bouncer contents and user switcher contents side-by-side, supporting a double tap
+ * anywhere on the background to flip their positions.
+ *
+ * In situations when [isUserSwitcherVisible] is `false`, one of two things may happen: either the
+ * UI for the bouncer will be shown on its own, taking up one side, with the other side just being
+ * empty space or, if that kind of "stand-alone side-by-side" isn't supported, the standard
+ * rendering of the bouncer will be used instead of the side-by-side layout.
+ */
+@Composable
+private fun SideBySideLayout(
+ viewModel: BouncerViewModel,
+ dialogFactory: BouncerDialogFactory,
+ isUserSwitcherVisible: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ SwappableLayout(
+ startContent = { startContentModifier ->
+ if (isUserSwitcherVisible) {
+ UserSwitcher(
+ viewModel = viewModel,
+ modifier = startContentModifier,
+ )
+ } else {
+ Box(
+ modifier = startContentModifier,
+ )
+ }
+ },
+ endContent = { endContentModifier ->
+ StandardLayout(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ modifier = endContentModifier,
+ )
+ },
+ modifier = modifier,
+ )
+}
+
+/** Arranges the bouncer contents and user switcher contents one on top of the other, vertically. */
+@Composable
+private fun StackedLayout(
+ viewModel: BouncerViewModel,
+ dialogFactory: BouncerDialogFactory,
+ isUserSwitcherVisible: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier = modifier,
+ ) {
+ if (isUserSwitcherVisible) {
+ UserSwitcher(
+ viewModel = viewModel,
+ modifier = Modifier.fillMaxWidth().weight(1f),
+ )
+ }
+
+ StandardLayout(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ modifier = Modifier.fillMaxWidth().weight(1f),
+ )
+ }
+}
+
+interface BouncerDialogFactory {
+ operator fun invoke(): AlertDialog
+}
+
+/** Enumerates all supported user-input area visibilities. */
+private enum class UserInputAreaVisibility {
+ /**
+ * Only the area where the user enters the input is shown; the area where the input is reflected
+ * back to the user is not shown.
+ */
+ INPUT_ONLY,
+ /**
+ * Only the area where the input is reflected back to the user is shown; the area where the
+ * input is entered by the user is not shown.
+ */
+ OUTPUT_ONLY,
+}
+
+/**
+ * Calculates an alpha for the user switcher and bouncer such that it's at `1` when the offset of
+ * the two reaches a stopping point but `0` in the middle of the transition.
+ */
+private fun animatedAlpha(
+ offset: Float,
+): Float {
+ // Describes a curve that is made of two parabolic U-shaped curves mirrored horizontally around
+ // the y-axis. The U on the left runs between x = -1 and x = 0 while the U on the right runs
+ // between x = 0 and x = 1.
+ //
+ // The minimum values of the curves are at -0.5 and +0.5.
+ //
+ // Both U curves are vertically scaled such that they reach the points (-1, 1) and (1, 1).
+ //
+ // Breaking it down, it's y = a×(|x|-m)²+b, where:
+ // x: the offset
+ // y: the alpha
+ // m: x-axis center of the parabolic curves, where the minima are.
+ // b: y-axis offset to apply to the entire curve so the animation spends more time with alpha =
+ // 0.
+ // a: amplitude to scale the parabolic curves to reach y = 1 at x = -1, x = 0, and x = +1.
+ val m = 0.5f
+ val b = -0.25
+ val a = (1 - b) / m.pow(2)
+
+ return max(0f, (a * (abs(offset) - m).pow(2) + b).toFloat())
+}
+
+private val SelectedUserImageSize = 190.dp
+private val UserSwitcherDropdownWidth = SelectedUserImageSize + 2 * 29.dp
+private val UserSwitcherDropdownHeight = 60.dp
+
+private object SceneKeys {
+ val ContiguousSceneKey = SceneKey("default")
+ val SplitSceneKey = SceneKey("split")
+}
+
+private object SceneElements {
+ val AboveFold = ElementKey("above_fold")
+ val BelowFold = ElementKey("below_fold")
+}
+
+private val SceneTransitions = transitions {
+ from(SceneKeys.ContiguousSceneKey, to = SceneKeys.SplitSceneKey) { spec = tween() }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 57af2ba..d638ffe 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -16,91 +16,22 @@
package com.android.systemui.bouncer.ui.composable
-import android.app.AlertDialog
-import android.app.Dialog
-import android.content.DialogInterface
-import androidx.compose.animation.Crossfade
-import androidx.compose.animation.core.animateFloatAsState
-import androidx.compose.animation.core.snap
-import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.combinedClickable
-import androidx.compose.foundation.gestures.detectTapGestures
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.aspectRatio
-import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.layout.widthIn
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.KeyboardArrowDown
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.DropdownMenu
-import androidx.compose.material3.DropdownMenuItem
-import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.asImageBitmap
-import androidx.compose.ui.graphics.graphicsLayer
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.DpOffset
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.times
-import com.android.compose.PlatformButton
import com.android.compose.animation.scene.ElementKey
-import com.android.compose.animation.scene.SceneKey as SceneTransitionLayoutSceneKey
import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.SceneTransitionLayout
-import com.android.compose.animation.scene.transitions
-import com.android.compose.modifiers.thenIf
-import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
-import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
-import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
-import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
-import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
-import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
-import com.android.systemui.common.shared.model.Text.Companion.loadText
-import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.fold.ui.composable.foldPosture
-import com.android.systemui.fold.ui.helper.FoldPosture
-import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.scene.shared.model.UserAction
import com.android.systemui.scene.ui.composable.ComposableScene
import javax.inject.Inject
-import kotlin.math.abs
-import kotlin.math.max
-import kotlin.math.pow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -118,7 +49,7 @@
@Inject
constructor(
private val viewModel: BouncerViewModel,
- private val dialogFactory: BouncerSceneDialogFactory,
+ private val dialogFactory: BouncerDialogFactory,
) : ComposableScene {
override val key = SceneKey.Bouncer
@@ -140,648 +71,22 @@
@Composable
private fun SceneScope.BouncerScene(
viewModel: BouncerViewModel,
- dialogFactory: BouncerSceneDialogFactory,
+ dialogFactory: BouncerDialogFactory,
modifier: Modifier = Modifier,
) {
val backgroundColor = MaterialTheme.colorScheme.surface
- val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsState()
- val layout = calculateLayout(isSideBySideSupported = isSideBySideSupported)
Box(modifier) {
Canvas(Modifier.element(Bouncer.Elements.Background).fillMaxSize()) {
drawRect(color = backgroundColor)
}
- val childModifier = Modifier.element(Bouncer.Elements.Content).fillMaxSize()
- val isFullScreenUserSwitcherEnabled = viewModel.isUserSwitcherVisible
-
- when (layout) {
- BouncerSceneLayout.STANDARD ->
- StandardLayout(
- viewModel = viewModel,
- dialogFactory = dialogFactory,
- modifier = childModifier,
- )
- BouncerSceneLayout.SIDE_BY_SIDE ->
- SideBySideLayout(
- viewModel = viewModel,
- dialogFactory = dialogFactory,
- isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
- modifier = childModifier,
- )
- BouncerSceneLayout.STACKED ->
- StackedLayout(
- viewModel = viewModel,
- dialogFactory = dialogFactory,
- isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
- modifier = childModifier,
- )
- BouncerSceneLayout.SPLIT ->
- SplitLayout(
- viewModel = viewModel,
- dialogFactory = dialogFactory,
- modifier = childModifier,
- )
- }
- }
-}
-
-/**
- * Renders the contents of the actual bouncer UI, the area that takes user input to do an
- * authentication attempt, including all messaging UI (directives, reasoning, errors, etc.).
- */
-@Composable
-private fun StandardLayout(
- viewModel: BouncerViewModel,
- dialogFactory: BouncerSceneDialogFactory,
- modifier: Modifier = Modifier,
- outputOnly: Boolean = false,
-) {
- val foldPosture: FoldPosture by foldPosture()
- val isSplitAroundTheFoldRequired by viewModel.isFoldSplitRequired.collectAsState()
- val isSplitAroundTheFold =
- foldPosture == FoldPosture.Tabletop && !outputOnly && isSplitAroundTheFoldRequired
- val currentSceneKey =
- if (isSplitAroundTheFold) SceneKeys.SplitSceneKey else SceneKeys.ContiguousSceneKey
-
- SceneTransitionLayout(
- currentScene = currentSceneKey,
- onChangeScene = {},
- transitions = SceneTransitions,
- modifier = modifier,
- ) {
- scene(SceneKeys.ContiguousSceneKey) {
- FoldSplittable(
- viewModel = viewModel,
- dialogFactory = dialogFactory,
- outputOnly = outputOnly,
- isSplit = false,
- )
- }
-
- scene(SceneKeys.SplitSceneKey) {
- FoldSplittable(
- viewModel = viewModel,
- dialogFactory = dialogFactory,
- outputOnly = outputOnly,
- isSplit = true,
- )
- }
- }
-}
-
-/**
- * Renders the "standard" layout of the bouncer, where the bouncer is rendered on its own (no user
- * switcher UI) and laid out vertically, centered horizontally.
- *
- * If [isSplit] is `true`, the top and bottom parts of the bouncer are split such that they don't
- * render across the location of the fold hardware when the device is fully or part-way unfolded
- * with the fold hinge in a horizontal position.
- *
- * If [outputOnly] is `true`, only the "output" part of the UI is shown (where the entered PIN
- * "shapes" appear), if `false`, the entire UI is shown, including the area where the user can enter
- * their PIN or pattern.
- */
-@Composable
-private fun SceneScope.FoldSplittable(
- viewModel: BouncerViewModel,
- dialogFactory: BouncerSceneDialogFactory,
- outputOnly: Boolean,
- isSplit: Boolean,
- modifier: Modifier = Modifier,
-) {
- val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
- val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
- var dialog: Dialog? by remember { mutableStateOf(null) }
- val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsState()
- val splitRatio =
- LocalContext.current.resources.getFloat(
- R.dimen.motion_layout_half_fold_bouncer_height_ratio
- )
-
- Column(modifier = modifier.padding(horizontal = 32.dp)) {
- // Content above the fold, when split on a foldable device in a "table top" posture:
- Box(
- modifier =
- Modifier.element(SceneElements.AboveFold).fillMaxWidth().thenIf(isSplit) {
- Modifier.weight(splitRatio)
- },
- ) {
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier.fillMaxWidth().padding(top = 92.dp),
- ) {
- Crossfade(
- targetState = message,
- label = "Bouncer message",
- animationSpec = if (message.isUpdateAnimated) tween() else snap(),
- ) { message ->
- Text(
- text = message.text,
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.bodyLarge,
- )
- }
-
- Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
-
- UserInputArea(
- viewModel = viewModel,
- visibility = UserInputAreaVisibility.OUTPUT_ONLY,
- )
- }
- }
-
- // Content below the fold, when split on a foldable device in a "table top" posture:
- Box(
- modifier =
- Modifier.element(SceneElements.BelowFold).fillMaxWidth().thenIf(isSplit) {
- Modifier.weight(1 - splitRatio)
- },
- ) {
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier.fillMaxWidth(),
- ) {
- if (!outputOnly) {
- Box(Modifier.weight(1f)) {
- UserInputArea(
- viewModel = viewModel,
- visibility = UserInputAreaVisibility.INPUT_ONLY,
- modifier = Modifier.align(Alignment.Center),
- )
- }
- }
-
- Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
-
- val actionButtonModifier = Modifier.height(56.dp)
-
- actionButton.let { actionButtonViewModel ->
- if (actionButtonViewModel != null) {
- BouncerActionButton(
- viewModel = actionButtonViewModel,
- modifier = actionButtonModifier,
- )
- } else {
- Spacer(modifier = actionButtonModifier)
- }
- }
-
- Spacer(Modifier.height(48.dp))
- }
- }
-
- if (dialogMessage != null) {
- if (dialog == null) {
- dialog =
- dialogFactory().apply {
- setMessage(dialogMessage)
- setButton(
- DialogInterface.BUTTON_NEUTRAL,
- context.getString(R.string.ok),
- ) { _, _ ->
- viewModel.onThrottlingDialogDismissed()
- }
- setCancelable(false)
- setCanceledOnTouchOutside(false)
- show()
- }
- }
- } else {
- dialog?.dismiss()
- dialog = null
- }
- }
-}
-
-/**
- * Renders the user input area, where the user interacts with the UI to enter their credentials.
- *
- * For example, this can be the pattern input area, the password text box, or pin pad.
- */
-@Composable
-private fun UserInputArea(
- viewModel: BouncerViewModel,
- visibility: UserInputAreaVisibility,
- modifier: Modifier = Modifier,
-) {
- val authMethodViewModel: AuthMethodBouncerViewModel? by
- viewModel.authMethodViewModel.collectAsState()
-
- when (val nonNullViewModel = authMethodViewModel) {
- is PinBouncerViewModel ->
- when (visibility) {
- UserInputAreaVisibility.OUTPUT_ONLY ->
- PinInputDisplay(
- viewModel = nonNullViewModel,
- modifier = modifier,
- )
- UserInputAreaVisibility.INPUT_ONLY ->
- PinPad(
- viewModel = nonNullViewModel,
- modifier = modifier,
- )
- }
- is PasswordBouncerViewModel ->
- if (visibility == UserInputAreaVisibility.INPUT_ONLY) {
- PasswordBouncer(
- viewModel = nonNullViewModel,
- modifier = modifier,
- )
- }
- is PatternBouncerViewModel ->
- if (visibility == UserInputAreaVisibility.INPUT_ONLY) {
- PatternBouncer(
- viewModel = nonNullViewModel,
- modifier = modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
- )
- }
- else -> Unit
- }
-}
-
-/**
- * Renders the action button on the bouncer, which triggers either Return to Call or Emergency Call.
- */
-@OptIn(ExperimentalFoundationApi::class)
-@Composable
-private fun BouncerActionButton(
- viewModel: BouncerActionButtonModel,
- modifier: Modifier = Modifier,
-) {
- Button(
- onClick = viewModel.onClick,
- modifier =
- modifier.thenIf(viewModel.onLongClick != null) {
- Modifier.combinedClickable(
- onClick = viewModel.onClick,
- onLongClick = viewModel.onLongClick,
- )
- },
- colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.tertiaryContainer,
- contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
- ),
- ) {
- Text(
- text = viewModel.label,
- style = MaterialTheme.typography.bodyMedium,
+ // Separate the bouncer content into a reusable composable that doesn't have any SceneScope
+ // dependencies
+ BouncerContent(
+ viewModel,
+ dialogFactory,
+ Modifier.element(Bouncer.Elements.Content).fillMaxSize()
)
}
}
-
-/** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
-@Composable
-private fun UserSwitcher(
- viewModel: BouncerViewModel,
- modifier: Modifier = Modifier,
-) {
- val selectedUserImage by viewModel.selectedUserImage.collectAsState(null)
- val dropdownItems by viewModel.userSwitcherDropdown.collectAsState(emptyList())
-
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center,
- modifier = modifier,
- ) {
- selectedUserImage?.let {
- Image(
- bitmap = it.asImageBitmap(),
- contentDescription = null,
- modifier = Modifier.size(SelectedUserImageSize),
- )
- }
-
- val (isDropdownExpanded, setDropdownExpanded) = remember { mutableStateOf(false) }
-
- dropdownItems.firstOrNull()?.let { firstDropdownItem ->
- Spacer(modifier = Modifier.height(40.dp))
-
- Box {
- PlatformButton(
- modifier =
- Modifier
- // Remove the built-in padding applied inside PlatformButton:
- .padding(vertical = 0.dp)
- .width(UserSwitcherDropdownWidth)
- .height(UserSwitcherDropdownHeight),
- colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.surfaceContainerHighest,
- contentColor = MaterialTheme.colorScheme.onSurface,
- ),
- onClick = { setDropdownExpanded(!isDropdownExpanded) },
- ) {
- val context = LocalContext.current
- Text(
- text = checkNotNull(firstDropdownItem.text.loadText(context)),
- style = MaterialTheme.typography.headlineSmall,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- )
-
- Spacer(modifier = Modifier.weight(1f))
-
- Icon(
- imageVector = Icons.Default.KeyboardArrowDown,
- contentDescription = null,
- modifier = Modifier.size(32.dp),
- )
- }
-
- UserSwitcherDropdownMenu(
- isExpanded = isDropdownExpanded,
- items = dropdownItems,
- onDismissed = { setDropdownExpanded(false) },
- )
- }
- }
- }
-}
-
-/**
- * Renders the dropdowm menu that displays the actual users and/or user actions that can be
- * selected.
- */
-@Composable
-private fun UserSwitcherDropdownMenu(
- isExpanded: Boolean,
- items: List<BouncerViewModel.UserSwitcherDropdownItemViewModel>,
- onDismissed: () -> Unit,
-) {
- val context = LocalContext.current
-
- // TODO(b/303071855): once the FR is fixed, remove this composition local override.
- MaterialTheme(
- colorScheme =
- MaterialTheme.colorScheme.copy(
- surface = MaterialTheme.colorScheme.surfaceContainerHighest,
- ),
- shapes = MaterialTheme.shapes.copy(extraSmall = RoundedCornerShape(28.dp)),
- ) {
- DropdownMenu(
- expanded = isExpanded,
- onDismissRequest = onDismissed,
- offset =
- DpOffset(
- x = 0.dp,
- y = -UserSwitcherDropdownHeight,
- ),
- modifier = Modifier.width(UserSwitcherDropdownWidth),
- ) {
- items.forEach { userSwitcherDropdownItem ->
- DropdownMenuItem(
- leadingIcon = {
- Icon(
- icon = userSwitcherDropdownItem.icon,
- tint = MaterialTheme.colorScheme.primary,
- modifier = Modifier.size(28.dp),
- )
- },
- text = {
- Text(
- text = checkNotNull(userSwitcherDropdownItem.text.loadText(context)),
- style = MaterialTheme.typography.bodyLarge,
- color = MaterialTheme.colorScheme.onSurface,
- )
- },
- onClick = {
- onDismissed()
- userSwitcherDropdownItem.onClick()
- },
- )
- }
- }
- }
-}
-
-/**
- * Renders the bouncer UI in split mode, with half on one side and half on the other side, swappable
- * by double-tapping on the side.
- */
-@Composable
-private fun SplitLayout(
- viewModel: BouncerViewModel,
- dialogFactory: BouncerSceneDialogFactory,
- modifier: Modifier = Modifier,
-) {
- SwappableLayout(
- startContent = { startContentModifier ->
- StandardLayout(
- viewModel = viewModel,
- dialogFactory = dialogFactory,
- outputOnly = true,
- modifier = startContentModifier,
- )
- },
- endContent = { endContentModifier ->
- UserInputArea(
- viewModel = viewModel,
- visibility = UserInputAreaVisibility.INPUT_ONLY,
- modifier = endContentModifier,
- )
- },
- modifier = modifier
- )
-}
-
-/**
- * Arranges the given two contents side-by-side, supporting a double tap anywhere on the background
- * to flip their positions.
- */
-@Composable
-private fun SwappableLayout(
- startContent: @Composable (Modifier) -> Unit,
- endContent: @Composable (Modifier) -> Unit,
- modifier: Modifier = Modifier,
-) {
- val layoutDirection = LocalLayoutDirection.current
- val isLeftToRight = layoutDirection == LayoutDirection.Ltr
- val (isSwapped, setSwapped) = rememberSaveable(isLeftToRight) { mutableStateOf(!isLeftToRight) }
-
- Row(
- modifier =
- modifier.pointerInput(Unit) {
- detectTapGestures(
- onDoubleTap = { offset ->
- // Depending on where the user double tapped, switch the elements such that
- // the endContent is closer to the side that was double tapped.
- setSwapped(offset.x < size.width / 2)
- }
- )
- },
- ) {
- val animatedOffset by
- animateFloatAsState(
- targetValue =
- if (!isSwapped) {
- // When startContent is first, both elements have their natural placement so
- // they are not offset in any way.
- 0f
- } else if (isLeftToRight) {
- // Since startContent is not first, the elements have to be swapped
- // horizontally. In the case of LTR locales, this means pushing startContent
- // to the right, hence the positive number.
- 1f
- } else {
- // Since startContent is not first, the elements have to be swapped
- // horizontally. In the case of RTL locales, this means pushing startContent
- // to the left, hence the negative number.
- -1f
- },
- label = "offset",
- )
-
- startContent(
- Modifier.fillMaxHeight().weight(1f).graphicsLayer {
- translationX = size.width * animatedOffset
- alpha = animatedAlpha(animatedOffset)
- }
- )
-
- Box(
- modifier =
- Modifier.fillMaxHeight().weight(1f).graphicsLayer {
- // A negative sign is used to make sure this is offset in the direction that's
- // opposite of the direction that the user switcher is pushed in.
- translationX = -size.width * animatedOffset
- alpha = animatedAlpha(animatedOffset)
- }
- ) {
- endContent(Modifier.widthIn(max = 400.dp).align(Alignment.BottomCenter))
- }
- }
-}
-
-/**
- * Arranges the bouncer contents and user switcher contents side-by-side, supporting a double tap
- * anywhere on the background to flip their positions.
- *
- * In situations when [isUserSwitcherVisible] is `false`, one of two things may happen: either the
- * UI for the bouncer will be shown on its own, taking up one side, with the other side just being
- * empty space or, if that kind of "stand-alone side-by-side" isn't supported, the standard
- * rendering of the bouncer will be used instead of the side-by-side layout.
- */
-@Composable
-private fun SideBySideLayout(
- viewModel: BouncerViewModel,
- dialogFactory: BouncerSceneDialogFactory,
- isUserSwitcherVisible: Boolean,
- modifier: Modifier = Modifier,
-) {
- SwappableLayout(
- startContent = { startContentModifier ->
- if (isUserSwitcherVisible) {
- UserSwitcher(
- viewModel = viewModel,
- modifier = startContentModifier,
- )
- } else {
- Box(
- modifier = startContentModifier,
- )
- }
- },
- endContent = { endContentModifier ->
- StandardLayout(
- viewModel = viewModel,
- dialogFactory = dialogFactory,
- modifier = endContentModifier,
- )
- },
- modifier = modifier,
- )
-}
-
-/** Arranges the bouncer contents and user switcher contents one on top of the other, vertically. */
-@Composable
-private fun StackedLayout(
- viewModel: BouncerViewModel,
- dialogFactory: BouncerSceneDialogFactory,
- isUserSwitcherVisible: Boolean,
- modifier: Modifier = Modifier,
-) {
- Column(
- modifier = modifier,
- ) {
- if (isUserSwitcherVisible) {
- UserSwitcher(
- viewModel = viewModel,
- modifier = Modifier.fillMaxWidth().weight(1f),
- )
- }
-
- StandardLayout(
- viewModel = viewModel,
- dialogFactory = dialogFactory,
- modifier = Modifier.fillMaxWidth().weight(1f),
- )
- }
-}
-
-interface BouncerSceneDialogFactory {
- operator fun invoke(): AlertDialog
-}
-
-/** Enumerates all supported user-input area visibilities. */
-private enum class UserInputAreaVisibility {
- /**
- * Only the area where the user enters the input is shown; the area where the input is reflected
- * back to the user is not shown.
- */
- INPUT_ONLY,
- /**
- * Only the area where the input is reflected back to the user is shown; the area where the
- * input is entered by the user is not shown.
- */
- OUTPUT_ONLY,
-}
-
-/**
- * Calculates an alpha for the user switcher and bouncer such that it's at `1` when the offset of
- * the two reaches a stopping point but `0` in the middle of the transition.
- */
-private fun animatedAlpha(
- offset: Float,
-): Float {
- // Describes a curve that is made of two parabolic U-shaped curves mirrored horizontally around
- // the y-axis. The U on the left runs between x = -1 and x = 0 while the U on the right runs
- // between x = 0 and x = 1.
- //
- // The minimum values of the curves are at -0.5 and +0.5.
- //
- // Both U curves are vertically scaled such that they reach the points (-1, 1) and (1, 1).
- //
- // Breaking it down, it's y = a×(|x|-m)²+b, where:
- // x: the offset
- // y: the alpha
- // m: x-axis center of the parabolic curves, where the minima are.
- // b: y-axis offset to apply to the entire curve so the animation spends more time with alpha =
- // 0.
- // a: amplitude to scale the parabolic curves to reach y = 1 at x = -1, x = 0, and x = +1.
- val m = 0.5f
- val b = -0.25
- val a = (1 - b) / m.pow(2)
-
- return max(0f, (a * (abs(offset) - m).pow(2) + b).toFloat())
-}
-
-private val SelectedUserImageSize = 190.dp
-private val UserSwitcherDropdownWidth = SelectedUserImageSize + 2 * 29.dp
-private val UserSwitcherDropdownHeight = 60.dp
-
-private object SceneKeys {
- val ContiguousSceneKey = SceneTransitionLayoutSceneKey("default")
- val SplitSceneKey = SceneTransitionLayoutSceneKey("split")
-}
-
-private object SceneElements {
- val AboveFold = ElementKey("above_fold")
- val BelowFold = ElementKey("below_fold")
-}
-
-private val SceneTransitions = transitions {
- from(SceneKeys.ContiguousSceneKey, to = SceneKeys.SplitSceneKey) { spec = tween() }
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
index 0b13383..eb06889 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
@@ -16,12 +16,10 @@
package com.android.systemui.bouncer.ui.composable
+import android.view.ViewTreeObserver
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.imeAnimationTarget
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.LocalTextStyle
@@ -30,46 +28,56 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
+import androidx.core.view.WindowInsetsCompat
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
/** UI for the input part of a password-requiring version of the bouncer. */
-@OptIn(ExperimentalLayoutApi::class)
@Composable
internal fun PasswordBouncer(
viewModel: PasswordBouncerViewModel,
modifier: Modifier = Modifier,
) {
val focusRequester = remember { FocusRequester() }
+ val isTextFieldFocusRequested by viewModel.isTextFieldFocusRequested.collectAsState()
+ LaunchedEffect(isTextFieldFocusRequested) {
+ if (isTextFieldFocusRequested) {
+ focusRequester.requestFocus()
+ }
+ }
+ val (isTextFieldFocused, onTextFieldFocusChanged) = remember { mutableStateOf(false) }
+ LaunchedEffect(isTextFieldFocused) {
+ viewModel.onTextFieldFocusChanged(isFocused = isTextFieldFocused)
+ }
+
val password: String by viewModel.password.collectAsState()
val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
- val density = LocalDensity.current
- val isImeVisible by rememberUpdatedState(WindowInsets.imeAnimationTarget.getBottom(density) > 0)
+ val isImeVisible by isSoftwareKeyboardVisible()
LaunchedEffect(isImeVisible) { viewModel.onImeVisibilityChanged(isImeVisible) }
DisposableEffect(Unit) {
viewModel.onShown()
-
- // When the UI comes up, request focus on the TextField to bring up the software keyboard.
- focusRequester.requestFocus()
-
onDispose { viewModel.onHidden() }
}
@@ -104,16 +112,39 @@
onDone = { viewModel.onAuthenticateKeyPressed() },
),
modifier =
- Modifier.focusRequester(focusRequester).drawBehind {
- drawLine(
- color = color,
- start = Offset(x = 0f, y = size.height - lineWidthPx),
- end = Offset(size.width, y = size.height - lineWidthPx),
- strokeWidth = lineWidthPx,
- )
- },
+ Modifier.focusRequester(focusRequester)
+ .onFocusChanged { onTextFieldFocusChanged(it.isFocused) }
+ .drawBehind {
+ drawLine(
+ color = color,
+ start = Offset(x = 0f, y = size.height - lineWidthPx),
+ end = Offset(size.width, y = size.height - lineWidthPx),
+ strokeWidth = lineWidthPx,
+ )
+ },
)
Spacer(Modifier.height(100.dp))
}
}
+
+/** Returns a [State] with `true` when the IME/keyboard is visible and `false` when it's not. */
+@Composable
+fun isSoftwareKeyboardVisible(): State<Boolean> {
+ val view = LocalView.current
+ val viewTreeObserver = view.viewTreeObserver
+
+ return produceState(
+ initialValue = false,
+ key1 = viewTreeObserver,
+ ) {
+ val listener =
+ ViewTreeObserver.OnGlobalLayoutListener {
+ value = view.rootWindowInsets?.isVisible(WindowInsetsCompat.Type.ime()) ?: false
+ }
+
+ viewTreeObserver.addOnGlobalLayoutListener(listener)
+
+ awaitDispose { viewTreeObserver.removeOnGlobalLayoutListener(listener) }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
index fab290d..7b2ac90 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.tiles.impl.flashlight.domain
-import android.graphics.drawable.Drawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -26,7 +25,6 @@
import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
-import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import org.junit.Test
@@ -37,7 +35,7 @@
class FlashlightMapperTest : SysuiTestCase() {
private val kosmos = Kosmos()
private val qsTileConfig = kosmos.qsFlashlightTileConfig
- private val mapper by lazy { FlashlightMapper(context) }
+ private val mapper by lazy { FlashlightMapper(context.orCreateTestableResources.resources) }
@Test
fun mapsDisabledDataToInactiveState() {
@@ -58,12 +56,7 @@
@Test
fun mapsEnabledDataToOnIconState() {
- val fakeDrawable = mock<Drawable>()
- context.orCreateTestableResources.addOverride(
- R.drawable.qs_flashlight_icon_on,
- fakeDrawable
- )
- val expectedIcon = Icon.Loaded(fakeDrawable, null)
+ val expectedIcon = Icon.Resource(R.drawable.qs_flashlight_icon_on, null)
val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(true))
@@ -73,12 +66,7 @@
@Test
fun mapsDisabledDataToOffIconState() {
- val fakeDrawable = mock<Drawable>()
- context.orCreateTestableResources.addOverride(
- R.drawable.qs_flashlight_icon_off,
- fakeDrawable
- )
- val expectedIcon = Icon.Loaded(fakeDrawable, null)
+ val expectedIcon = Icon.Resource(R.drawable.qs_flashlight_icon_off, null)
val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(false))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
index 820c056..8791877 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.tiles.impl.location.domain
-import android.graphics.drawable.Drawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -26,7 +25,6 @@
import com.android.systemui.qs.tiles.impl.location.qsLocationTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
-import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth
import junit.framework.Assert
import org.junit.Test
@@ -37,7 +35,8 @@
class LocationTileMapperTest : SysuiTestCase() {
private val kosmos = Kosmos()
private val qsTileConfig = kosmos.qsLocationTileConfig
- private val mapper by lazy { LocationTileMapper(context) }
+
+ private val mapper by lazy { LocationTileMapper(context.orCreateTestableResources.resources) }
@Test
fun mapsDisabledDataToInactiveState() {
@@ -57,9 +56,7 @@
@Test
fun mapsEnabledDataToOnIconState() {
- val fakeDrawable = mock<Drawable>()
- context.orCreateTestableResources.addOverride(R.drawable.qs_location_icon_on, fakeDrawable)
- val expectedIcon = Icon.Loaded(fakeDrawable, null)
+ val expectedIcon = Icon.Resource(R.drawable.qs_location_icon_on, null)
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(true))
@@ -69,9 +66,7 @@
@Test
fun mapsDisabledDataToOffIconState() {
- val fakeDrawable = mock<Drawable>()
- context.orCreateTestableResources.addOverride(R.drawable.qs_location_icon_off, fakeDrawable)
- val expectedIcon = Icon.Loaded(fakeDrawable, null)
+ val expectedIcon = Icon.Resource(R.drawable.qs_location_icon_off, null)
val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(false))
diff --git a/packages/SystemUI/res/anim/instant_fade_out.xml b/packages/SystemUI/res/anim/instant_fade_out.xml
new file mode 100644
index 0000000..800420b
--- /dev/null
+++ b/packages/SystemUI/res/anim/instant_fade_out.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 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.
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:interpolator="@android:interpolator/linear_out_slow_in"
+ android:duration="0"/>
+
diff --git a/packages/SystemUI/res/drawable/ksh_key_item_background.xml b/packages/SystemUI/res/drawable/ksh_key_item_background.xml
index 75ff30d..1db48fa 100644
--- a/packages/SystemUI/res/drawable/ksh_key_item_background.xml
+++ b/packages/SystemUI/res/drawable/ksh_key_item_background.xml
@@ -17,5 +17,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/ksh_key_item_background" />
- <corners android:radius="2dp" />
+ <corners android:radius="8dp" />
</shape>
diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml
index 9c08f5e..355e75d 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -18,7 +18,7 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:color="?android:attr/colorControlHighlight">
- <item android:id="@+id/notification_background_color_layer">
+ <item>
<shape>
<solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
</shape>
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
index af29cad..50241cd 100644
--- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -111,107 +111,57 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bluetooth_toggle"
- app:layout_constraintBottom_toTopOf="@+id/see_all_text" />
+ app:layout_constraintBottom_toTopOf="@+id/see_all_button" />
- <androidx.constraintlayout.widget.Group
- android:id="@+id/see_all_layout_group"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- app:constraint_referenced_ids="ic_arrow,see_all_text" />
-
- <View
- android:id="@+id/see_all_clickable_row"
+ <Button
+ android:id="@+id/see_all_button"
+ style="@style/BluetoothTileDialog.Device"
+ android:paddingEnd="0dp"
+ android:paddingStart="20dp"
+ android:background="@drawable/bluetooth_tile_dialog_bg_off"
android:layout_width="0dp"
- android:layout_height="0dp"
+ android:layout_height="64dp"
android:contentDescription="@string/accessibility_bluetooth_device_settings_see_all"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/device_list"
- app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text" />
-
- <ImageView
- android:id="@+id/ic_arrow"
- android:layout_marginStart="36dp"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:importantForAccessibility="no"
- android:gravity="center_vertical"
- android:src="@drawable/ic_arrow_forward"
- app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/see_all_text"
- app:layout_constraintTop_toBottomOf="@id/device_list" />
-
- <TextView
- android:id="@+id/see_all_text"
- style="@style/BluetoothTileDialog.Device"
- android:layout_width="0dp"
- android:layout_height="64dp"
- android:maxLines="1"
- android:ellipsize="end"
- android:gravity="center_vertical"
- android:importantForAccessibility="no"
- android:clickable="false"
- android:layout_marginStart="0dp"
- android:paddingStart="20dp"
+ app:layout_constraintBottom_toTopOf="@+id/pair_new_device_button"
+ android:drawableStart="@drawable/ic_arrow_forward"
+ android:drawablePadding="20dp"
+ android:drawableTint="?android:attr/textColorPrimary"
android:text="@string/see_all_bluetooth_devices"
android:textSize="14sp"
android:textAppearance="@style/TextAppearance.Dialog.Title"
- app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text"
- app:layout_constraintStart_toEndOf="@+id/ic_arrow"
- app:layout_constraintTop_toBottomOf="@id/device_list"
- app:layout_constraintEnd_toEndOf="parent" />
+ android:textDirection="locale"
+ android:textAlignment="viewStart"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:visibility="gone" />
- <androidx.constraintlayout.widget.Group
- android:id="@+id/pair_new_device_layout_group"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- app:constraint_referenced_ids="ic_add,pair_new_device_text" />
-
- <View
- android:id="@+id/pair_new_device_clickable_row"
+ <Button
+ android:id="@+id/pair_new_device_button"
+ style="@style/BluetoothTileDialog.Device"
+ android:paddingEnd="0dp"
+ android:paddingStart="20dp"
+ android:background="@drawable/bluetooth_tile_dialog_bg_off"
android:layout_width="0dp"
- android:layout_height="0dp"
+ android:layout_height="64dp"
android:contentDescription="@string/accessibility_bluetooth_device_settings_pair_new_device"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/see_all_text"
- app:layout_constraintBottom_toTopOf="@+id/done_button" />
-
- <ImageView
- android:id="@+id/ic_add"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_marginStart="36dp"
- android:gravity="center_vertical"
- android:importantForAccessibility="no"
- android:src="@drawable/ic_add"
- app:layout_constraintBottom_toTopOf="@id/done_button"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/pair_new_device_text"
- app:layout_constraintTop_toBottomOf="@id/see_all_text"
- android:tint="?android:attr/textColorPrimary" />
-
- <TextView
- android:id="@+id/pair_new_device_text"
- style="@style/BluetoothTileDialog.Device"
- android:layout_width="0dp"
- android:layout_height="64dp"
- android:maxLines="1"
- android:ellipsize="end"
- android:gravity="center_vertical"
- android:importantForAccessibility="no"
- android:clickable="false"
- android:layout_marginStart="0dp"
- android:paddingStart="20dp"
+ app:layout_constraintTop_toBottomOf="@+id/see_all_button"
+ app:layout_constraintBottom_toTopOf="@+id/done_button"
+ android:drawableStart="@drawable/ic_add"
+ android:drawablePadding="20dp"
+ android:drawableTint="?android:attr/textColorPrimary"
android:text="@string/pair_new_bluetooth_devices"
android:textSize="14sp"
android:textAppearance="@style/TextAppearance.Dialog.Title"
- app:layout_constraintStart_toEndOf="@+id/ic_add"
- app:layout_constraintTop_toBottomOf="@id/see_all_text"
- app:layout_constraintEnd_toEndOf="parent" />
+ android:textDirection="locale"
+ android:textAlignment="viewStart"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:visibility="gone" />
<Button
android:id="@+id/done_button"
@@ -227,7 +177,7 @@
android:maxLines="1"
android:text="@string/inline_done_button"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pair_new_device_text"
+ app:layout_constraintTop_toBottomOf="@id/pair_new_device_button"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
index fcf9638..a005100 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
@@ -22,8 +22,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
- android:paddingStart="24dp"
- android:paddingEnd="24dp"
android:paddingBottom="8dp">
<ImageView
android:id="@+id/keyboard_shortcuts_icon"
@@ -57,5 +55,6 @@
android:layout_alignParentEnd="true"
android:textSize="14sp"
android:scrollHorizontally="false"
- android:layout_centerVertical="true"/>
+ android:layout_centerVertical="true"
+ android:padding="0dp" />
</com.android.systemui.statusbar.KeyboardShortcutAppItemLayout>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
index 0759990..4f100f6 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
@@ -21,7 +21,5 @@
android:textSize="14sp"
android:fontFamily="sans-serif-medium"
android:importantForAccessibility="yes"
- android:paddingStart="24dp"
android:paddingTop="20dp"
- android:paddingEnd="24dp"
android:paddingBottom="10dp"/>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
index a3901d0..f96edbf 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
@@ -18,7 +18,12 @@
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="@dimen/ksh_item_padding"
+ android:minWidth="48dp"
+ android:minHeight="32dp"
+ android:paddingLeft="@dimen/ksh_key_view_padding_horizontal"
+ android:paddingRight="@dimen/ksh_key_view_padding_horizontal"
+ android:paddingTop="@dimen/ksh_key_view_padding_vertical"
+ android:paddingBottom="@dimen/ksh_key_view_padding_vertical"
android:layout_marginStart="@dimen/ksh_item_margin_start"
- android:scaleType="fitXY"
+ android:scaleType="matrix"
android:background="@drawable/ksh_key_item_background" />
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_icon_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_icon_view.xml
deleted file mode 100644
index a037cb2..0000000
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_icon_view.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="@dimen/ksh_item_padding"
- android:layout_marginLeft="0dp"
- android:layout_marginRight="0dp"
- android:scaleType="fitXY"
- android:tint="?android:attr/textColorPrimary"
- style="@style/ShortcutItemBackground" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_view.xml
deleted file mode 100644
index 12b4e15..0000000
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_new_view.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="@dimen/ksh_item_padding"
- android:layout_marginStart="@dimen/ksh_item_margin_start"
- style="@style/ShortcutItemBackground"
- android:textColor="?android:attr/textColorPrimary"
- android:singleLine="false"
- android:gravity="center"
- android:textSize="@dimen/ksh_item_text_size"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_plus_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_plus_view.xml
deleted file mode 100644
index 727f2c1..0000000
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_plus_view.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="@dimen/ksh_item_padding"
- android:layout_marginLeft="0dp"
- android:layout_marginRight="0dp"
- android:text="+"
- style="@style/ShortcutItemBackground"
- android:textColor="?android:attr/textColorPrimary"
- android:singleLine="true"
- android:gravity="center"
- android:textSize="@dimen/ksh_item_text_size"
- android:textAllCaps="true"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_separator_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_separator_view.xml
new file mode 100644
index 0000000..8772a73
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_separator_view.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minHeight="32dp"
+ android:padding="@dimen/ksh_item_padding"
+ android:layout_marginLeft="0dp"
+ android:layout_marginRight="0dp"
+ android:text="@string/keyboard_shortcut_join"
+ android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:singleLine="true"
+ android:gravity="center"
+ android:textSize="@dimen/ksh_item_text_size" />
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_vertical_bar_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_vertical_bar_view.xml
deleted file mode 100644
index 00ef947..0000000
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_vertical_bar_view.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="@dimen/ksh_item_padding"
- android:layout_marginLeft="0dp"
- android:layout_marginRight="0dp"
- android:text="|"
- style="@style/ShortcutItemBackground"
- android:textColor="?android:attr/textColorPrimary"
- android:singleLine="true"
- android:gravity="center"
- android:textSize="@dimen/ksh_item_text_size"
- android:textAllCaps="true"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
index b06f7fc..42bbf25 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
@@ -14,14 +14,18 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="@dimen/ksh_item_padding"
- android:layout_marginStart="@dimen/ksh_item_margin_start"
- android:background="@drawable/ksh_key_item_background"
- android:textColor="@color/ksh_key_item_color"
- android:singleLine="true"
- android:gravity="center"
- android:textSize="@dimen/ksh_item_text_size"
- android:textAllCaps="true"/>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="32dp"
+ android:minHeight="32dp"
+ android:paddingLeft="@dimen/ksh_key_view_padding_horizontal"
+ android:paddingRight="@dimen/ksh_key_view_padding_horizontal"
+ android:paddingTop="@dimen/ksh_key_view_padding_vertical"
+ android:paddingBottom="@dimen/ksh_key_view_padding_vertical"
+ android:layout_marginStart="@dimen/ksh_item_margin_start"
+ android:background="@drawable/ksh_key_item_background"
+ android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:singleLine="true"
+ android:gravity="center"
+ android:textSize="@dimen/ksh_item_text_size" />
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
index dbcd263..61f69c0 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_search_view.xml
@@ -70,17 +70,14 @@
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginStart="49dp"
+ android:layout_marginEnd="0dp"
android:scrollbars="none">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
- <View
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginStart="37dp"/>
-
<Button
android:id="@+id/shortcut_system"
android:layout_width="wrap_content"
@@ -116,6 +113,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
+ android:layout_marginStart="49dp"
+ android:layout_marginEnd="49dp"
android:layout_gravity="center_horizontal"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorPrimary"
@@ -126,8 +125,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
- android:layout_marginStart="25dp"
- android:layout_marginEnd="25dp">
+ android:layout_marginStart="49dp"
+ android:layout_marginEnd="49dp"
+ android:overScrollMode="never"
+ android:layout_marginBottom="16dp">
<LinearLayout
android:id="@+id/keyboard_shortcuts_container"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
index 7aba1cf..9e54ab1 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
@@ -22,6 +22,8 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginStart="24dp"
+ android:layout_marginEnd="24dp"
android:orientation="vertical">
<ScrollView
android:id="@+id/keyboard_shortcuts_scroll_view"
diff --git a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
index 130472d..02c8c3a 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
@@ -17,7 +17,8 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="250dp"
- android:layout_height="48dp"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp"
android:orientation="vertical"
android:padding="12dp">
<TextView
diff --git a/packages/SystemUI/res/layout/screen_record_options.xml b/packages/SystemUI/res/layout/screen_record_options.xml
index d9f4b79..8916e42 100644
--- a/packages/SystemUI/res/layout/screen_record_options.xml
+++ b/packages/SystemUI/res/layout/screen_record_options.xml
@@ -47,6 +47,7 @@
android:layout_weight="0"
android:layout_gravity="end"
android:id="@+id/screenrecord_audio_switch"
+ android:contentDescription="@string/screenrecord_audio_label"
style="@style/ScreenRecord.Switch"
android:importantForAccessibility="yes"/>
</LinearLayout>
@@ -79,6 +80,7 @@
android:minWidth="48dp"
android:layout_height="48dp"
android:id="@+id/screenrecord_taps_switch"
+ android:contentDescription="@string/screenrecord_taps_label"
style="@style/ScreenRecord.Switch"
android:importantForAccessibility="yes"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 0620355..e124aa0 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -119,9 +119,8 @@
<!-- Keyboard shortcuts colors -->
<color name="ksh_application_group_color">#fff44336</color>
- <color name="ksh_key_item_color">@color/material_grey_600</color>
- <color name="ksh_key_item_background">@color/material_grey_100</color>
- <color name="ksh_key_item_new_background">@color/transparent</color>
+ <color name="ksh_key_item_color">?androidprv:attr/materialColorOnSurfaceVariant</color>
+ <color name="ksh_key_item_background">?androidprv:attr/materialColorSurfaceContainerHighest</color>
<color name="instant_apps_color">#ff4d5a64</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 30392d2..03960d5 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -940,8 +940,11 @@
<!-- Keyboard shortcuts helper -->
<dimen name="ksh_layout_width">@dimen/match_parent</dimen>
<dimen name="ksh_item_text_size">14sp</dimen>
- <dimen name="ksh_item_padding">4dp</dimen>
+ <dimen name="ksh_item_padding">0dp</dimen>
<dimen name="ksh_item_margin_start">4dp</dimen>
+ <dimen name="ksh_icon_scaled_size">18dp</dimen>
+ <dimen name="ksh_key_view_padding_vertical">4dp</dimen>
+ <dimen name="ksh_key_view_padding_horizontal">12dp</dimen>
<!-- The size of corner radius of the arrow in the onboarding toast. -->
<dimen name="recents_onboarding_toast_arrow_corner_radius">2dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index daf6cb3..f49d2a1 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1773,13 +1773,13 @@
<!-- Name used to refer to the "Back" key on the keyboard. -->
<string name="keyboard_key_back">Back</string>
<!-- Name used to refer to the "Up" arrow key on the keyboard. -->
- <string name="keyboard_key_dpad_up">Up</string>
+ <string name="keyboard_key_dpad_up">Up arrow</string>
<!-- Name used to refer to the "Down" arrow key on the keyboard. -->
- <string name="keyboard_key_dpad_down">Down</string>
+ <string name="keyboard_key_dpad_down">Down arrow</string>
<!-- Name used to refer to the "Left" arrow key on the keyboard. -->
- <string name="keyboard_key_dpad_left">Left</string>
+ <string name="keyboard_key_dpad_left">Left arrow</string>
<!-- Name used to refer to the "Right" arrow key on the keyboard. -->
- <string name="keyboard_key_dpad_right">Right</string>
+ <string name="keyboard_key_dpad_right">Right arrow</string>
<!-- Name used to refer to the "Center" arrow key on the keyboard. -->
<string name="keyboard_key_dpad_center">Center</string>
<!-- Name used to refer to the "Tab" key on the keyboard. -->
@@ -1834,9 +1834,11 @@
<string name="keyboard_shortcut_group_system_shortcuts_helper">Keyboard Shortcuts</string>
<!-- User visible title for the keyboard shortcut that switches to the next hardware keyboard layout. -->
<string name="keyboard_shortcut_group_system_switch_input">Switch keyboard layout</string>
+ <!-- User visible string that joins different shortcuts in a list, e.g. shortcut1 "or" shortcut2 "or" ... -->
+ <string name="keyboard_shortcut_join">or</string>
- <!-- Content description for the clear text button in shortcut search list. [CHAR LIMIT=NONE] -->
- <string name="keyboard_shortcut_clear_text">Clear text</string>
+ <!-- Content description for the clear search button in shortcut search list. [CHAR LIMIT=NONE] -->
+ <string name="keyboard_shortcut_clear_text">Clear search query</string>
<!-- The title for keyboard shortcut search list [CHAR LIMIT=25] -->
<string name="keyboard_shortcut_search_list_title">Shortcuts</string>
<!-- The hint for keyboard shortcut search list [CHAR LIMIT=25] -->
@@ -1852,52 +1854,63 @@
<!-- The title of current app category in shortcut search list. [CHAR LIMIT=25] -->
<string name="keyboard_shortcut_search_category_current_app">Current app</string>
+ <!-- A11y message when showing keyboard shortcut search results. [CHAR LIMT=NONE] -->
+ <string name="keyboard_shortcut_a11y_show_search_results">Showing search results</string>
+ <!-- A11y message when filtering to "system" keyboard shortcuts. [CHAR LIMT=NONE] -->
+ <string name="keyboard_shortcut_a11y_filter_system">Showing system shortcuts</string>
+ <!-- A11y message when filtering to "input" keyboard shortcuts. [CHAR LIMT=NONE] -->
+ <string name="keyboard_shortcut_a11y_filter_input">Showing input shortcuts</string>
+ <!-- A11y message when filtering to "app opening" keyboard shortcuts. [CHAR LIMT=NONE] -->
+ <string name="keyboard_shortcut_a11y_filter_open_apps">Showing shortcuts that open apps</string>
+ <!-- A11y message when filtering to "current app" keyboard shortcuts. [CHAR LIMT=NONE] -->
+ <string name="keyboard_shortcut_a11y_filter_current_app">Showing shortcuts for the current app</string>
+
<!-- User visible title for the keyboard shortcut that triggers the notification shade. [CHAR LIMIT=70] -->
- <string name="group_system_access_notification_shade">Access notification shade</string>
+ <string name="group_system_access_notification_shade">View notifications</string>
<!-- User visible title for the keyboard shortcut that takes a full screenshot. [CHAR LIMIT=70] -->
- <string name="group_system_full_screenshot">Take a full screenshot</string>
+ <string name="group_system_full_screenshot">Take screenshot</string>
<!-- User visible title for the keyboard shortcut that access list of system / apps shortcuts. [CHAR LIMIT=70] -->
- <string name="group_system_access_system_app_shortcuts">Access list of system / apps shortcuts</string>
+ <string name="group_system_access_system_app_shortcuts">Show shortcuts</string>
<!-- User visible title for the keyboard shortcut that goes back to previous state. [CHAR LIMIT=70] -->
- <string name="group_system_go_back">Back: go back to previous state (back button)</string>
+ <string name="group_system_go_back">Go back</string>
<!-- User visible title for the keyboard shortcut that takes the user to the home screen. [CHAR LIMIT=70] -->
- <string name="group_system_access_home_screen">Access home screen</string>
+ <string name="group_system_access_home_screen">Go to home screen</string>
<!-- User visible title for the keyboard shortcut that triggers overview of open apps. [CHAR LIMIT=70] -->
- <string name="group_system_overview_open_apps">Overview of open apps</string>
+ <string name="group_system_overview_open_apps">View recent apps</string>
<!-- User visible title for the keyboard shortcut that cycles through recent apps (forward). [CHAR LIMIT=70] -->
- <string name="group_system_cycle_forward">Cycle through recent apps (forward)</string>
+ <string name="group_system_cycle_forward">Cycle forward through recent apps</string>
<!-- User visible title for the keyboard shortcut that cycles through recent apps (back). [CHAR LIMIT=70] -->
- <string name="group_system_cycle_back">Cycle through recent apps (back)</string>
+ <string name="group_system_cycle_back">Cycle backward through recent apps</string>
<!-- User visible title for the keyboard shortcut that accesses list of all apps and search. [CHAR LIMIT=70] -->
- <string name="group_system_access_all_apps_search">Access list of all apps and search (i.e. Search/Launcher)</string>
+ <string name="group_system_access_all_apps_search">Open apps list</string>
<!-- User visible title for the keyboard shortcut that hides and (re)showes taskbar. [CHAR LIMIT=70] -->
- <string name="group_system_hide_reshow_taskbar">Hide and (re)show taskbar</string>
- <!-- User visible title for the keyboard shortcut that accesses system settings. [CHAR LIMIT=70] -->
- <string name="group_system_access_system_settings">Access system settings</string>
- <!-- User visible title for the keyboard shortcut that accesses Google Assistant. [CHAR LIMIT=70] -->
- <string name="group_system_access_google_assistant">Access Google Assistant</string>
+ <string name="group_system_hide_reshow_taskbar">Show taskbar</string>
+ <!-- User visible title for the keyboard shortcut that accesses [system] settings. [CHAR LIMIT=70] -->
+ <string name="group_system_access_system_settings">Open settings</string>
+ <!-- User visible title for the keyboard shortcut that accesses Assistant app. [CHAR LIMIT=70] -->
+ <string name="group_system_access_google_assistant">Open assistant</string>
<!-- User visible title for the keyboard shortcut that locks screen. [CHAR LIMIT=70] -->
<string name="group_system_lock_screen">Lock screen</string>
<!-- User visible title for the keyboard shortcut that pulls up Notes app for quick memo. [CHAR LIMIT=70] -->
- <string name="group_system_quick_memo">Pull up Notes app for quick memo</string>
+ <string name="group_system_quick_memo">Open notes</string>
<!-- User visible title for the system multitasking keyboard shortcuts list. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_system_multitasking">System multitasking</string>
<!-- User visible title for the keyboard shortcut that enters split screen with current app to RHS [CHAR LIMIT=70] -->
- <string name="system_multitasking_rhs">Enter Split screen with current app to RHS</string>
+ <string name="system_multitasking_rhs">Enter split screen with current app to RHS</string>
<!-- User visible title for the keyboard shortcut that enters split screen with current app to LHS [CHAR LIMIT=70] -->
- <string name="system_multitasking_lhs">Enter Split screen with current app to LHS</string>
+ <string name="system_multitasking_lhs">Enter split screen with current app to LHS</string>
<!-- User visible title for the keyboard shortcut that switches from split screen to full screen [CHAR LIMIT=70] -->
- <string name="system_multitasking_full_screen">Switch from Split screen to full screen</string>
+ <string name="system_multitasking_full_screen">Switch from split screen to full screen</string>
<!-- User visible title for the keyboard shortcut that replaces an app from one to another during split screen [CHAR LIMIT=70] -->
- <string name="system_multitasking_replace">During Split screen: replace an app from one to another</string>
+ <string name="system_multitasking_replace">During split screen: replace an app from one to another</string>
<!-- User visible title for the input keyboard shortcuts list. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_input">Input</string>
<!-- User visible title for the keyboard shortcut that switches input language (next language). [CHAR LIMIT=70] -->
- <string name="input_switch_input_language_next">Switch input language (next language)</string>
+ <string name="input_switch_input_language_next">Switch to next language</string>
<!-- User visible title for the keyboard shortcut that switches input language (previous language). [CHAR LIMIT=70] -->
- <string name="input_switch_input_language_previous">Switch input language (previous language)</string>
+ <string name="input_switch_input_language_previous">Switch to previous language</string>
<!-- User visible title for the keyboard shortcut that accesses emoji. [CHAR LIMIT=70] -->
<string name="input_access_emoji">Access emoji</string>
<!-- User visible title for the keyboard shortcut that accesses voice typing. [CHAR LIMIT=70] -->
@@ -1905,14 +1918,14 @@
<!-- User visible title for the system-wide applications keyboard shortcuts list. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_applications">Applications</string>
- <!-- User visible title for the keyboard shortcut that takes the user to the assist app. [CHAR LIMIT=70] -->
- <string name="keyboard_shortcut_group_applications_assist">Assist</string>
+ <!-- User visible title for the keyboard shortcut that takes the user to the assistant app. [CHAR LIMIT=70] -->
+ <string name="keyboard_shortcut_group_applications_assist">Assistant</string>
<!-- User visible title for the keyboard shortcut that takes the user to the browser app. [CHAR LIMIT=70] -->
- <string name="keyboard_shortcut_group_applications_browser">Browser (Chrome as default)</string>
+ <string name="keyboard_shortcut_group_applications_browser">Browser</string>
<!-- User visible title for the keyboard shortcut that takes the user to the contacts app. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_applications_contacts">Contacts</string>
<!-- User visible title for the keyboard shortcut that takes the user to the email app. [CHAR LIMIT=70] -->
- <string name="keyboard_shortcut_group_applications_email">Email (Gmail as default)</string>
+ <string name="keyboard_shortcut_group_applications_email">Email</string>
<!-- User visible title for the keyboard shortcut that takes the user to the SMS messaging app. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_applications_sms">SMS</string>
<!-- User visible title for the keyboard shortcut that takes the user to the music app. [CHAR LIMIT=70] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index befee2b..c48dd9d 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -420,6 +420,11 @@
<!-- Cannot double inherit. Use Theme.SystemUI.QuickSettings in code to match -->
<style name="BrightnessDialog" parent="@android:style/Theme.DeviceDefault.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowAnimationStyle">@style/Animation.BrightnessDialog</item>
+ </style>
+
+ <style name="Animation.BrightnessDialog">
+ <item name="android:windowExitAnimation">@anim/instant_fade_out</item>
</style>
<style name="Theme.SystemUI.ContrastDialog" parent="@android:style/Theme.DeviceDefault.Dialog">
@@ -1513,10 +1518,6 @@
<item name="android:background">?android:attr/dividerHorizontal</item>
</style>
- <style name="ShortcutItemBackground">
- <item name="android:background">@color/ksh_key_item_new_background</item>
- </style>
-
<style name="LongPressLockScreenAnimation">
<item name="android:windowEnterAnimation">@anim/long_press_lock_screen_popup_enter</item>
<item name="android:windowExitAnimation">@anim/long_press_lock_screen_popup_exit</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt
index c9e57b4..b33f6fa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt
@@ -32,8 +32,7 @@
override val isHomeActivity: Boolean?
get() = _isHomeActivity
- private var _isHomeActivity: Boolean? = null
-
+ @Volatile private var _isHomeActivity: Boolean? = null
override fun init() {
_isHomeActivity = activityManager.isOnHomeActivity()
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt
index 3b8d318..baa8889 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt
@@ -18,18 +18,19 @@
import android.hardware.devicestate.DeviceStateManager
import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.updates.FoldProvider.FoldCallback
+import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
-class DeviceStateManagerFoldProvider @Inject constructor(
- private val deviceStateManager: DeviceStateManager,
- private val context: Context
-) : FoldProvider {
+class DeviceStateManagerFoldProvider
+@Inject
+constructor(private val deviceStateManager: DeviceStateManager, private val context: Context) :
+ FoldProvider {
- private val callbacks: MutableMap<FoldCallback,
- DeviceStateManager.DeviceStateCallback> = hashMapOf()
+ private val callbacks =
+ ConcurrentHashMap<FoldCallback, DeviceStateManager.DeviceStateCallback>()
override fun registerCallback(callback: FoldCallback, executor: Executor) {
val listener = FoldStateListener(context, callback)
@@ -39,13 +40,9 @@
override fun unregisterCallback(callback: FoldCallback) {
val listener = callbacks.remove(callback)
- listener?.let {
- deviceStateManager.unregisterCallback(it)
- }
+ listener?.let { deviceStateManager.unregisterCallback(it) }
}
- private inner class FoldStateListener(
- context: Context,
- listener: FoldCallback
- ) : DeviceStateManager.FoldStateListener(context, { listener.onFoldUpdated(it) })
+ private inner class FoldStateListener(context: Context, listener: FoldCallback) :
+ DeviceStateManager.FoldStateListener(context, { listener.onFoldUpdated(it) })
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
index 7b67e3f..7af9917 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
@@ -15,24 +15,29 @@
package com.android.systemui.unfold.system
import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
+import android.os.Process
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
import com.android.systemui.unfold.config.UnfoldTransitionConfig
-import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
+import com.android.systemui.unfold.dagger.UnfoldBg
import com.android.systemui.unfold.dagger.UnfoldMain
+import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.util.CurrentActivityTypeProvider
import dagger.Binds
import dagger.Module
+import dagger.Provides
import java.util.concurrent.Executor
+import javax.inject.Singleton
/**
- * Dagger module with system-only dependencies for the unfold animation.
- * The code that is used to calculate unfold transition progress
- * depends on some hidden APIs that are not available in normal
- * apps. In order to re-use this code and use alternative implementations
- * of these classes in other apps and hidden APIs here.
+ * Dagger module with system-only dependencies for the unfold animation. The code that is used to
+ * calculate unfold transition progress depends on some hidden APIs that are not available in normal
+ * apps. In order to re-use this code and use alternative implementations of these classes in other
+ * apps and hidden APIs here.
*/
@Module
abstract class SystemUnfoldSharedModule {
@@ -61,4 +66,22 @@
@Binds
@UnfoldSingleThreadBg
abstract fun backgroundExecutor(@UiBackground executor: Executor): Executor
+
+ companion object {
+ @Provides
+ @UnfoldBg
+ @Singleton
+ fun unfoldBgProgressHandler(@UnfoldBg looper: Looper): Handler {
+ return Handler(looper)
+ }
+
+ @Provides
+ @UnfoldBg
+ @Singleton
+ fun provideBgLooper(): Looper {
+ return HandlerThread("UnfoldBg", Process.THREAD_PRIORITY_FOREGROUND)
+ .apply { start() }
+ .looper
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
index b704f3c..1edb551 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
@@ -71,7 +71,7 @@
private final DisplayTracker mDisplayTracker;
private final AccessibilityLogger mA11yLogger;
- private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl;
+ private MagnificationConnectionImpl mMagnificationConnectionImpl;
private SysUiState mSysUiState;
@VisibleForTesting
@@ -220,7 +220,7 @@
}
@MainThread
- void setScale(int displayId, float scale) {
+ void setScaleForWindowMagnification(int displayId, float scale) {
final WindowMagnificationController windowMagnificationController =
mMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
@@ -321,37 +321,37 @@
final WindowMagnifierCallback mWindowMagnifierCallback = new WindowMagnifierCallback() {
@Override
public void onWindowMagnifierBoundsChanged(int displayId, Rect frame) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onWindowMagnifierBoundsChanged(displayId, frame);
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onWindowMagnifierBoundsChanged(displayId, frame);
}
}
@Override
public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onSourceBoundsChanged(displayId, sourceBounds);
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onSourceBoundsChanged(displayId, sourceBounds);
}
}
@Override
public void onPerformScaleAction(int displayId, float scale, boolean updatePersistence) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onPerformScaleAction(
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onPerformScaleAction(
displayId, scale, updatePersistence);
}
}
@Override
public void onAccessibilityActionPerformed(int displayId) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId);
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId);
}
}
@Override
public void onMove(int displayId) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onMove(displayId);
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onMove(displayId);
}
}
@@ -394,8 +394,8 @@
@Override
public void onMagnifierScale(int displayId, float scale,
boolean updatePersistence) {
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onPerformScaleAction(
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onPerformScaleAction(
displayId, scale, updatePersistence);
}
mA11yLogger.logThrottled(
@@ -454,8 +454,8 @@
if (magnificationSettingsController != null) {
magnificationSettingsController.closeMagnificationSettings();
}
- if (mWindowMagnificationConnectionImpl != null) {
- mWindowMagnificationConnectionImpl.onChangeMagnificationMode(displayId, newMode);
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onChangeMagnificationMode(displayId, newMode);
}
}
}
@@ -500,12 +500,12 @@
}
private void setWindowMagnificationConnection() {
- if (mWindowMagnificationConnectionImpl == null) {
- mWindowMagnificationConnectionImpl = new WindowMagnificationConnectionImpl(this,
+ if (mMagnificationConnectionImpl == null) {
+ mMagnificationConnectionImpl = new MagnificationConnectionImpl(this,
mHandler);
}
mAccessibilityManager.setWindowMagnificationConnection(
- mWindowMagnificationConnectionImpl);
+ mMagnificationConnectionImpl);
}
private void clearWindowMagnificationConnection() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
rename to packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
index 5666851..5f0d496 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
@@ -32,7 +32,7 @@
*
* @see IWindowMagnificationConnection
*/
-class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.Stub {
+class MagnificationConnectionImpl extends IWindowMagnificationConnection.Stub {
private static final String TAG = "WindowMagnificationConnectionImpl";
@@ -40,7 +40,7 @@
private final Magnification mMagnification;
private final Handler mHandler;
- WindowMagnificationConnectionImpl(@NonNull Magnification magnification,
+ MagnificationConnectionImpl(@NonNull Magnification magnification,
@Main Handler mainHandler) {
mMagnification = magnification;
mHandler = mainHandler;
@@ -57,8 +57,8 @@
}
@Override
- public void setScale(int displayId, float scale) {
- mHandler.post(() -> mMagnification.setScale(displayId, scale));
+ public void setScaleForWindowMagnification(int displayId, float scale) {
+ mHandler.post(() -> mMagnification.setScaleForWindowMagnification(displayId, scale));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 1ac4163..ab23564 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -30,6 +30,7 @@
import android.graphics.PixelFormat;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -168,7 +169,7 @@
// HAT received from LockSettingsService when credential is verified.
@Nullable private byte[] mCredentialAttestation;
- // TODO(b/287311775): remove when legacy prompt is replaced
+ // TODO(b/313469218): remove when legacy prompt is replaced
@Deprecated
static class Config {
Context mContext;
@@ -220,6 +221,9 @@
mHandler.postDelayed(() -> {
addCredentialView(false /* animatePanel */, true /* animateContents */);
}, mConfig.mSkipAnimation ? 0 : ANIMATE_CREDENTIAL_START_DELAY_MS);
+
+ // TODO(b/313469218): Remove Config
+ mConfig.mPromptInfo.setAuthenticators(Authenticators.DEVICE_CREDENTIAL);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
index 050b399..ff23837 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
@@ -30,13 +30,16 @@
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import java.util.concurrent.Executor
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.stateIn
/** Repository for the current state of the display */
@@ -66,7 +69,8 @@
deviceStateManager: DeviceStateManager,
displayManager: DisplayManager,
@Main handler: Handler,
- @Main mainExecutor: Executor
+ @Background backgroundExecutor: Executor,
+ @Background backgroundDispatcher: CoroutineDispatcher,
) : DisplayStateRepository {
override val isReverseDefaultRotation =
context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)
@@ -94,9 +98,10 @@
}
sendRearDisplayStateUpdate(false)
- deviceStateManager.registerCallback(mainExecutor, callback)
+ deviceStateManager.registerCallback(backgroundExecutor, callback)
awaitClose { deviceStateManager.unregisterCallback(callback) }
}
+ .flowOn(backgroundDispatcher)
.stateIn(
applicationScope,
started = SharingStarted.Eagerly,
@@ -137,6 +142,7 @@
)
awaitClose { displayManager.unregisterDisplayListener(callback) }
}
+ .flowOn(backgroundDispatcher)
.stateIn(
applicationScope,
started = SharingStarted.Eagerly,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
index 11a5d8b..3defec5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
@@ -485,8 +485,7 @@
): Int =
if (isPendingConfirmation) {
when (sensorType) {
- FingerprintSensorType.POWER_BUTTON ->
- R.string.security_settings_sfps_enroll_find_sensor_message
+ FingerprintSensorType.POWER_BUTTON -> -1
else -> R.string.fingerprint_dialog_authenticated_confirmation
}
} else if (isAuthenticating || isAuthenticated) {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index b598631..7c46339 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -105,9 +105,9 @@
val isUserSwitcherVisible: Boolean
get() = repository.isUserSwitcherVisible
- private val _onImeHidden = MutableSharedFlow<Unit>()
- /** Provide the onImeHidden events from the bouncer */
- val onImeHidden: SharedFlow<Unit> = _onImeHidden
+ private val _onImeHiddenByUser = MutableSharedFlow<Unit>()
+ /** Emits a [Unit] each time the IME (keyboard) is hidden by the user. */
+ val onImeHiddenByUser: SharedFlow<Unit> = _onImeHiddenByUser
init {
if (flags.isEnabled()) {
@@ -230,9 +230,9 @@
repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod()))
}
- /** Notifies the interactor that the input method editor has been hidden. */
- suspend fun onImeHidden() {
- _onImeHidden.emit(Unit)
+ /** Notifies that the input method editor (software keyboard) has been hidden by the user. */
+ suspend fun onImeHiddenByUser() {
+ _onImeHiddenByUser.emit(Unit)
}
private fun promptMessage(authMethod: AuthenticationMethodModel): String {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 8024874..e379dab 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -46,9 +46,6 @@
*/
val animateFailure: StateFlow<Boolean> = _animateFailure.asStateFlow()
- /** Whether the input method editor (for example, the software keyboard) is visible. */
- private var isImeVisible: Boolean = false
-
/** The authentication method that corresponds to this view model. */
abstract val authenticationMethod: AuthenticationMethodModel
@@ -68,7 +65,7 @@
/**
* Notifies that the UI has been hidden from the user (after any transitions have completed).
*/
- fun onHidden() {
+ open fun onHidden() {
clearInput()
interactor.resetMessage()
}
@@ -79,18 +76,6 @@
}
/**
- * Notifies that the input method editor (for example, the software keyboard) has been shown or
- * hidden.
- */
- suspend fun onImeVisibilityChanged(isVisible: Boolean) {
- if (isImeVisible && !isVisible) {
- interactor.onImeHidden()
- }
-
- isImeVisible = isVisible
- }
-
- /**
* Notifies that the failure animation has been shown. This should be called to consume a `true`
* value in [animateFailure].
*/
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index a15698e..45d18128 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -21,8 +21,11 @@
import com.android.systemui.res.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
/** Holds UI state and handles user input for the password bouncer UI. */
class PasswordBouncerViewModel(
@@ -45,6 +48,32 @@
override val throttlingMessageId = R.string.kg_too_many_failed_password_attempts_dialog_message
+ /** Whether the input method editor (for example, the software keyboard) is visible. */
+ private var isImeVisible: Boolean = false
+
+ /** Whether the text field element currently has focus. */
+ private val isTextFieldFocused = MutableStateFlow(false)
+
+ /** Whether the UI should request focus on the text field element. */
+ val isTextFieldFocusRequested =
+ combine(
+ interactor.isThrottled,
+ isTextFieldFocused,
+ ) { isThrottled, hasFocus ->
+ !isThrottled && !hasFocus
+ }
+ .stateIn(
+ scope = viewModelScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = !interactor.isThrottled.value && !isTextFieldFocused.value,
+ )
+
+ override fun onHidden() {
+ super.onHidden()
+ isImeVisible = false
+ isTextFieldFocused.value = false
+ }
+
override fun clearInput() {
_password.value = ""
}
@@ -72,4 +101,21 @@
tryAuthenticate()
}
}
+
+ /**
+ * Notifies that the input method editor (for example, the software keyboard) has been shown or
+ * hidden.
+ */
+ suspend fun onImeVisibilityChanged(isVisible: Boolean) {
+ if (isImeVisible && !isVisible && !interactor.isThrottled.value) {
+ interactor.onImeHiddenByUser()
+ }
+
+ isImeVisible = isVisible
+ }
+
+ /** Notifies that the password text field has gained or lost focus. */
+ fun onTextFieldFocusChanged(isFocused: Boolean) {
+ isTextFieldFocused.value = isFocused
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
index 7bca86e..12be32c 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
@@ -31,8 +31,10 @@
import com.android.systemui.util.kotlin.emitOnStart
import com.android.systemui.util.view.bindLatest
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
@@ -95,7 +97,8 @@
* call [onInflate] on the resulting view each time. Disposes of the [DisposableHandle] returned by
* [onInflate] when done.
*
- * This never completes unless cancelled, it just suspends and waits for updates.
+ * This never completes unless cancelled, it just suspends and waits for updates. It runs on a
+ * background thread using [backgroundDispatcher].
*
* For parameters [resource], [root] and [attachToRoot], see [LayoutInflater.inflate].
*
@@ -105,7 +108,7 @@
* ```
* parentView.repeatWhenAttached {
* configurationState
- * .reinflateOnChange(
+ * .reinflateAndBindLatest(
* R.layout.my_layout,
* parentView,
* attachToRoot = false,
@@ -124,7 +127,10 @@
@LayoutRes resource: Int,
root: ViewGroup?,
attachToRoot: Boolean,
+ backgroundDispatcher: CoroutineDispatcher,
onInflate: (T) -> DisposableHandle?,
) {
- inflateLayout<T>(resource, root, attachToRoot).bindLatest(onInflate)
+ inflateLayout<T>(resource, root, attachToRoot)
+ .flowOn(backgroundDispatcher)
+ .bindLatest(onInflate)
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index d9e0629..e7b8773 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -19,6 +19,7 @@
import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.CoreStartable;
import com.android.systemui.Dependency;
+import com.android.systemui.Flags;
import com.android.systemui.InitController;
import com.android.systemui.SystemUIAppComponentFactoryBase;
import com.android.systemui.dagger.qualifiers.PerUser;
@@ -35,6 +36,7 @@
import com.android.systemui.unfold.FoldStateLoggingProvider;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.dagger.UnfoldBg;
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
@@ -144,7 +146,15 @@
getConnectingDisplayViewModel().init();
getFoldStateLoggingProvider().ifPresent(FoldStateLoggingProvider::init);
getFoldStateLogger().ifPresent(FoldStateLogger::init);
- getUnfoldTransitionProgressProvider()
+
+ Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider;
+
+ if (Flags.unfoldAnimationBackgroundProgress()) {
+ unfoldTransitionProgressProvider = getBgUnfoldTransitionProgressProvider();
+ } else {
+ unfoldTransitionProgressProvider = getUnfoldTransitionProgressProvider();
+ }
+ unfoldTransitionProgressProvider
.ifPresent(
(progressProvider) ->
getUnfoldTransitionProgressForwarder()
@@ -170,7 +180,14 @@
ContextComponentHelper getContextComponentHelper();
/**
- * Creates a UnfoldTransitionProgressProvider.
+ * Creates a UnfoldTransitionProgressProvider that calculates progress in the background.
+ */
+ @SysUISingleton
+ @UnfoldBg
+ Optional<UnfoldTransitionProgressProvider> getBgUnfoldTransitionProgressProvider();
+
+ /**
+ * Creates a UnfoldTransitionProgressProvider that calculates progress in the main thread.
*/
@SysUISingleton
Optional<UnfoldTransitionProgressProvider> getUnfoldTransitionProgressProvider();
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 715fb17..4cddb9c 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -103,8 +103,11 @@
initialValue = false,
)
- // Authenticated by a TrustAgent like trusted device, location, etc or by face auth.
- private val passivelyAuthenticated =
+ /**
+ * Whether the user is currently authenticated by a TrustAgent like trusted device, location,
+ * etc., or by face auth.
+ */
+ private val isPassivelyAuthenticated =
merge(
trustRepository.isCurrentUserTrusted,
deviceEntryFaceAuthRepository.isAuthenticated,
@@ -117,25 +120,31 @@
* mechanism like face or trust manager. This returns `false` whenever the lockscreen has been
* dismissed.
*
+ * A value of `null` is meaningless and is used as placeholder while the actual value is still
+ * being loaded in the background.
+ *
* Note: `true` doesn't mean the lockscreen is visible. It may be occluded or covered by other
* UI.
*/
- val canSwipeToEnter =
+ val canSwipeToEnter: StateFlow<Boolean?> =
combine(
// This is true when the user has chosen to show the lockscreen but has not made it
// secure.
authenticationInteractor.authenticationMethod.map {
it == AuthenticationMethodModel.None && repository.isLockscreenEnabled()
},
- passivelyAuthenticated,
+ isPassivelyAuthenticated,
isDeviceEntered
- ) { isSwipeAuthMethod, passivelyAuthenticated, isDeviceEntered ->
- (isSwipeAuthMethod || passivelyAuthenticated) && !isDeviceEntered
+ ) { isSwipeAuthMethod, isPassivelyAuthenticated, isDeviceEntered ->
+ (isSwipeAuthMethod || isPassivelyAuthenticated) && !isDeviceEntered
}
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
- initialValue = false,
+ // Starts as null to prevent downstream collectors from falsely assuming that the
+ // user can or cannot swipe to enter the device while the real value is being loaded
+ // from upstream data sources.
+ initialValue = null,
)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 26c5ea6..c93b8e1 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -29,7 +29,6 @@
import com.android.app.tracing.traceSection
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.DisplayEvent
import com.android.systemui.util.Compile
@@ -93,7 +92,7 @@
constructor(
private val displayManager: DisplayManager,
@Background backgroundHandler: Handler,
- @Application applicationScope: CoroutineScope,
+ @Background bgApplicationScope: CoroutineScope,
@Background backgroundCoroutineDispatcher: CoroutineDispatcher
) : DisplayRepository {
private val allDisplayEvents: Flow<DisplayEvent> =
@@ -141,8 +140,7 @@
private val enabledDisplays =
allDisplayEvents
.map { getDisplays() }
- .flowOn(backgroundCoroutineDispatcher)
- .shareIn(applicationScope, started = SharingStarted.WhileSubscribed(), replay = 1)
+ .shareIn(bgApplicationScope, started = SharingStarted.WhileSubscribed(), replay = 1)
override val displays: Flow<Set<Display>> = enabledDisplays
@@ -203,9 +201,8 @@
}
.distinctUntilChanged()
.debugLog("connectedDisplayIds")
- .flowOn(backgroundCoroutineDispatcher)
.stateIn(
- applicationScope,
+ bgApplicationScope,
started = SharingStarted.WhileSubscribed(),
// The initial value is set to empty, but connected displays are gathered as soon as
// the flow starts being collected. This is to ensure the call to get displays (an
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2a0d6a8..0c24752 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -545,28 +545,12 @@
unreleasedFlag("clipboard_shared_transitions", teamfood = true)
/**
- * Whether the scene container (Flexiglass) is enabled. Note that [SCENE_CONTAINER] should be
- * checked and toggled together with [SCENE_CONTAINER_ENABLED] so that ProGuard can remove
- * unused code from our APK at compile time.
+ * Whether the scene container (Flexiglass) is enabled. Note that SceneContainerFlags#isEnabled
+ * should be checked and toggled together with [SCENE_CONTAINER_ENABLED] so that ProGuard can
+ * remove unused code from our APK at compile time.
*/
// TODO(b/283300105): Tracking Bug
@JvmField val SCENE_CONTAINER_ENABLED = false
- @Deprecated(
- message = """
- Do not use this flag directly. Please use
- [com.android.systemui.scene.shared.flag.SceneContainerFlags#isEnabled].
-
- (Not really deprecated but using this as a simple way to bring attention to the above).
- """,
- replaceWith = ReplaceWith(
- "com.android.systemui.scene.shared.flag.SceneContainerFlags#isEnabled",
- ),
- level = DeprecationLevel.WARNING,
- )
- @JvmField val SCENE_CONTAINER = resourceBooleanFlag(
- R.bool.config_sceneContainerFrameworkEnabled,
- "scene_container",
- )
// 1900
@JvmField val NOTE_TASKS = releasedFlag("keycode_flag")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 2b1cdc2..33dd3d9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -647,7 +647,7 @@
public void dismissKeyguardToLaunch(Intent intentToLaunch) {
trace("dismissKeyguardToLaunch");
checkPermission();
- mKeyguardViewMediator.dismissKeyguardToLaunch(intentToLaunch);
+ Slog.d(TAG, "Ignoring dismissKeyguardToLaunch " + intentToLaunch);
}
@Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 53ec3de..3009087 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -248,7 +248,6 @@
private static final int SHOW = 1;
private static final int HIDE = 2;
private static final int RESET = 3;
- private static final int VERIFY_UNLOCK = 4;
private static final int NOTIFY_FINISHED_GOING_TO_SLEEP = 5;
private static final int KEYGUARD_DONE = 7;
private static final int KEYGUARD_DONE_DRAWING = 8;
@@ -2316,15 +2315,6 @@
mHandler.sendMessage(msg);
}
- /**
- * Send message to keyguard telling it to verify unlock
- * @see #handleVerifyUnlock()
- */
- private void verifyUnlockLocked() {
- if (DEBUG) Log.d(TAG, "verifyUnlockLocked");
- mHandler.sendEmptyMessage(VERIFY_UNLOCK);
- }
-
private void notifyStartedGoingToSleep() {
if (DEBUG) Log.d(TAG, "notifyStartedGoingToSleep");
mHandler.sendEmptyMessage(NOTIFY_STARTED_GOING_TO_SLEEP);
@@ -2498,12 +2488,6 @@
message = "RESET";
handleReset(msg.arg1 != 0);
break;
- case VERIFY_UNLOCK:
- message = "VERIFY_UNLOCK";
- Trace.beginSection("KeyguardViewMediator#handleMessage VERIFY_UNLOCK");
- handleVerifyUnlock();
- Trace.endSection();
- break;
case NOTIFY_STARTED_GOING_TO_SLEEP:
message = "NOTIFY_STARTED_GOING_TO_SLEEP";
handleNotifyStartedGoingToSleep();
@@ -3435,20 +3419,6 @@
scheduleNonStrongBiometricIdleTimeout();
}
- /**
- * Handle message sent by {@link #verifyUnlock}
- * @see #VERIFY_UNLOCK
- */
- private void handleVerifyUnlock() {
- Trace.beginSection("KeyguardViewMediator#handleVerifyUnlock");
- synchronized (KeyguardViewMediator.this) {
- if (DEBUG) Log.d(TAG, "handleVerifyUnlock");
- setShowingLocked(true);
- mKeyguardViewControllerLazy.get().dismissAndCollapse();
- }
- Trace.endSection();
- }
-
private void handleNotifyStartedGoingToSleep() {
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleNotifyStartedGoingToSleep");
@@ -3606,10 +3576,6 @@
// do nothing
}
- public void dismissKeyguardToLaunch(Intent intentToLaunch) {
- // do nothing
- }
-
public void onSystemKeyPressed(int keycode) {
// do nothing
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
index f9b89b1..7354cfc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
@@ -18,6 +18,7 @@
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
import com.android.app.tracing.traceSection
+import java.util.concurrent.CopyOnWriteArrayList
import javax.inject.Inject
import javax.inject.Singleton
@@ -29,7 +30,7 @@
screenLifecycle.addObserver(this)
}
- private val listeners: MutableList<ScreenListener> = mutableListOf()
+ private val listeners: MutableList<ScreenListener> = CopyOnWriteArrayList()
override fun removeCallback(listener: ScreenListener) {
listeners.remove(listener)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index 8ef2662..36412e3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -253,7 +253,7 @@
Pair(isAuthenticated.isFalse(), "faceNotAuthenticated"),
)
.andAllFlows("canFaceAuthRun", faceAuthLog)
- .flowOn(mainDispatcher)
+ .flowOn(backgroundDispatcher)
.stateIn(applicationScope, SharingStarted.Eagerly, false)
// Face detection can run only when lockscreen bypass is enabled
@@ -280,7 +280,7 @@
)
)
.andAllFlows("canFaceDetectRun", faceDetectLog)
- .flowOn(mainDispatcher)
+ .flowOn(backgroundDispatcher)
.stateIn(applicationScope, SharingStarted.Eagerly, false)
observeFaceAuthGatingChecks()
observeFaceDetectGatingChecks()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index 9bec300..96386f3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -26,6 +26,7 @@
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
@@ -33,11 +34,13 @@
import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.stateIn
/** Encapsulates state about device entry fingerprint auth mechanism. */
@@ -74,6 +77,7 @@
val authController: AuthController,
val keyguardUpdateMonitor: KeyguardUpdateMonitor,
@Application scope: CoroutineScope,
+ @Main private val mainDispatcher: CoroutineDispatcher,
) : DeviceEntryFingerprintAuthRepository {
override val availableFpSensorType: Flow<BiometricType?>
@@ -137,30 +141,34 @@
.stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
override val isRunning: Flow<Boolean>
- get() = conflatedCallbackFlow {
- val callback =
- object : KeyguardUpdateMonitorCallback() {
- override fun onBiometricRunningStateChanged(
- running: Boolean,
- biometricSourceType: BiometricSourceType?
- ) {
- if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
- trySendWithFailureLogging(
- running,
- TAG,
- "Fingerprint running state changed"
- )
+ get() =
+ conflatedCallbackFlow {
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onBiometricRunningStateChanged(
+ running: Boolean,
+ biometricSourceType: BiometricSourceType?
+ ) {
+ if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
+ trySendWithFailureLogging(
+ running,
+ TAG,
+ "Fingerprint running state changed"
+ )
+ }
+ }
}
- }
+ keyguardUpdateMonitor.registerCallback(callback)
+ trySendWithFailureLogging(
+ keyguardUpdateMonitor.isFingerprintDetectionRunning,
+ TAG,
+ "Initial fingerprint running state"
+ )
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
- keyguardUpdateMonitor.registerCallback(callback)
- trySendWithFailureLogging(
- keyguardUpdateMonitor.isFingerprintDetectionRunning,
- TAG,
- "Initial fingerprint running state"
- )
- awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
- }
+ .flowOn(
+ mainDispatcher
+ ) // keyguardUpdateMonitor requires registration on main thread.
override val authenticationStatus: Flow<FingerprintAuthenticationStatus>
get() = conflatedCallbackFlow {
@@ -171,7 +179,6 @@
biometricSourceType: BiometricSourceType,
isStrongBiometric: Boolean,
) {
-
sendUpdateIfFingerprint(
biometricSourceType,
SuccessFingerprintAuthenticationStatus(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt
index adb1e01..7c43092 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt
@@ -19,11 +19,14 @@
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.shared.model.DevicePosture
import com.android.systemui.statusbar.policy.DevicePostureController
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
/** Provide current device posture state. */
interface DevicePostureRepository {
@@ -34,23 +37,28 @@
@SysUISingleton
class DevicePostureRepositoryImpl
@Inject
-constructor(private val postureController: DevicePostureController) : DevicePostureRepository {
+constructor(
+ private val postureController: DevicePostureController,
+ @Main private val mainDispatcher: CoroutineDispatcher
+) : DevicePostureRepository {
override val currentDevicePosture: Flow<DevicePosture>
- get() = conflatedCallbackFlow {
- val sendPostureUpdate = { posture: Int ->
- val currentDevicePosture = DevicePosture.toPosture(posture)
- trySendWithFailureLogging(
- currentDevicePosture,
- TAG,
- "Error sending posture update to $currentDevicePosture"
- )
- }
- val callback = DevicePostureController.Callback { sendPostureUpdate(it) }
- postureController.addCallback(callback)
- sendPostureUpdate(postureController.devicePosture)
+ get() =
+ conflatedCallbackFlow {
+ val sendPostureUpdate = { posture: Int ->
+ val currentDevicePosture = DevicePosture.toPosture(posture)
+ trySendWithFailureLogging(
+ currentDevicePosture,
+ TAG,
+ "Error sending posture update to $currentDevicePosture"
+ )
+ }
+ val callback = DevicePostureController.Callback { sendPostureUpdate(it) }
+ postureController.addCallback(callback)
+ sendPostureUpdate(postureController.devicePosture)
- awaitClose { postureController.removeCallback(callback) }
- }
+ awaitClose { postureController.removeCallback(callback) }
+ }
+ .flowOn(mainDispatcher) // DevicePostureController requirement
companion object {
const val TAG = "PostureRepositoryImpl"
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 4da48f6..706aba3c 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
@@ -302,4 +302,11 @@
fun isFinishedInState(state: KeyguardState): Flow<Boolean> {
return finishedKeyguardState.map { it == state }.distinctUntilChanged()
}
+
+ /**
+ * Whether we've FINISHED a transition to a state that matches the given predicate. Consider
+ * using [isFinishedInStateWhere] whenever possible instead
+ */
+ fun isFinishedInStateWhereValue(stateMatcher: (KeyguardState) -> Boolean) =
+ stateMatcher(finishedKeyguardState.replayCache.last())
}
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 a2e930c..9f46e22 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -287,6 +287,10 @@
return
}
+ if (smartSpaceView != null) {
+ parentView.removeView(smartSpaceView)
+ }
+
smartSpaceView = lockscreenSmartspaceController.buildAndConnectDateView(parentView)
val topPadding: Int =
@@ -362,12 +366,13 @@
),
)
+ setUpUdfps(previewContext, rootView)
+
disposables.add(
PreviewKeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel) {
if (keyguardBottomAreaRefactor()) {
setupShortcuts(keyguardRootView)
}
- setUpUdfps(previewContext, rootView)
if (!shouldHideClock) {
setUpClock(previewContext, rootView)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 0c5a14f..48f432e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -58,8 +58,8 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastSender;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import java.util.concurrent.Executor;
@@ -85,6 +85,13 @@
final MediaOutputController mMediaOutputController;
final BroadcastSender mBroadcastSender;
+ /**
+ * Signals whether the dialog should NOT show app-related metadata.
+ *
+ * <p>A metadata-less dialog hides the title, subtitle, and app icon in the header.
+ */
+ private final boolean mIncludePlaybackAndAppMetadata;
+
@VisibleForTesting
View mDialogView;
private TextView mHeaderTitle;
@@ -210,8 +217,11 @@
}
}
- public MediaOutputBaseDialog(Context context, BroadcastSender broadcastSender,
- MediaOutputController mediaOutputController) {
+ public MediaOutputBaseDialog(
+ Context context,
+ BroadcastSender broadcastSender,
+ MediaOutputController mediaOutputController,
+ boolean includePlaybackAndAppMetadata) {
super(context, R.style.Theme_SystemUI_Dialog_Media);
// Save the context that is wrapped with our theme.
@@ -226,6 +236,7 @@
mListPaddingTop = mContext.getResources().getDimensionPixelSize(
R.dimen.media_output_dialog_list_padding_top);
mExecutor = Executors.newSingleThreadExecutor();
+ mIncludePlaybackAndAppMetadata = includePlaybackAndAppMetadata;
}
@Override
@@ -354,7 +365,10 @@
updateDialogBackgroundColor();
mHeaderIcon.setVisibility(View.GONE);
}
- if (appSourceIcon != null) {
+
+ if (!mIncludePlaybackAndAppMetadata) {
+ mAppResourceIcon.setVisibility(View.GONE);
+ } else if (appSourceIcon != null) {
Icon appIcon = appSourceIcon.toIcon(mContext);
mAppResourceIcon.setColorFilter(mMediaOutputController.getColorItemContent());
mAppResourceIcon.setImageIcon(appIcon);
@@ -373,17 +387,24 @@
mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size + padding, size));
}
mAppButton.setText(mMediaOutputController.getAppSourceName());
- // Update title and subtitle
- mHeaderTitle.setText(getHeaderText());
- final CharSequence subTitle = getHeaderSubtitle();
- if (TextUtils.isEmpty(subTitle)) {
+
+ if (!mIncludePlaybackAndAppMetadata) {
+ mHeaderTitle.setVisibility(View.GONE);
mHeaderSubtitle.setVisibility(View.GONE);
- mHeaderTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
} else {
- mHeaderSubtitle.setVisibility(View.VISIBLE);
- mHeaderSubtitle.setText(subTitle);
- mHeaderTitle.setGravity(Gravity.NO_GRAVITY);
+ // Update title and subtitle
+ mHeaderTitle.setText(getHeaderText());
+ final CharSequence subTitle = getHeaderSubtitle();
+ if (TextUtils.isEmpty(subTitle)) {
+ mHeaderSubtitle.setVisibility(View.GONE);
+ mHeaderTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
+ } else {
+ mHeaderSubtitle.setVisibility(View.VISIBLE);
+ mHeaderSubtitle.setText(subTitle);
+ mHeaderTitle.setGravity(Gravity.NO_GRAVITY);
+ }
}
+
// Show when remote media session is available or
// when the device supports BT LE audio + media is playing
mStopButton.setVisibility(getStopButtonVisibility());
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
index ac64300..8e0191e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
@@ -42,12 +42,10 @@
import androidx.core.graphics.drawable.IconCompat;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.settingslib.media.BluetoothMediaDevice;
-import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.qrcode.QrCodeGenerator;
-import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.google.zxing.WriterException;
@@ -237,7 +235,11 @@
MediaOutputBroadcastDialog(Context context, boolean aboveStatusbar,
BroadcastSender broadcastSender, MediaOutputController mediaOutputController) {
- super(context, broadcastSender, mediaOutputController);
+ super(
+ context,
+ broadcastSender,
+ mediaOutputController, /* includePlaybackAndAppMetadata */
+ true);
mAdapter = new MediaOutputAdapter(mMediaOutputController);
// TODO(b/226710953): Move the part to MediaOutputBaseDialog for every class
// that extends MediaOutputBaseDialog
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 426a497..375a0ce 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -78,7 +78,6 @@
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.utils.ThreadUtils;
-import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
@@ -86,6 +85,7 @@
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.monet.ColorScheme;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -358,7 +358,7 @@
}
Drawable getAppSourceIconFromPackage() {
- if (mPackageName.isEmpty()) {
+ if (TextUtils.isEmpty(mPackageName)) {
return null;
}
try {
@@ -372,7 +372,7 @@
}
String getAppSourceName() {
- if (mPackageName.isEmpty()) {
+ if (TextUtils.isEmpty(mPackageName)) {
return null;
}
final PackageManager packageManager = mContext.getPackageManager();
@@ -391,7 +391,7 @@
}
Intent getAppLaunchIntent() {
- if (mPackageName.isEmpty()) {
+ if (TextUtils.isEmpty(mPackageName)) {
return null;
}
return mContext.getPackageManager().getLaunchIntentForPackage(mPackageName);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index 4640a5d..d40699c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -27,10 +27,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.res.R;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.res.R;
/**
* Dialog for media output transferring.
@@ -40,10 +40,15 @@
private final DialogLaunchAnimator mDialogLaunchAnimator;
private final UiEventLogger mUiEventLogger;
- MediaOutputDialog(Context context, boolean aboveStatusbar, BroadcastSender broadcastSender,
- MediaOutputController mediaOutputController, DialogLaunchAnimator dialogLaunchAnimator,
- UiEventLogger uiEventLogger) {
- super(context, broadcastSender, mediaOutputController);
+ MediaOutputDialog(
+ Context context,
+ boolean aboveStatusbar,
+ BroadcastSender broadcastSender,
+ MediaOutputController mediaOutputController,
+ DialogLaunchAnimator dialogLaunchAnimator,
+ UiEventLogger uiEventLogger,
+ boolean includePlaybackAndAppMetadata) {
+ super(context, broadcastSender, mediaOutputController, includePlaybackAndAppMetadata);
mDialogLaunchAnimator = dialogLaunchAnimator;
mUiEventLogger = uiEventLogger;
mAdapter = new MediaOutputAdapter(mMediaOutputController);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index 2b38edb..b04a7a4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -61,6 +61,19 @@
/** Creates a [MediaOutputDialog] for the given package. */
open fun create(packageName: String, aboveStatusBar: Boolean, view: View? = null) {
+ create(packageName, aboveStatusBar, view, includePlaybackAndAppMetadata = true)
+ }
+
+ open fun createDialogForSystemRouting() {
+ create(packageName = null, aboveStatusBar = false, includePlaybackAndAppMetadata = false)
+ }
+
+ private fun create(
+ packageName: String?,
+ aboveStatusBar: Boolean,
+ view: View? = null,
+ includePlaybackAndAppMetadata: Boolean = true
+ ) {
// Dismiss the previous dialog, if any.
mediaOutputDialog?.dismiss()
@@ -71,7 +84,7 @@
powerExemptionManager, keyGuardManager, featureFlags, userTracker)
val dialog =
MediaOutputDialog(context, aboveStatusBar, broadcastSender, controller,
- dialogLaunchAnimator, uiEventLogger)
+ dialogLaunchAnimator, uiEventLogger, includePlaybackAndAppMetadata)
mediaOutputDialog = dialog
// Show the dialog.
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
index 132bf99..1002cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
@@ -19,7 +19,6 @@
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import android.text.TextUtils
import android.util.Log
import com.android.settingslib.media.MediaOutputConstants
import javax.inject.Inject
@@ -35,16 +34,16 @@
private val mediaOutputBroadcastDialogFactory: MediaOutputBroadcastDialogFactory
) : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
- when {
- TextUtils.equals(
- MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG, intent.action) -> {
+ when (intent.action) {
+ MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG -> {
val packageName: String? =
intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME)
launchMediaOutputDialogIfPossible(packageName)
}
- TextUtils.equals(
- MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG,
- intent.action) -> {
+ MediaOutputConstants.ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG -> {
+ mediaOutputDialogFactory.createDialogForSystemRouting()
+ }
+ MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG -> {
val packageName: String? =
intent.getStringExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME)
launchMediaOutputBroadcastDialogIfPossible(packageName)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
index 654fffe8..1983a67 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
@@ -24,6 +24,7 @@
import android.view.ViewGroup
import android.view.ViewStub
import android.view.WindowManager
+import android.view.accessibility.AccessibilityNodeInfo
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.ImageView
@@ -106,6 +107,19 @@
screenShareModeSpinner = dialog.requireViewById(R.id.screen_share_mode_spinner)
screenShareModeSpinner.adapter = adapter
screenShareModeSpinner.onItemSelectedListener = this
+
+ // disable redundant Touch & Hold accessibility action for Switch Access
+ screenShareModeSpinner.accessibilityDelegate =
+ object : View.AccessibilityDelegate() {
+ override fun onInitializeAccessibilityNodeInfo(
+ host: View,
+ info: AccessibilityNodeInfo
+ ) {
+ info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK)
+ super.onInitializeAccessibilityNodeInfo(host, info)
+ }
+ }
+ screenShareModeSpinner.isLongClickable = false
}
override fun onItemSelected(adapterView: AdapterView<*>?, view: View, pos: Int, id: Long) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index f13add9..e2e94da 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -72,6 +72,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.Trace;
import android.provider.DeviceConfig;
import android.telecom.TelecomManager;
import android.text.TextUtils;
@@ -736,17 +737,27 @@
}
public void destroyView() {
- setAutoHideController(/* autoHideController */ null);
- mCommandQueue.removeCallback(this);
- mWindowManager.removeViewImmediate(mView.getRootView());
- mNavigationModeController.removeListener(mModeChangedListener);
- mEdgeBackGestureHandler.setStateChangeCallback(null);
+ Trace.beginSection("NavigationBar#destroyView");
+ try {
+ setAutoHideController(/* autoHideController */ null);
+ mCommandQueue.removeCallback(this);
+ Trace.beginSection("NavigationBar#removeViewImmediate");
+ try {
+ mWindowManager.removeViewImmediate(mView.getRootView());
+ } finally {
+ Trace.endSection();
+ }
+ mNavigationModeController.removeListener(mModeChangedListener);
+ mEdgeBackGestureHandler.setStateChangeCallback(null);
- mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
- mNotificationShadeDepthController.removeListener(mDepthListener);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNotificationShadeDepthController.removeListener(mDepthListener);
- mDeviceConfigProxy.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
- mTaskStackChangeListeners.unregisterTaskStackListener(mTaskStackListener);
+ mDeviceConfigProxy.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
+ mTaskStackChangeListeners.unregisterTaskStackListener(mTaskStackListener);
+ } finally {
+ Trace.endSection();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 3b32313e..8d1ff98a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -46,6 +46,7 @@
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.Trace;
import android.util.Log;
import android.view.Display;
import android.view.View;
@@ -229,28 +230,34 @@
}
public void init(int displayId) {
- if (mInitialized) {
- return;
+ Trace.beginSection("TaskbarDelegate#init");
+ try {
+ if (mInitialized) {
+ return;
+ }
+ mDisplayId = displayId;
+ parseCurrentSysuiState();
+ mCommandQueue.addCallback(this);
+ mOverviewProxyService.addCallback(this);
+ onNavigationModeChanged(mNavigationModeController.addListener(this));
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ // Initialize component callback
+ Display display = mDisplayManager.getDisplay(displayId);
+ mWindowContext = mContext.createWindowContext(display, TYPE_APPLICATION, null);
+ mScreenPinningNotify = new ScreenPinningNotify(mWindowContext);
+ // Set initial state for any listeners
+ updateSysuiFlags();
+ mAutoHideController.setNavigationBar(mAutoHideUiElement);
+ mLightBarController.setNavigationBar(mLightBarTransitionsController);
+ mPipOptional.ifPresent(this::addPipExclusionBoundsChangeListener);
+ mEdgeBackGestureHandler.setBackAnimation(mBackAnimation);
+ mEdgeBackGestureHandler.onConfigurationChanged(
+ mContext.getResources().getConfiguration());
+ mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
+ mInitialized = true;
+ } finally {
+ Trace.endSection();
}
- mDisplayId = displayId;
- parseCurrentSysuiState();
- mCommandQueue.addCallback(this);
- mOverviewProxyService.addCallback(this);
- onNavigationModeChanged(mNavigationModeController.addListener(this));
- mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
- // Initialize component callback
- Display display = mDisplayManager.getDisplay(displayId);
- mWindowContext = mContext.createWindowContext(display, TYPE_APPLICATION, null);
- mScreenPinningNotify = new ScreenPinningNotify(mWindowContext);
- // Set initial state for any listeners
- updateSysuiFlags();
- mAutoHideController.setNavigationBar(mAutoHideUiElement);
- mLightBarController.setNavigationBar(mLightBarTransitionsController);
- mPipOptional.ifPresent(this::addPipExclusionBoundsChangeListener);
- mEdgeBackGestureHandler.setBackAnimation(mBackAnimation);
- mEdgeBackGestureHandler.onConfigurationChanged(mContext.getResources().getConfiguration());
- mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
- mInitialized = true;
}
public void destroy() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 3dfd292..9846f4b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -578,10 +578,15 @@
* @see NavigationModeController.ModeChangedListener#onNavigationModeChanged
*/
public void onNavigationModeChanged(int mode) {
- mUsingThreeButtonNav = QuickStepContract.isLegacyMode(mode);
- mInGestureNavMode = QuickStepContract.isGesturalMode(mode);
- updateIsEnabled();
- updateCurrentUserResources();
+ Trace.beginSection("EdgeBackGestureHandler#onNavigationModeChanged");
+ try {
+ mUsingThreeButtonNav = QuickStepContract.isLegacyMode(mode);
+ mInGestureNavMode = QuickStepContract.isGesturalMode(mode);
+ updateIsEnabled();
+ updateCurrentUserResources();
+ } finally {
+ Trace.endSection();
+ }
}
public void onNavBarTransientStateChanged(boolean isTransient) {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index d9a8080..270bfbe 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -37,9 +37,10 @@
import android.provider.Settings
import android.widget.Toast
import androidx.annotation.VisibleForTesting
-import com.android.systemui.res.R
+import com.android.app.tracing.TraceUtils.Companion.launch
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled
import com.android.systemui.log.DebugLogger.debugLog
import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
@@ -47,6 +48,7 @@
import com.android.systemui.notetask.NoteTaskRoleManagerExt.createNoteShortcutInfoAsUser
import com.android.systemui.notetask.NoteTaskRoleManagerExt.getDefaultRoleHolderAsUser
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
+import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.system.ActivityManagerKt.isInForeground
import com.android.systemui.util.settings.SecureSettings
@@ -54,8 +56,8 @@
import com.android.wm.shell.bubbles.Bubbles.BubbleExpandListener
import java.util.concurrent.atomic.AtomicReference
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
/**
* Entry point for creating and managing note.
@@ -81,7 +83,8 @@
private val devicePolicyManager: DevicePolicyManager,
private val userTracker: UserTracker,
private val secureSettings: SecureSettings,
- @Application private val applicationScope: CoroutineScope
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val bgCoroutineContext: CoroutineContext
) {
@VisibleForTesting val infoReference = AtomicReference<NoteTaskInfo?>()
@@ -172,7 +175,9 @@
) {
if (!isEnabled) return
- applicationScope.launch { awaitShowNoteTaskAsUser(entryPoint, user) }
+ applicationScope.launch("$TAG#showNoteTaskAsUser") {
+ awaitShowNoteTaskAsUser(entryPoint, user)
+ }
}
private suspend fun awaitShowNoteTaskAsUser(
@@ -337,7 +342,7 @@
@InternalNoteTaskApi
fun launchUpdateNoteTaskAsUser(user: UserHandle) {
- applicationScope.launch {
+ applicationScope.launch("$TAG#launchUpdateNoteTaskAsUser", bgCoroutineContext) {
if (!userManager.isUserUnlocked(user)) {
debugLog { "updateNoteTaskAsUserInternal call but user locked: user=$user" }
return@launch
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
index a321eef..6f5dea3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
@@ -18,17 +18,19 @@
import android.content.ComponentName
import android.content.Context
+import android.content.SharedPreferences
import android.service.quicksettings.Tile
import android.util.Log
import com.android.internal.annotations.VisibleForTesting
+import javax.inject.Inject
import org.json.JSONException
import org.json.JSONObject
-import javax.inject.Inject
data class TileServiceKey(val componentName: ComponentName, val user: Int) {
private val string = "${componentName.flattenToString()}:$user"
override fun toString() = string
}
+
private const val STATE = "state"
private const val LABEL = "label"
private const val SUBTITLE = "subtitle"
@@ -44,12 +46,7 @@
* It persists the state from a [Tile] necessary to present the view in the same state when
* retrieved, with the exception of the icon.
*/
-class CustomTileStatePersister @Inject constructor(context: Context) {
- companion object {
- private const val FILE_NAME = "custom_tiles_state"
- }
-
- private val sharedPreferences = context.getSharedPreferences(FILE_NAME, 0)
+interface CustomTileStatePersister {
/**
* Read the state from [SharedPreferences].
@@ -58,7 +55,31 @@
*
* Any fields that have not been saved will be set to `null`
*/
- fun readState(key: TileServiceKey): Tile? {
+ fun readState(key: TileServiceKey): Tile?
+ /**
+ * Persists the state into [SharedPreferences].
+ *
+ * The implementation does not store fields that are `null` or icons.
+ */
+ fun persistState(key: TileServiceKey, tile: Tile)
+ /**
+ * Removes the state for a given tile, user pair.
+ *
+ * Used when the tile is removed by the user.
+ */
+ fun removeState(key: TileServiceKey)
+}
+
+// TODO(b/299909989) Merge this class into into CustomTileRepository
+class CustomTileStatePersisterImpl @Inject constructor(context: Context) :
+ CustomTileStatePersister {
+ companion object {
+ private const val FILE_NAME = "custom_tiles_state"
+ }
+
+ private val sharedPreferences: SharedPreferences = context.getSharedPreferences(FILE_NAME, 0)
+
+ override fun readState(key: TileServiceKey): Tile? {
val state = sharedPreferences.getString(key.toString(), null) ?: return null
return try {
readTileFromString(state)
@@ -68,23 +89,13 @@
}
}
- /**
- * Persists the state into [SharedPreferences].
- *
- * The implementation does not store fields that are `null` or icons.
- */
- fun persistState(key: TileServiceKey, tile: Tile) {
+ override fun persistState(key: TileServiceKey, tile: Tile) {
val state = writeToString(tile)
sharedPreferences.edit().putString(key.toString(), state).apply()
}
- /**
- * Removes the state for a given tile, user pair.
- *
- * Used when the tile is removed by the user.
- */
- fun removeState(key: TileServiceKey) {
+ override fun removeState(key: TileServiceKey) {
sharedPreferences.edit().remove(key.toString()).apply()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index f8e0159..4565200 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -45,6 +45,7 @@
import android.widget.Switch
import android.widget.TextView
import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.traceSection
import com.android.settingslib.Utils
import com.android.systemui.FontSizeUtils
import com.android.systemui.animation.LaunchableView
@@ -707,7 +708,7 @@
inner class StateChangeRunnable(private val state: QSTile.State) : Runnable {
override fun run() {
- handleStateChanged(state)
+ traceSection("QSTileViewImpl#handleStateChanged") { handleStateChanged(state) }
}
// We want all instances of this runnable to be equal to each other, so they can be used to
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
index 94137c8..4a34276 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tiles.di
+import com.android.systemui.qs.external.CustomTileStatePersister
+import com.android.systemui.qs.external.CustomTileStatePersisterImpl
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerImpl
import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent
@@ -52,4 +54,7 @@
fun bindQSTileIntentUserInputHandler(
impl: QSTileIntentUserInputHandlerImpl
): QSTileIntentUserInputHandler
+
+ @Binds
+ fun bindCustomTileStatePersister(impl: CustomTileStatePersisterImpl): CustomTileStatePersister
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
index 5bdb592..db3cf0f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
@@ -81,10 +81,8 @@
private lateinit var toggleView: Switch
private lateinit var subtitleTextView: TextView
private lateinit var doneButton: View
- private lateinit var seeAllViewGroup: View
- private lateinit var pairNewDeviceViewGroup: View
- private lateinit var seeAllRow: View
- private lateinit var pairNewDeviceRow: View
+ private lateinit var seeAllButton: View
+ private lateinit var pairNewDeviceButton: View
private lateinit var deviceListView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
@@ -99,10 +97,8 @@
toggleView = requireViewById(R.id.bluetooth_toggle)
subtitleTextView = requireViewById(R.id.bluetooth_tile_dialog_subtitle) as TextView
doneButton = requireViewById(R.id.done_button)
- seeAllViewGroup = requireViewById(R.id.see_all_layout_group)
- pairNewDeviceViewGroup = requireViewById(R.id.pair_new_device_layout_group)
- seeAllRow = requireViewById(R.id.see_all_clickable_row)
- pairNewDeviceRow = requireViewById(R.id.pair_new_device_clickable_row)
+ seeAllButton = requireViewById(R.id.see_all_button)
+ pairNewDeviceButton = requireViewById(R.id.pair_new_device_button)
deviceListView = requireViewById<RecyclerView>(R.id.device_list)
setupToggle()
@@ -110,8 +106,8 @@
subtitleTextView.text = context.getString(subtitleResIdInitialValue)
doneButton.setOnClickListener { dismiss() }
- seeAllRow.setOnClickListener { bluetoothTileDialogCallback.onSeeAllClicked(it) }
- pairNewDeviceRow.setOnClickListener {
+ seeAllButton.setOnClickListener { bluetoothTileDialogCallback.onSeeAllClicked(it) }
+ pairNewDeviceButton.setOnClickListener {
bluetoothTileDialogCallback.onPairNewDeviceClicked(it)
}
}
@@ -134,8 +130,8 @@
}
if (isActive) {
deviceItemAdapter.refreshDeviceItemList(deviceItem) {
- seeAllViewGroup.visibility = if (showSeeAll) VISIBLE else GONE
- pairNewDeviceViewGroup.visibility = if (showPairNewDevice) VISIBLE else GONE
+ seeAllButton.visibility = if (showSeeAll) VISIBLE else GONE
+ pairNewDeviceButton.visibility = if (showPairNewDevice) VISIBLE else GONE
lastUiUpdateMs = systemClock.elapsedRealtime()
lastItemRow = itemRow
logger.logDeviceUiUpdate(lastUiUpdateMs - start)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
index 34c2aba..5d5e747 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
@@ -20,7 +20,6 @@
import android.content.Intent
import android.os.Bundle
import android.view.View
-import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.DialogCuj
@@ -40,6 +39,8 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.channels.produce
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -63,26 +64,25 @@
private var job: Job? = null
- @VisibleForTesting internal var dialog: BluetoothTileDialog? = null
-
/**
* Shows the dialog.
*
* @param context The context in which the dialog is displayed.
* @param view The view from which the dialog is shown.
*/
+ @kotlinx.coroutines.ExperimentalCoroutinesApi
fun showDialog(context: Context, view: View?) {
- dismissDialog()
-
- var updateDeviceItemJob: Job? = null
- var updateDialogUiJob: Job? = null
+ cancelJob()
job =
coroutineScope.launch(mainDispatcher) {
- dialog = createBluetoothTileDialog(context)
+ var updateDeviceItemJob: Job?
+ var updateDialogUiJob: Job? = null
+ val dialog = createBluetoothTileDialog(context)
+
view?.let {
dialogLaunchAnimator.showFromView(
- dialog!!,
+ dialog,
it,
animateBackgroundBoundsChange = true,
cuj =
@@ -92,9 +92,8 @@
)
)
}
- ?: dialog!!.show()
+ ?: dialog.show()
- updateDeviceItemJob?.cancel()
updateDeviceItemJob = launch {
deviceItemInteractor.updateDeviceItems(context, DeviceFetchTrigger.FIRST_LOAD)
}
@@ -102,7 +101,7 @@
bluetoothStateInteractor.bluetoothStateUpdate
.filterNotNull()
.onEach {
- dialog!!.onBluetoothStateUpdated(it, getSubtitleResId(it))
+ dialog.onBluetoothStateUpdated(it, getSubtitleResId(it))
updateDeviceItemJob?.cancel()
updateDeviceItemJob = launch {
deviceItemInteractor.updateDeviceItems(
@@ -129,7 +128,7 @@
.onEach {
updateDialogUiJob?.cancel()
updateDialogUiJob = launch {
- dialog?.onDeviceItemUpdated(
+ dialog.onDeviceItemUpdated(
it.take(MAX_DEVICE_ITEM_ENTRY),
showSeeAll = it.size > MAX_DEVICE_ITEM_ENTRY,
showPairNewDevice = bluetoothStateInteractor.isBluetoothEnabled
@@ -138,15 +137,15 @@
}
.launchIn(this)
- dialog!!
- .bluetoothStateToggle
+ dialog.bluetoothStateToggle
.onEach { bluetoothStateInteractor.isBluetoothEnabled = it }
.launchIn(this)
- dialog!!
- .deviceItemClick
+ dialog.deviceItemClick
.onEach { deviceItemInteractor.updateDeviceItemOnClick(it) }
.launchIn(this)
+
+ produce<Unit> { awaitClose { dialog.cancel() } }
}
}
@@ -161,7 +160,7 @@
logger,
context
)
- .apply { SystemUIDialog.registerDismissListener(this) { dismissDialog() } }
+ .apply { SystemUIDialog.registerDismissListener(this) { cancelJob() } }
}
override fun onDeviceItemGearClicked(deviceItem: DeviceItem, view: View) {
@@ -188,15 +187,13 @@
startSettingsActivity(Intent(ACTION_PAIR_NEW_DEVICE), view)
}
- private fun dismissDialog() {
+ private fun cancelJob() {
job?.cancel()
job = null
- dialog?.dismiss()
- dialog = null
}
private fun startSettingsActivity(intent: Intent, view: View) {
- dialog?.run {
+ if (job?.isActive == true) {
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
activityStarter.postStartActivityDismissingKeyguard(
intent,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
index 76fbf8e..fcd45a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
@@ -168,26 +168,30 @@
)
}
- internal fun updateDeviceItemOnClick(deviceItem: DeviceItem) {
- logger.logDeviceClick(deviceItem.cachedBluetoothDevice.address, deviceItem.type)
+ internal suspend fun updateDeviceItemOnClick(deviceItem: DeviceItem) {
+ withContext(backgroundDispatcher) {
+ logger.logDeviceClick(deviceItem.cachedBluetoothDevice.address, deviceItem.type)
- deviceItem.cachedBluetoothDevice.apply {
- when (deviceItem.type) {
- DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE -> {
- disconnect()
- uiEventLogger.log(BluetoothTileDialogUiEvent.ACTIVE_DEVICE_DISCONNECT)
- }
- DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> {
- setActive()
- uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_DEVICE_SET_ACTIVE)
- }
- DeviceItemType.CONNECTED_BLUETOOTH_DEVICE -> {
- disconnect()
- uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_OTHER_DEVICE_DISCONNECT)
- }
- DeviceItemType.SAVED_BLUETOOTH_DEVICE -> {
- connect()
- uiEventLogger.log(BluetoothTileDialogUiEvent.SAVED_DEVICE_CONNECT)
+ deviceItem.cachedBluetoothDevice.apply {
+ when (deviceItem.type) {
+ DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE -> {
+ disconnect()
+ uiEventLogger.log(BluetoothTileDialogUiEvent.ACTIVE_DEVICE_DISCONNECT)
+ }
+ DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> {
+ setActive()
+ uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_DEVICE_SET_ACTIVE)
+ }
+ DeviceItemType.CONNECTED_BLUETOOTH_DEVICE -> {
+ disconnect()
+ uiEventLogger.log(
+ BluetoothTileDialogUiEvent.CONNECTED_OTHER_DEVICE_DISCONNECT
+ )
+ }
+ DeviceItemType.SAVED_BLUETOOTH_DEVICE -> {
+ connect()
+ uiEventLogger.log(BluetoothTileDialogUiEvent.SAVED_DEVICE_CONNECT)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
index c390695b..cfb5442 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.tiles.impl.airplane.domain
-import android.content.Context
+import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
@@ -26,29 +27,27 @@
import javax.inject.Inject
/** Maps [AirplaneModeTileModel] to [QSTileState]. */
-class AirplaneModeMapper @Inject constructor(private val context: Context) :
+class AirplaneModeMapper @Inject constructor(@Main private val resources: Resources) :
QSTileDataToStateMapper<AirplaneModeTileModel> {
override fun map(config: QSTileConfig, data: AirplaneModeTileModel): QSTileState =
- QSTileState.build(context, config.uiConfig) {
+ QSTileState.build(resources, config.uiConfig) {
val icon =
- Icon.Loaded(
- context.getDrawable(
- if (data.isEnabled) {
- R.drawable.qs_airplane_icon_on
- } else {
- R.drawable.qs_airplane_icon_off
- }
- )!!,
+ Icon.Resource(
+ if (data.isEnabled) {
+ R.drawable.qs_airplane_icon_on
+ } else {
+ R.drawable.qs_airplane_icon_off
+ },
contentDescription = null
)
this.icon = { icon }
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_airplane)[2]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_airplane)[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_airplane)[1]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_airplane)[1]
}
contentDescription = label
supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt
new file mode 100644
index 0000000..869f6f32
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.commons
+
+import android.service.quicksettings.Tile
+
+fun Tile.copy(): Tile =
+ Tile().also {
+ it.icon = icon
+ it.label = label
+ it.subtitle = subtitle
+ it.contentDescription = contentDescription
+ it.stateDescription = stateDescription
+ it.activityLaunchForClick = activityLaunchForClick
+ it.state = state
+ }
+
+fun Tile.setFrom(otherTile: Tile) {
+ if (otherTile.icon != null) {
+ icon = otherTile.icon
+ }
+ if (otherTile.customLabel != null) {
+ label = otherTile.customLabel
+ }
+ if (otherTile.subtitle != null) {
+ subtitle = otherTile.subtitle
+ }
+ if (otherTile.contentDescription != null) {
+ contentDescription = otherTile.contentDescription
+ }
+ if (otherTile.stateDescription != null) {
+ stateDescription = otherTile.stateDescription
+ }
+ activityLaunchForClick = otherTile.activityLaunchForClick
+ state = otherTile.state
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
new file mode 100644
index 0000000..ca5302e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.data.repository
+
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.external.CustomTileStatePersister
+import com.android.systemui.qs.external.TileServiceKey
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.commons.copy
+import com.android.systemui.qs.tiles.impl.custom.commons.setFrom
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import kotlinx.coroutines.withContext
+
+/**
+ * Repository store the [Tile] associated with the custom tile. It lives on [QSTileScope] which
+ * allows it to survive service rebinding. Given that, it provides the last received state when
+ * connected again.
+ */
+interface CustomTileRepository {
+
+ /**
+ * Restores the [Tile] if it's [isPersistable]. Restored [Tile] will be available via [getTile]
+ * (but there is no guarantee that restoration is synchronous) and emitted in [getTiles] for a
+ * corresponding [user].
+ */
+ suspend fun restoreForTheUserIfNeeded(user: UserHandle, isPersistable: Boolean)
+
+ /** Returns [Tile] updates for a [user]. */
+ fun getTiles(user: UserHandle): Flow<Tile>
+
+ /**
+ * Return current [Tile] for a [user] or null if the [user] doesn't match currently cached one.
+ * Suspending until [getTiles] returns something is a way to wait for this to become available.
+ *
+ * @throws IllegalStateException when there is no current tile.
+ */
+ fun getTile(user: UserHandle): Tile?
+
+ /**
+ * Updates tile with the non-null values from [newTile]. Overwrites the current cache when
+ * [user] differs from the cached one. [isPersistable] tile will be persisted to be possibly
+ * loaded when the [restoreForTheUserIfNeeded].
+ */
+ suspend fun updateWithTile(
+ user: UserHandle,
+ newTile: Tile,
+ isPersistable: Boolean,
+ )
+
+ /**
+ * Updates tile with the values from [defaults]. Overwrites the current cache when [user]
+ * differs from the cached one. [isPersistable] tile will be persisted to be possibly loaded
+ * when the [restoreForTheUserIfNeeded].
+ */
+ suspend fun updateWithDefaults(
+ user: UserHandle,
+ defaults: CustomTileDefaults,
+ isPersistable: Boolean,
+ )
+}
+
+@QSTileScope
+class CustomTileRepositoryImpl
+@Inject
+constructor(
+ private val tileSpec: TileSpec.CustomTileSpec,
+ private val customTileStatePersister: CustomTileStatePersister,
+ @Background private val backgroundContext: CoroutineContext,
+) : CustomTileRepository {
+
+ private val tileUpdateMutex = Mutex()
+ private val tileWithUserState =
+ MutableSharedFlow<TileWithUser>(onBufferOverflow = BufferOverflow.DROP_OLDEST, replay = 1)
+
+ override suspend fun restoreForTheUserIfNeeded(user: UserHandle, isPersistable: Boolean) {
+ if (isPersistable && getCurrentTileWithUser()?.user != user) {
+ withContext(backgroundContext) {
+ customTileStatePersister.readState(user.getKey())?.let {
+ updateWithTile(
+ user,
+ it,
+ true,
+ )
+ }
+ }
+ }
+ }
+
+ override fun getTiles(user: UserHandle): Flow<Tile> =
+ tileWithUserState.filter { it.user == user }.map { it.tile }
+
+ override fun getTile(user: UserHandle): Tile? {
+ val tileWithUser =
+ getCurrentTileWithUser() ?: throw IllegalStateException("Tile is not set")
+ return if (tileWithUser.user == user) {
+ tileWithUser.tile
+ } else {
+ null
+ }
+ }
+
+ override suspend fun updateWithTile(
+ user: UserHandle,
+ newTile: Tile,
+ isPersistable: Boolean,
+ ) = updateTile(user, isPersistable) { setFrom(newTile) }
+
+ override suspend fun updateWithDefaults(
+ user: UserHandle,
+ defaults: CustomTileDefaults,
+ isPersistable: Boolean,
+ ) {
+ if (defaults is CustomTileDefaults.Result) {
+ updateTile(user, isPersistable) {
+ // Update the icon if it's not set or is the default icon.
+ val updateIcon = (icon == null || icon.isResourceEqual(defaults.icon))
+ if (updateIcon) {
+ icon = defaults.icon
+ }
+ setDefaultLabel(defaults.label)
+ }
+ }
+ }
+
+ private suspend fun updateTile(
+ user: UserHandle,
+ isPersistable: Boolean,
+ update: Tile.() -> Unit
+ ): Unit =
+ tileUpdateMutex.withLock {
+ val currentTileWithUser = getCurrentTileWithUser()
+ val tileToUpdate =
+ if (currentTileWithUser?.user == user) {
+ currentTileWithUser.tile.copy()
+ } else {
+ Tile()
+ }
+ tileToUpdate.update()
+ if (isPersistable) {
+ withContext(backgroundContext) {
+ customTileStatePersister.persistState(user.getKey(), tileToUpdate)
+ }
+ }
+ tileWithUserState.tryEmit(TileWithUser(user, tileToUpdate))
+ }
+
+ private fun getCurrentTileWithUser(): TileWithUser? = tileWithUserState.replayCache.lastOrNull()
+
+ /** Compare two icons, only works for resources. */
+ private fun Icon.isResourceEqual(icon2: Icon?): Boolean {
+ if (icon2 == null) {
+ return false
+ }
+ if (this === icon2) {
+ return true
+ }
+ if (type != Icon.TYPE_RESOURCE || icon2.type != Icon.TYPE_RESOURCE) {
+ return false
+ }
+ if (resId != icon2.resId) {
+ return false
+ }
+ return resPackage == icon2.resPackage
+ }
+
+ private fun UserHandle.getKey() = TileServiceKey(tileSpec.componentName, this.identifier)
+
+ private data class TileWithUser(val user: UserHandle, val tile: Tile)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
index 83767aa..d956fde 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
@@ -24,6 +24,8 @@
import com.android.systemui.qs.tiles.impl.custom.CustomTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepositoryImpl
import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent
import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import dagger.Binds
@@ -50,4 +52,6 @@
fun bindCustomTileDefaultsRepository(
impl: CustomTileDefaultsRepositoryImpl
): CustomTileDefaultsRepository
+
+ @Binds fun bindCustomTileRepository(impl: CustomTileRepositoryImpl): CustomTileRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
new file mode 100644
index 0000000..351bba5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.domain.interactor
+
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.external.TileServiceManager
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository
+import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundScope
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.firstOrNull
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+/** Manages updates of the [Tile] assigned for the current custom tile. */
+@CustomTileBoundScope
+class CustomTileInteractor
+@Inject
+constructor(
+ private val user: UserHandle,
+ private val defaultsRepository: CustomTileDefaultsRepository,
+ private val customTileRepository: CustomTileRepository,
+ private val tileServiceManager: TileServiceManager,
+ @CustomTileBoundScope private val boundScope: CoroutineScope,
+ @Background private val backgroundContext: CoroutineContext,
+) {
+
+ private val tileUpdates =
+ MutableSharedFlow<Tile>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+
+ /** [Tile] updates. [updateTile] to emit a new one. */
+ val tiles: Flow<Tile>
+ get() = customTileRepository.getTiles(user)
+
+ /**
+ * Current [Tile]
+ *
+ * @throws IllegalStateException when the repository stores a tile for another user. This means
+ * the tile hasn't been updated for the current user. Can happen when this is accessed before
+ * [init] returns.
+ */
+ val tile: Tile
+ get() =
+ customTileRepository.getTile(user)
+ ?: throw IllegalStateException("Attempt to get a tile for a wrong user")
+
+ /**
+ * Initializes the repository for the current user. Suspends until it's safe to call [tile]
+ * which needs at least one of the following:
+ * - defaults are loaded;
+ * - receive tile update in [updateTile];
+ * - restoration happened for a persisted tile.
+ */
+ suspend fun init() {
+ launchUpdates()
+ customTileRepository.restoreForTheUserIfNeeded(user, tileServiceManager.isActiveTile)
+ // Suspend to make sure it gets the tile from one of the sources: restoration, defaults, or
+ // tile update.
+ customTileRepository.getTiles(user).firstOrNull()
+ }
+
+ private fun launchUpdates() {
+ tileUpdates
+ .onEach {
+ customTileRepository.updateWithTile(
+ user,
+ it,
+ tileServiceManager.isActiveTile,
+ )
+ }
+ .flowOn(backgroundContext)
+ .launchIn(boundScope)
+ defaultsRepository
+ .defaults(user)
+ .onEach {
+ customTileRepository.updateWithDefaults(
+ user,
+ it,
+ tileServiceManager.isActiveTile,
+ )
+ }
+ .flowOn(backgroundContext)
+ .launchIn(boundScope)
+ }
+
+ /** Updates current [Tile]. Emits a new event in [tiles]. */
+ fun updateTile(newTile: Tile) {
+ tileUpdates.tryEmit(newTile)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
index b2b22646..881a6bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.tiles.impl.flashlight.domain
-import android.content.Context
+import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
@@ -26,30 +27,28 @@
import javax.inject.Inject
/** Maps [FlashlightTileModel] to [QSTileState]. */
-class FlashlightMapper @Inject constructor(private val context: Context) :
+class FlashlightMapper @Inject constructor(@Main private val resources: Resources) :
QSTileDataToStateMapper<FlashlightTileModel> {
override fun map(config: QSTileConfig, data: FlashlightTileModel): QSTileState =
- QSTileState.build(context, config.uiConfig) {
+ QSTileState.build(resources, config.uiConfig) {
val icon =
- Icon.Loaded(
- context.resources.getDrawable(
- if (data.isEnabled) {
- R.drawable.qs_flashlight_icon_on
- } else {
- R.drawable.qs_flashlight_icon_off
- }
- ),
+ Icon.Resource(
+ if (data.isEnabled) {
+ R.drawable.qs_flashlight_icon_on
+ } else {
+ R.drawable.qs_flashlight_icon_off
+ },
contentDescription = null
)
this.icon = { icon }
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_flashlight)[2]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_flashlight)[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_flashlight)[1]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_flashlight)[1]
}
contentDescription = label
supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
index 8e53723..7e7034d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
@@ -16,8 +16,9 @@
package com.android.systemui.qs.tiles.impl.location.domain
-import android.content.Context
+import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
@@ -26,32 +27,30 @@
import javax.inject.Inject
/** Maps [LocationTileModel] to [QSTileState]. */
-class LocationTileMapper @Inject constructor(private val context: Context) :
+class LocationTileMapper @Inject constructor(@Main private val resources: Resources) :
QSTileDataToStateMapper<LocationTileModel> {
override fun map(config: QSTileConfig, data: LocationTileModel): QSTileState =
- QSTileState.build(context, config.uiConfig) {
+ QSTileState.build(resources, config.uiConfig) {
val icon =
- Icon.Loaded(
- context.resources.getDrawable(
- if (data.isEnabled) {
- R.drawable.qs_location_icon_on
- } else {
- R.drawable.qs_location_icon_off
- }
- ),
+ Icon.Resource(
+ if (data.isEnabled) {
+ R.drawable.qs_location_icon_on
+ } else {
+ R.drawable.qs_location_icon_off
+ },
contentDescription = null
)
this.icon = { icon }
- this.label = context.resources.getString(R.string.quick_settings_location_label)
+ this.label = resources.getString(R.string.quick_settings_location_label)
if (data.isEnabled) {
activationState = QSTileState.ActivationState.ACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_location)[2]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_location)[2]
} else {
activationState = QSTileState.ActivationState.INACTIVE
- secondaryLabel = context.resources.getStringArray(R.array.tile_states_location)[1]
+ secondaryLabel = resources.getStringArray(R.array.tile_states_location)[1]
}
contentDescription = label
supportedActions =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index f9e0b16..23e0cb6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -16,7 +16,7 @@
package com.android.systemui.qs.tiles.viewmodel
-import android.content.Context
+import android.content.res.Resources
import android.service.quicksettings.Tile
import android.view.View
import android.widget.Switch
@@ -46,13 +46,13 @@
companion object {
fun build(
- context: Context,
+ resources: Resources,
config: QSTileUIConfig,
build: Builder.() -> Unit
): QSTileState =
build(
{ Icon.Resource(config.iconRes, null) },
- context.getString(config.labelRes),
+ resources.getString(config.labelRes),
build,
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 3afbd7c..e8623f9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -101,7 +101,10 @@
override fun addCallback(callback: QSTile.Callback?) {
callback ?: return
- synchronized(callbacks) { callbacks.add(callback) }
+ synchronized(callbacks) {
+ callbacks.add(callback)
+ state?.let(callback::onStateChanged)
+ }
}
override fun removeCallback(callback: QSTile.Callback?) {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index f3f9c91..d42fde6 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -128,7 +128,7 @@
private fun automaticallySwitchScenes() {
applicationScope.launch {
// TODO (b/308001302): Move this to a bouncer specific interactor.
- bouncerInteractor.onImeHidden.collectLatest {
+ bouncerInteractor.onImeHiddenByUser.collectLatest {
if (sceneInteractor.desiredScene.value.key == SceneKey.Bouncer) {
sceneInteractor.changeScene(
scene = SceneModel(SceneKey.Lockscreen),
@@ -146,19 +146,21 @@
isAnySimLocked -> {
switchToScene(
targetSceneKey = SceneKey.Bouncer,
- loggingReason = "Need to authenticate locked sim card."
+ loggingReason = "Need to authenticate locked SIM card."
)
}
- isUnlocked && !canSwipeToEnter -> {
+ isUnlocked && canSwipeToEnter == false -> {
switchToScene(
targetSceneKey = SceneKey.Gone,
- loggingReason = "Sim cards are unlocked."
+ loggingReason = "All SIM cards unlocked and device already" +
+ " unlocked and lockscreen doesn't require a swipe to dismiss."
)
}
else -> {
switchToScene(
targetSceneKey = SceneKey.Lockscreen,
- loggingReason = "Sim cards are unlocked."
+ loggingReason = "All SIM cards unlocked and device still locked" +
+ " or lockscreen still requires a swipe to dismiss."
)
}
}
@@ -205,11 +207,17 @@
// when the user is passively authenticated, the false value here
// when the unlock state changes indicates this is an active
// authentication attempt.
- if (isBypassEnabled || !canSwipeToEnter)
- SceneKey.Gone to
- "device has been unlocked on lockscreen with either " +
- "bypass enabled or using an active authentication mechanism"
- else null
+ when {
+ isBypassEnabled ->
+ SceneKey.Gone to
+ "device has been unlocked on lockscreen with bypass" +
+ " enabled"
+ canSwipeToEnter == false ->
+ SceneKey.Gone to
+ "device has been unlocked on lockscreen using an active" +
+ " authentication mechanism"
+ else -> null
+ }
// Not on lockscreen or bouncer, so remain in the current scene.
else -> null
}
@@ -232,7 +240,7 @@
} else {
val canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value
val isUnlocked = deviceEntryInteractor.isUnlocked.value
- if (isUnlocked && !canSwipeToEnter) {
+ if (isUnlocked && canSwipeToEnter == false) {
switchToScene(
targetSceneKey = SceneKey.Gone,
loggingReason =
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index d14ef35..dbb58a3 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -16,12 +16,14 @@
package com.android.systemui.scene.shared.flag
+import android.content.Context
import androidx.annotation.VisibleForTesting
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.Flags.sceneContainer
import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flag
import com.android.systemui.flags.Flags
@@ -29,6 +31,7 @@
import com.android.systemui.flags.ResourceBooleanFlag
import com.android.systemui.flags.UnreleasedFlag
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
+import com.android.systemui.res.R
import dagger.Module
import dagger.Provides
import dagger.assisted.Assisted
@@ -51,6 +54,7 @@
class SceneContainerFlagsImpl
@AssistedInject
constructor(
+ @Application private val context: Context,
private val featureFlagsClassic: FeatureFlagsClassic,
@Assisted private val isComposeAvailable: Boolean,
) : SceneContainerFlags {
@@ -80,7 +84,11 @@
),
) +
classicFlagTokens.map { flagToken -> FlagMustBeEnabled(flagToken) } +
- listOf(ComposeMustBeAvailable(), CompileTimeFlagMustBeEnabled())
+ listOf(
+ ComposeMustBeAvailable(),
+ CompileTimeFlagMustBeEnabled(),
+ ResourceConfigMustBeEnabled()
+ )
override fun isEnabled(): Boolean {
// SCENE_CONTAINER_ENABLED is an explicit static flag check that helps with downstream
@@ -146,6 +154,14 @@
}
}
+ private inner class ResourceConfigMustBeEnabled : Requirement {
+ override val name: String = "R.bool.config_sceneContainerFrameworkEnabled must be true"
+
+ override fun isMet(): Boolean {
+ return context.resources.getBoolean(R.bool.config_sceneContainerFrameworkEnabled)
+ }
+ }
+
@AssistedFactory
interface Factory {
fun create(isComposeAvailable: Boolean): SceneContainerFlagsImpl
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
index e57a0fd..3f6c58d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.screenrecord
+import android.annotation.SuppressLint
import android.app.Activity
import android.app.PendingIntent
import android.content.Intent
@@ -23,6 +24,7 @@
import android.os.Looper
import android.os.ResultReceiver
import android.os.UserHandle
+import android.view.MotionEvent.ACTION_MOVE
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
@@ -102,11 +104,19 @@
@LayoutRes override fun getOptionsViewLayoutId(): Int = R.layout.screen_record_options
+ @SuppressLint("ClickableViewAccessibility")
private fun initRecordOptionsView() {
audioSwitch = dialog.requireViewById(R.id.screenrecord_audio_switch)
tapsSwitch = dialog.requireViewById(R.id.screenrecord_taps_switch)
+
+ // Add these listeners so that the switch only responds to movement
+ // within its target region, to meet accessibility requirements
+ audioSwitch.setOnTouchListener { _, event -> event.action == ACTION_MOVE }
+ tapsSwitch.setOnTouchListener { _, event -> event.action == ACTION_MOVE }
+
tapsView = dialog.requireViewById(R.id.show_taps)
updateTapsViewVisibility()
+
options = dialog.requireViewById(R.id.screen_recording_options)
val a: ArrayAdapter<*> =
ScreenRecordingAdapter(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index c925010..67ec03f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1302,6 +1302,7 @@
@Override
public void updateResources() {
+ Trace.beginSection("NSSLC#updateResources");
final boolean newSplitShadeEnabled =
mSplitShadeStateController.shouldUseSplitNotificationShade(mResources);
final boolean splitShadeChanged = mSplitShadeEnabled != newSplitShadeEnabled;
@@ -1318,6 +1319,7 @@
mSplitShadeFullTransitionDistance =
mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance);
+ Trace.endSection();
}
private void onSplitShadeEnabledChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index 2a071de..0065db3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -66,10 +66,10 @@
private fun upDestinationSceneKey(
isUnlocked: Boolean,
- canSwipeToDismiss: Boolean,
+ canSwipeToDismiss: Boolean?,
): SceneKey {
return when {
- canSwipeToDismiss -> SceneKey.Lockscreen
+ canSwipeToDismiss == true -> SceneKey.Lockscreen
isUnlocked -> SceneKey.Gone
else -> SceneKey.Lockscreen
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 4a50897..1b096b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -29,8 +29,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
+import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.hardware.input.InputManagerGlobal;
@@ -119,7 +118,6 @@
private final SparseArray<String> mSpecialCharacterNames = new SparseArray<>();
private final SparseArray<String> mModifierNames = new SparseArray<>();
- private final SparseArray<Drawable> mSpecialCharacterDrawables = new SparseArray<>();
private final SparseArray<Drawable> mModifierDrawables = new SparseArray<>();
// Ordered list of modifiers that are supported. All values in this array must exist in
// mModifierNames.
@@ -146,7 +144,7 @@
} else {
this.mWindowManager = mContext.getSystemService(WindowManager.class);
}
- loadResources(context);
+ loadResources(this.mContext);
createHardcodedShortcuts();
}
@@ -287,7 +285,7 @@
mSpecialCharacterNames.put(
KeyEvent.KEYCODE_NUM_LOCK, context.getString(R.string.keyboard_key_num_lock));
mSpecialCharacterNames.put(KeyEvent.KEYCODE_MINUS, "-");
- mSpecialCharacterNames.put(KeyEvent.KEYCODE_GRAVE, "~");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_GRAVE, "`");
mSpecialCharacterNames.put(KeyEvent.KEYCODE_EQUALS, "=");
mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_0,
@@ -350,19 +348,6 @@
mModifierNames.put(KeyEvent.META_SYM_ON, "Sym");
mModifierNames.put(KeyEvent.META_FUNCTION_ON, "Fn");
- mSpecialCharacterDrawables.put(
- KeyEvent.KEYCODE_DEL, context.getDrawable(R.drawable.ic_ksh_key_backspace));
- mSpecialCharacterDrawables.put(
- KeyEvent.KEYCODE_ENTER, context.getDrawable(R.drawable.ic_ksh_key_enter));
- mSpecialCharacterDrawables.put(
- KeyEvent.KEYCODE_DPAD_UP, context.getDrawable(R.drawable.ic_ksh_key_up));
- mSpecialCharacterDrawables.put(
- KeyEvent.KEYCODE_DPAD_RIGHT, context.getDrawable(R.drawable.ic_ksh_key_right));
- mSpecialCharacterDrawables.put(
- KeyEvent.KEYCODE_DPAD_DOWN, context.getDrawable(R.drawable.ic_ksh_key_down));
- mSpecialCharacterDrawables.put(
- KeyEvent.KEYCODE_DPAD_LEFT, context.getDrawable(R.drawable.ic_ksh_key_left));
-
mModifierDrawables.put(
KeyEvent.META_META_ON, context.getDrawable(R.drawable.ic_ksh_key_meta));
}
@@ -508,7 +493,7 @@
Arrays.asList(
Pair.create(KeyEvent.KEYCODE_SLASH, KeyEvent.META_META_ON))),
/* Back: go back to previous state (back button) */
- /* Meta + ~, Meta + backspace, Meta + left arrow */
+ /* Meta + Grave, Meta + backspace, Meta + left arrow */
new ShortcutKeyGroupMultiMappingInfo(
context.getString(R.string.group_system_go_back),
Arrays.asList(
@@ -826,11 +811,12 @@
new BottomSheetDialog(mContext);
final View keyboardShortcutsView = inflater.inflate(
R.layout.keyboard_shortcuts_search_view, null);
+ LinearLayout shortcutsContainer = keyboardShortcutsView.findViewById(
+ R.id.keyboard_shortcuts_container);
mNoSearchResults = keyboardShortcutsView.findViewById(R.id.shortcut_search_no_result);
mKeyboardShortcutsBottomSheetDialog.setContentView(keyboardShortcutsView);
setButtonsDefaultStatus(keyboardShortcutsView);
- populateKeyboardShortcutSearchList(
- keyboardShortcutsView.findViewById(R.id.keyboard_shortcuts_container));
+ populateKeyboardShortcutSearchList(shortcutsContainer);
// Workaround for solve issue about dialog not full expanded when landscape.
FrameLayout bottomSheet = (FrameLayout)
@@ -880,9 +866,14 @@
@Override
public void afterTextChanged(Editable s) {
mQueryString = s.toString();
- populateKeyboardShortcutSearchList(
- keyboardShortcutsView.findViewById(
- R.id.keyboard_shortcuts_container));
+ populateKeyboardShortcutSearchList(shortcutsContainer);
+ if (mNoSearchResults.getVisibility() == View.VISIBLE) {
+ shortcutsContainer.setAccessibilityPaneTitle(mContext.getString(
+ R.string.keyboard_shortcut_search_list_no_result));
+ } else if (mSearchEditText.getText().length() > 0) {
+ shortcutsContainer.setAccessibilityPaneTitle(mContext.getString(
+ R.string.keyboard_shortcut_a11y_show_search_results));
+ }
}
@Override
@@ -1034,16 +1025,32 @@
StringDrawableContainer shortcutRepresentation = shortcutKeys.get(k);
if (shortcutRepresentation.mDrawable != null) {
ImageView shortcutKeyIconView = (ImageView) inflater.inflate(
- R.layout.keyboard_shortcuts_key_new_icon_view,
+ R.layout.keyboard_shortcuts_key_icon_view,
shortcutItemsContainer,
false);
- Bitmap bitmap = Bitmap.createBitmap(shortcutKeyIconItemHeightWidth,
- shortcutKeyIconItemHeightWidth, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- shortcutRepresentation.mDrawable.setBounds(0, 0, canvas.getWidth(),
- canvas.getHeight());
- shortcutRepresentation.mDrawable.draw(canvas);
- shortcutKeyIconView.setImageBitmap(bitmap);
+ shortcutKeyIconView.setImageDrawable(
+ shortcutRepresentation.mDrawable);
+ // Once the view has been measured, scale and position the icon in
+ // the center.
+ shortcutKeyIconView.post(() -> {
+ Drawable d = shortcutKeyIconView.getDrawable();
+
+ float newSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.ksh_icon_scaled_size);
+ int viewWidth = shortcutKeyIconView.getWidth();
+ int viewHeight = shortcutKeyIconView.getHeight();
+ float scaleFactor = newSize / d.getIntrinsicWidth();
+ // Assumes that top/bottom and left/right padding are equal.
+ int paddingHorizontal = shortcutKeyIconView.getPaddingLeft();
+ int paddingVertical = shortcutKeyIconView.getPaddingTop();
+
+ Matrix m = new Matrix();
+ m.postScale(scaleFactor, scaleFactor);
+ m.postTranslate(
+ (viewWidth - newSize) / 2 - paddingHorizontal,
+ (viewHeight - newSize) / 2 - paddingVertical);
+ shortcutKeyIconView.setImageMatrix(m);
+ });
shortcutKeyIconView.setImportantForAccessibility(
IMPORTANT_FOR_ACCESSIBILITY_YES);
shortcutKeyIconView.setAccessibilityDelegate(
@@ -1052,7 +1059,7 @@
shortcutItemsContainer.addView(shortcutKeyIconView);
} else if (shortcutRepresentation.mString != null) {
TextView shortcutKeyTextView = (TextView) inflater.inflate(
- R.layout.keyboard_shortcuts_key_new_view,
+ R.layout.keyboard_shortcuts_key_view,
shortcutItemsContainer,
false);
shortcutKeyTextView.setMinimumWidth(shortcutKeyTextItemMinWidth);
@@ -1062,18 +1069,10 @@
shortcutRepresentation.mString));
shortcutItemsContainer.addView(shortcutKeyTextView);
}
-
- if (k < shortcutKeysSize - 1) {
- TextView shortcutKeyTextView = (TextView) inflater.inflate(
- R.layout.keyboard_shortcuts_key_plus_view,
- shortcutItemsContainer,
- false);
- shortcutItemsContainer.addView(shortcutKeyTextView);
- }
}
} else {
TextView shortcutKeyTextView = (TextView) inflater.inflate(
- R.layout.keyboard_shortcuts_key_new_view,
+ R.layout.keyboard_shortcuts_key_view,
shortcutItemsContainer,
false);
shortcutKeyTextView.setMinimumWidth(shortcutKeyTextItemMinWidth);
@@ -1085,7 +1084,7 @@
if (p < keyGroupItemsSize - 1) {
TextView shortcutKeyTextView = (TextView) inflater.inflate(
- R.layout.keyboard_shortcuts_key_vertical_bar_view,
+ R.layout.keyboard_shortcuts_key_separator_view,
shortcutItemsContainer,
false);
shortcutItemsContainer.addView(shortcutKeyTextView);
@@ -1124,9 +1123,6 @@
Drawable shortcutKeyDrawable = null;
if (info.getBaseCharacter() > Character.MIN_VALUE) {
shortcutKeyString = String.valueOf(info.getBaseCharacter());
- } else if (mSpecialCharacterDrawables.get(info.getKeycode()) != null) {
- shortcutKeyDrawable = mSpecialCharacterDrawables.get(info.getKeycode());
- shortcutKeyString = mSpecialCharacterNames.get(info.getKeycode());
} else if (mSpecialCharacterNames.get(info.getKeycode()) != null) {
shortcutKeyString = mSpecialCharacterNames.get(info.getKeycode());
} else {
@@ -1232,28 +1228,35 @@
mButtonOpenApps = keyboardShortcutsView.findViewById(R.id.shortcut_open_apps);
mButtonSpecificApp = keyboardShortcutsView.findViewById(R.id.shortcut_specific_app);
+ LinearLayout shortcutsContainer = keyboardShortcutsView.findViewById(
+ R.id.keyboard_shortcuts_container);
+
mButtonSystem.setOnClickListener(v -> {
setCurrentCategoryIndex(SHORTCUT_SYSTEM_INDEX);
- populateKeyboardShortcutSearchList(keyboardShortcutsView.findViewById(
- R.id.keyboard_shortcuts_container));
+ populateKeyboardShortcutSearchList(shortcutsContainer);
+ shortcutsContainer.setAccessibilityPaneTitle(mContext.getString(
+ R.string.keyboard_shortcut_a11y_filter_system));
});
mButtonInput.setOnClickListener(v -> {
setCurrentCategoryIndex(SHORTCUT_INPUT_INDEX);
- populateKeyboardShortcutSearchList(keyboardShortcutsView.findViewById(
- R.id.keyboard_shortcuts_container));
+ populateKeyboardShortcutSearchList(shortcutsContainer);
+ shortcutsContainer.setAccessibilityPaneTitle(mContext.getString(
+ R.string.keyboard_shortcut_a11y_filter_input));
});
mButtonOpenApps.setOnClickListener(v -> {
setCurrentCategoryIndex(SHORTCUT_OPENAPPS_INDEX);
- populateKeyboardShortcutSearchList(keyboardShortcutsView.findViewById(
- R.id.keyboard_shortcuts_container));
+ populateKeyboardShortcutSearchList(shortcutsContainer);
+ shortcutsContainer.setAccessibilityPaneTitle(mContext.getString(
+ R.string.keyboard_shortcut_a11y_filter_open_apps));
});
mButtonSpecificApp.setOnClickListener(v -> {
setCurrentCategoryIndex(SHORTCUT_SPECIFICAPP_INDEX);
- populateKeyboardShortcutSearchList(keyboardShortcutsView.findViewById(
- R.id.keyboard_shortcuts_container));
+ populateKeyboardShortcutSearchList(shortcutsContainer);
+ shortcutsContainer.setAccessibilityPaneTitle(mContext.getString(
+ R.string.keyboard_shortcut_a11y_filter_current_app));
});
mFullButtonList.add(mButtonSystem);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 61eaff9..acb00d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -85,7 +85,6 @@
private final SparseArray<String> mSpecialCharacterNames = new SparseArray<>();
private final SparseArray<String> mModifierNames = new SparseArray<>();
- private final SparseArray<Drawable> mSpecialCharacterDrawables = new SparseArray<>();
private final SparseArray<Drawable> mModifierDrawables = new SparseArray<>();
// Ordered list of modifiers that are supported. All values in this array must exist in
// mModifierNames.
@@ -340,19 +339,6 @@
mModifierNames.put(KeyEvent.META_SYM_ON, "Sym");
mModifierNames.put(KeyEvent.META_FUNCTION_ON, "Fn");
- mSpecialCharacterDrawables.put(
- KeyEvent.KEYCODE_DEL, context.getDrawable(R.drawable.ic_ksh_key_backspace));
- mSpecialCharacterDrawables.put(
- KeyEvent.KEYCODE_ENTER, context.getDrawable(R.drawable.ic_ksh_key_enter));
- mSpecialCharacterDrawables.put(
- KeyEvent.KEYCODE_DPAD_UP, context.getDrawable(R.drawable.ic_ksh_key_up));
- mSpecialCharacterDrawables.put(
- KeyEvent.KEYCODE_DPAD_RIGHT, context.getDrawable(R.drawable.ic_ksh_key_right));
- mSpecialCharacterDrawables.put(
- KeyEvent.KEYCODE_DPAD_DOWN, context.getDrawable(R.drawable.ic_ksh_key_down));
- mSpecialCharacterDrawables.put(
- KeyEvent.KEYCODE_DPAD_LEFT, context.getDrawable(R.drawable.ic_ksh_key_left));
-
mModifierDrawables.put(
KeyEvent.META_META_ON, context.getDrawable(R.drawable.ic_ksh_key_meta));
}
@@ -747,9 +733,6 @@
Drawable shortcutKeyDrawable = null;
if (info.getBaseCharacter() > Character.MIN_VALUE) {
shortcutKeyString = String.valueOf(info.getBaseCharacter());
- } else if (mSpecialCharacterDrawables.get(info.getKeycode()) != null) {
- shortcutKeyDrawable = mSpecialCharacterDrawables.get(info.getKeycode());
- shortcutKeyString = mSpecialCharacterNames.get(info.getKeycode());
} else if (mSpecialCharacterNames.get(info.getKeycode()) != null) {
shortcutKeyString = mSpecialCharacterNames.get(info.getKeycode());
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 0e83c78..e486457 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -530,16 +530,12 @@
userHandle = mCurrentUserId;
}
if (mUsersUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
- // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
- // default value before moving to 'released'
- Log.wtf(TAG, "Asking for redact notifs setting too early", new Throwable());
- updateUserShowPrivateSettings(userHandle);
+ Log.i(TAG, "Asking for redact notifs setting too early", new Throwable());
+ return false;
}
if (mUsersDpcAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
- // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
- // default value before moving to 'released'
- Log.wtf(TAG, "Asking for redact notifs dpm override too early", new Throwable());
- updateDpcSettings(userHandle);
+ Log.i(TAG, "Asking for redact notifs dpm override too early", new Throwable());
+ return false;
}
return mUsersUsersAllowingPrivateNotifications.get(userHandle)
&& mUsersDpcAllowingPrivateNotifications.get(userHandle);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
index 0299114..e0eee96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
@@ -34,34 +34,38 @@
viewModel: FooterViewModel,
clearAllNotifications: View.OnClickListener,
): DisposableHandle {
- // Listen for changes when the view is attached.
+ // Bind the resource IDs
+ footer.setMessageString(viewModel.message.messageId)
+ footer.setMessageIcon(viewModel.message.iconId)
+ footer.setClearAllButtonText(viewModel.clearAllButton.labelId)
+ footer.setClearAllButtonDescription(viewModel.clearAllButton.accessibilityDescriptionId)
+
+ // Bind the click listeners
+ footer.setClearAllButtonClickListener(clearAllNotifications)
+
+ // Listen for visibility changes when the view is attached.
return footer.repeatWhenAttached {
lifecycleScope.launch {
- viewModel.clearAllButton.collect { button ->
- if (button.isVisible.isAnimating) {
+ viewModel.clearAllButton.isVisible.collect { isVisible ->
+ if (isVisible.isAnimating) {
footer.setClearAllButtonVisible(
- button.isVisible.value,
+ isVisible.value,
/* animate = */ true,
) { _ ->
- button.isVisible.stopAnimating()
+ isVisible.stopAnimating()
}
} else {
footer.setClearAllButtonVisible(
- button.isVisible.value,
+ isVisible.value,
/* animate = */ false,
)
}
- footer.setClearAllButtonText(button.labelId)
- footer.setClearAllButtonDescription(button.accessibilityDescriptionId)
- footer.setClearAllButtonClickListener(clearAllNotifications)
}
}
lifecycleScope.launch {
- viewModel.message.collect { message ->
- footer.setFooterLabelVisible(message.visible)
- footer.setMessageString(message.messageId)
- footer.setMessageIcon(message.iconId)
+ viewModel.message.isVisible.collect { visible ->
+ footer.setFooterLabelVisible(visible)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt
index ea5abef..244555a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt
@@ -18,9 +18,10 @@
import android.annotation.StringRes
import com.android.systemui.util.ui.AnimatedValue
+import kotlinx.coroutines.flow.Flow
data class FooterButtonViewModel(
@StringRes val labelId: Int,
@StringRes val accessibilityDescriptionId: Int,
- val isVisible: AnimatedValue<Boolean>,
+ val isVisible: Flow<AnimatedValue<Boolean>>,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt
index bc912fb..85cd397 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt
@@ -18,10 +18,11 @@
import android.annotation.DrawableRes
import android.annotation.StringRes
+import kotlinx.coroutines.flow.StateFlow
/** A ViewModel for the string message that can be shown in the footer. */
data class FooterMessageViewModel(
@StringRes val messageId: Int,
@DrawableRes val iconId: Int,
- val visible: Boolean,
+ val isVisible: StateFlow<Boolean>,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
index 721bea1..e6b0abc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
@@ -30,9 +30,7 @@
import dagger.Provides
import java.util.Optional
import javax.inject.Provider
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
/** ViewModel for [FooterView]. */
@@ -41,36 +39,32 @@
seenNotificationsInteractor: SeenNotificationsInteractor,
shadeInteractor: ShadeInteractor,
) {
- val clearAllButton: Flow<FooterButtonViewModel> =
- activeNotificationsInteractor.hasClearableNotifications
- .sample(
- combine(
- shadeInteractor.isShadeFullyExpanded,
- shadeInteractor.isShadeTouchable,
- ::Pair
- )
- .onStart { emit(Pair(false, false)) }
- ) { hasClearableNotifications, (isShadeFullyExpanded, animationsEnabled) ->
- val shouldAnimate = isShadeFullyExpanded && animationsEnabled
- AnimatableEvent(hasClearableNotifications, shouldAnimate)
- }
- .toAnimatedValueFlow()
- .map { visible ->
- FooterButtonViewModel(
- labelId = R.string.clear_all_notifications_text,
- accessibilityDescriptionId = R.string.accessibility_clear_all,
- isVisible = visible,
- )
- }
+ val clearAllButton: FooterButtonViewModel =
+ FooterButtonViewModel(
+ labelId = R.string.clear_all_notifications_text,
+ accessibilityDescriptionId = R.string.accessibility_clear_all,
+ isVisible =
+ activeNotificationsInteractor.hasClearableNotifications
+ .sample(
+ combine(
+ shadeInteractor.isShadeFullyExpanded,
+ shadeInteractor.isShadeTouchable,
+ ::Pair
+ )
+ .onStart { emit(Pair(false, false)) }
+ ) { hasClearableNotifications, (isShadeFullyExpanded, animationsEnabled) ->
+ val shouldAnimate = isShadeFullyExpanded && animationsEnabled
+ AnimatableEvent(hasClearableNotifications, shouldAnimate)
+ }
+ .toAnimatedValueFlow(),
+ )
- val message: Flow<FooterMessageViewModel> =
- seenNotificationsInteractor.hasFilteredOutSeenNotifications.map { hasFilteredOutNotifs ->
- FooterMessageViewModel(
- messageId = R.string.unlock_to_see_notif_text,
- iconId = R.drawable.ic_friction_lock_closed,
- visible = hasFilteredOutNotifs,
- )
- }
+ val message: FooterMessageViewModel =
+ FooterMessageViewModel(
+ messageId = R.string.unlock_to_see_notif_text,
+ iconId = R.drawable.ic_friction_lock_closed,
+ isVisible = seenNotificationsInteractor.hasFilteredOutSeenNotifications,
+ )
}
@Module
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 64f61d9..8eda96f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -27,7 +27,6 @@
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
@@ -43,11 +42,9 @@
* A view that can be used for both the dimmed and normal background of an notification.
*/
public class NotificationBackgroundView extends View implements Dumpable {
- private static final String TAG = "NotificationBackgroundView";
private final boolean mDontModifyCorners;
private Drawable mBackground;
- private Drawable mBackgroundDrawableToTint;
private int mClipTopAmount;
private int mClipBottomAmount;
private int mTintColor;
@@ -134,7 +131,6 @@
unscheduleDrawable(mBackground);
}
mBackground = background;
- mBackgroundDrawableToTint = findBackgroundDrawableToTint(mBackground);
mRippleColor = null;
mBackground.mutate();
if (mBackground != null) {
@@ -148,46 +144,25 @@
invalidate();
}
- // setCustomBackground should be called from ActivatableNotificationView.initBackground
- // with R.drawable.notification_material_bg, which is a layer-list with a lower layer
- // for the background color (annotated with an ID so we can find it) and an upper layer
- // to blend in the stateful @color/notification_overlay_color.
- //
- // If the notification is tinted, we want to set a tint list on *just that lower layer* that
- // will replace the default materialColorSurfaceContainerHigh *without* wiping out the stateful
- // tints in the upper layer that make the hovered and pressed states visible.
- //
- // This function fishes that lower layer out, or makes a fuss in logcat if it can't find it.
- private @Nullable Drawable findBackgroundDrawableToTint(@Nullable Drawable background) {
- if (background == null) {
- return null;
- }
-
- if (!(background instanceof LayerDrawable)) {
- Log.wtf(TAG, "background is not a LayerDrawable: " + background);
- return background;
- }
-
- final Drawable backgroundColorLayer = ((LayerDrawable) background).findDrawableByLayerId(
- R.id.notification_background_color_layer);
-
- if (backgroundColorLayer == null) {
- Log.wtf(TAG, "background is missing background color layer: " + background);
- return background;
- }
-
- return backgroundColorLayer;
- }
-
public void setCustomBackground(int drawableResId) {
final Drawable d = mContext.getDrawable(drawableResId);
setCustomBackground(d);
}
public void setTint(int tintColor) {
- mBackgroundDrawableToTint.setTint(tintColor);
- mBackgroundDrawableToTint.setTintMode(PorterDuff.Mode.SRC_ATOP);
+ if (tintColor != 0) {
+ ColorStateList stateList = new ColorStateList(new int[][]{
+ new int[]{com.android.internal.R.attr.state_pressed},
+ new int[]{com.android.internal.R.attr.state_hovered},
+ new int[]{}},
+ new int[]{tintColor, tintColor, tintColor}
+ );
+ mBackground.setTintMode(PorterDuff.Mode.SRC_ATOP);
+ mBackground.setTintList(stateList);
+ } else {
+ mBackground.setTintList(null);
+ }
mTintColor = tintColor;
invalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 6944453..3bbdfd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1275,6 +1275,7 @@
* modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
*/
private void updateChildren() {
+ Trace.beginSection("NSSL#updateChildren");
updateScrollStateForAddedChildren();
mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
? 0
@@ -1285,6 +1286,7 @@
} else {
startAnimationToState();
}
+ Trace.endSection();
}
private void onPreDrawDuringAnimation() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index 4554085..a4e1a9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -24,10 +24,12 @@
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.reinflateAndBindLatest
import com.android.systemui.common.ui.view.setImportantForAccessibilityYesNo
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.NotificationShelf
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore
@@ -40,6 +42,7 @@
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.policy.ConfigurationController
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@@ -48,12 +51,13 @@
@Inject
constructor(
private val viewModel: NotificationListViewModel,
- private val metricsLogger: MetricsLogger,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
private val configuration: ConfigurationState,
private val configurationController: ConfigurationController,
private val falsingManager: FalsingManager,
private val iconAreaController: NotificationIconAreaController,
private val iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
+ private val metricsLogger: MetricsLogger,
private val shelfIconViewStore: ShelfNotificationIconViewStore,
) {
@@ -62,14 +66,17 @@
viewController: NotificationStackScrollLayoutController
) {
bindShelf(view)
- bindFooter(view)
- bindEmptyShade(view)
bindHideList(viewController, viewModel)
- view.repeatWhenAttached {
- lifecycleScope.launch {
- viewModel.isImportantForAccessibility.collect { isImportantForAccessibility ->
- view.setImportantForAccessibilityYesNo(isImportantForAccessibility)
+ if (FooterViewRefactor.isEnabled) {
+ bindFooter(view)
+ bindEmptyShade(view)
+
+ view.repeatWhenAttached {
+ lifecycleScope.launch {
+ viewModel.isImportantForAccessibility.collect { isImportantForAccessibility ->
+ view.setImportantForAccessibilityYesNo(isImportantForAccessibility)
+ }
}
}
}
@@ -101,6 +108,7 @@
R.layout.status_bar_notification_footer,
parentView,
attachToRoot = false,
+ backgroundDispatcher,
) { footerView: FooterView ->
traceSection("bind FooterView") {
val disposableHandle =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 674f169..a3d316b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -22,6 +22,7 @@
import android.hardware.biometrics.BiometricSourceType
import android.provider.Settings
import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.ListenersTracing.forEachTraced
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -186,7 +187,9 @@
}
}
- private fun notifyListeners() = listeners.forEach { it.onBypassStateChanged(bypassEnabled) }
+ private fun notifyListeners() = listeners.forEachTraced("KeyguardBypassController") {
+ it.onBypassStateChanged(bypassEnabled)
+ }
/**
* Notify that the biometric unlock has happened.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
index bbba19d..87df180 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
@@ -73,7 +73,11 @@
/** Callback to be notified about device posture changes. */
interface Callback {
- /** Called when the posture changes. */
+ /**
+ * Called when the posture changes. If there are multiple active displays ("concurrent"),
+ * this will report the physical posture of the device (also known as the base device
+ * state).
+ */
void onPostureChanged(@DevicePostureInt int posture);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
index a32a5ab..422aa4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
@@ -22,11 +22,14 @@
import androidx.annotation.NonNull;
+import com.android.app.tracing.ListenersTracing;
import com.android.internal.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.Assert;
+import kotlin.Unit;
+
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@@ -36,8 +39,11 @@
/** Implementation of {@link DevicePostureController} using the DeviceStateManager. */
@SysUISingleton
public class DevicePostureControllerImpl implements DevicePostureController {
+ /** From androidx.window.common.COMMON_STATE_USE_BASE_STATE */
+ private static final int COMMON_STATE_USE_BASE_STATE = 1000;
private final List<Callback> mListeners = new ArrayList<>();
private int mCurrentDevicePosture = DEVICE_POSTURE_UNKNOWN;
+ private int mCurrentBasePosture = DEVICE_POSTURE_UNKNOWN;
private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
@@ -70,12 +76,32 @@
mDeviceStateToPostureMap.put(deviceState, posture);
}
- deviceStateManager.registerCallback(executor, state -> {
- Assert.isMainThread();
- mCurrentDevicePosture =
- mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
+ deviceStateManager.registerCallback(executor, new DeviceStateManager.DeviceStateCallback() {
+ @Override
+ public void onStateChanged(int state) {
+ Assert.isMainThread();
+ mCurrentDevicePosture =
+ mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
+ sendUpdatePosture();
+ }
- mListeners.forEach(l -> l.onPostureChanged(mCurrentDevicePosture));
+ @Override
+ public void onBaseStateChanged(int state) {
+ Assert.isMainThread();
+ mCurrentBasePosture = mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
+
+ if (useBaseState()) {
+ sendUpdatePosture();
+ }
+ }
+
+ private void sendUpdatePosture() {
+ ListenersTracing.INSTANCE.forEachTraced(mListeners, "DevicePostureControllerImpl",
+ l -> {
+ l.onPostureChanged(getDevicePosture());
+ return Unit.INSTANCE;
+ });
+ }
});
}
@@ -93,6 +119,14 @@
@Override
public int getDevicePosture() {
- return mCurrentDevicePosture;
+ if (useBaseState()) {
+ return mCurrentBasePosture;
+ } else {
+ return mCurrentDevicePosture;
+ }
+ }
+
+ private boolean useBaseState() {
+ return mCurrentDevicePosture == COMMON_STATE_USE_BASE_STATE;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
index e576f36..279e5ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -16,6 +16,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
+import com.android.systemui.util.annotations.WeaklyReferencedCallback;
public interface FlashlightController extends CallbackController<FlashlightListener>, Dumpable {
@@ -24,6 +25,7 @@
boolean isAvailable();
boolean isEnabled();
+ @WeaklyReferencedCallback
public interface FlashlightListener {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index e4e9554..b598782 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -125,14 +125,19 @@
* is different.
*/
public void refreshStatusBarHeight() {
- int heightFromConfig = SystemBarUtils.getStatusBarHeight(mContext);
+ Trace.beginSection("StatusBarWindowController#refreshStatusBarHeight");
+ try {
+ int heightFromConfig = SystemBarUtils.getStatusBarHeight(mContext);
- if (mBarHeight != heightFromConfig) {
- mBarHeight = heightFromConfig;
- apply(mCurrentState);
+ if (mBarHeight != heightFromConfig) {
+ mBarHeight = heightFromConfig;
+ apply(mCurrentState);
+ }
+
+ if (DEBUG) Log.v(TAG, "defineSlots");
+ } finally {
+ Trace.endSection();
}
-
- if (DEBUG) Log.v(TAG, "defineSlots");
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 5a9f5d5..886fa70 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -19,6 +19,7 @@
import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
+import static com.android.systemui.Flags.themeOverlayControllerWakefulnessDeprecation;
import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_HOME;
import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_LOCK;
import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET;
@@ -71,12 +72,15 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.monet.ColorScheme;
import com.android.systemui.monet.Style;
import com.android.systemui.monet.TonalPalette;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;
import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors;
@@ -127,7 +131,6 @@
private final SecureSettings mSecureSettings;
private final Executor mMainExecutor;
private final Handler mBgHandler;
- private final boolean mIsMonochromaticEnabled;
private final Context mContext;
private final boolean mIsMonetEnabled;
private final boolean mIsFidelityEnabled;
@@ -161,6 +164,8 @@
private final SparseArray<WallpaperColors> mDeferredWallpaperColors = new SparseArray<>();
private final SparseIntArray mDeferredWallpaperColorsFlags = new SparseIntArray();
private final WakefulnessLifecycle mWakefulnessLifecycle;
+ private final JavaAdapter mJavaAdapter;
+ private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final UiModeManager mUiModeManager;
private DynamicScheme mDynamicSchemeDark;
private DynamicScheme mDynamicSchemeLight;
@@ -200,8 +205,12 @@
return;
}
boolean currentUser = userId == mUserTracker.getUserId();
- if (currentUser && !mAcceptColorEvents
- && mWakefulnessLifecycle.getWakefulness() != WAKEFULNESS_ASLEEP) {
+ boolean isAsleep = themeOverlayControllerWakefulnessDeprecation()
+ ? mKeyguardTransitionInteractor.isFinishedInStateWhereValue(
+ state -> KeyguardState.Companion.deviceIsAsleepInState(state))
+ : mWakefulnessLifecycle.getWakefulness() != WAKEFULNESS_ASLEEP;
+
+ if (currentUser && !mAcceptColorEvents && isAsleep) {
mDeferredWallpaperColors.put(userId, wallpaperColors);
mDeferredWallpaperColorsFlags.put(userId, which);
Log.i(TAG, "colors received; processing deferred until screen off: "
@@ -395,9 +404,10 @@
FeatureFlags featureFlags,
@Main Resources resources,
WakefulnessLifecycle wakefulnessLifecycle,
+ JavaAdapter javaAdapter,
+ KeyguardTransitionInteractor keyguardTransitionInteractor,
UiModeManager uiModeManager) {
mContext = context;
- mIsMonochromaticEnabled = featureFlags.isEnabled(Flags.MONOCHROMATIC_THEME);
mIsMonetEnabled = featureFlags.isEnabled(Flags.MONET);
mIsFidelityEnabled = featureFlags.isEnabled(Flags.COLOR_FIDELITY);
mDeviceProvisionedController = deviceProvisionedController;
@@ -412,6 +422,8 @@
mUserTracker = userTracker;
mResources = resources;
mWakefulnessLifecycle = wakefulnessLifecycle;
+ mJavaAdapter = javaAdapter;
+ mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mUiModeManager = uiModeManager;
dumpManager.registerDumpable(TAG, this);
}
@@ -494,21 +506,34 @@
}
mWallpaperManager.addOnColorsChangedListener(mOnColorsChangedListener, null,
UserHandle.USER_ALL);
- mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() {
- @Override
- public void onFinishedGoingToSleep() {
- final int userId = mUserTracker.getUserId();
- final WallpaperColors colors = mDeferredWallpaperColors.get(userId);
- if (colors != null) {
- int flags = mDeferredWallpaperColorsFlags.get(userId);
- mDeferredWallpaperColors.put(userId, null);
- mDeferredWallpaperColorsFlags.put(userId, 0);
+ Runnable whenAsleepHandler = () -> {
+ final int userId = mUserTracker.getUserId();
+ final WallpaperColors colors = mDeferredWallpaperColors.get(userId);
+ if (colors != null) {
+ int flags = mDeferredWallpaperColorsFlags.get(userId);
- handleWallpaperColors(colors, flags, userId);
- }
+ mDeferredWallpaperColors.put(userId, null);
+ mDeferredWallpaperColorsFlags.put(userId, 0);
+
+ handleWallpaperColors(colors, flags, userId);
}
- });
+ };
+
+ if (themeOverlayControllerWakefulnessDeprecation()) {
+ mJavaAdapter.alwaysCollectFlow(
+ mKeyguardTransitionInteractor.isFinishedInState(KeyguardState.DOZING),
+ isFinishedInDozing -> {
+ if (isFinishedInDozing) whenAsleepHandler.run();
+ });
+ } else {
+ mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() {
+ @Override
+ public void onFinishedGoingToSleep() {
+ whenAsleepHandler.run();
+ }
+ });
+ }
}
private void reevaluateSystemTheme(boolean forceReload) {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index 8ae093a..10fc83c 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -20,6 +20,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shade.NotificationPanelUnfoldAnimationController
import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController
+import com.android.systemui.unfold.dagger.UnfoldBg
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityManager
@@ -33,10 +34,7 @@
import javax.inject.Named
import javax.inject.Scope
-@Scope
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class SysUIUnfoldScope
+@Scope @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class SysUIUnfoldScope
/**
* Creates an injectable [SysUIUnfoldComponent] that provides objects that have been scoped with
@@ -55,20 +53,21 @@
@Provides
@SysUISingleton
fun provideSysUIUnfoldComponent(
- provider: Optional<UnfoldTransitionProgressProvider>,
- rotationProvider: Optional<NaturalRotationUnfoldProgressProvider>,
- @Named(UNFOLD_STATUS_BAR) scopedProvider:
- Optional<ScopedUnfoldTransitionProgressProvider>,
- unfoldLatencyTracker: Lazy<UnfoldLatencyTracker>,
- factory: SysUIUnfoldComponent.Factory
+ provider: Optional<UnfoldTransitionProgressProvider>,
+ rotationProvider: Optional<NaturalRotationUnfoldProgressProvider>,
+ @Named(UNFOLD_STATUS_BAR) scopedProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
+ @UnfoldBg bgProvider: Optional<UnfoldTransitionProgressProvider>,
+ unfoldLatencyTracker: Lazy<UnfoldLatencyTracker>,
+ factory: SysUIUnfoldComponent.Factory
): Optional<SysUIUnfoldComponent> {
val p1 = provider.getOrNull()
val p2 = rotationProvider.getOrNull()
val p3 = scopedProvider.getOrNull()
- return if (p1 == null || p2 == null || p3 == null) {
+ val p4 = bgProvider.getOrNull()
+ return if (p1 == null || p2 == null || p3 == null || p4 == null) {
Optional.empty()
} else {
- Optional.of(factory.create(p1, p2, p3, unfoldLatencyTracker.get()))
+ Optional.of(factory.create(p1, p2, p3, p4, unfoldLatencyTracker.get()))
}
}
}
@@ -76,13 +75,15 @@
@SysUIUnfoldScope
@Subcomponent
interface SysUIUnfoldComponent {
+
@Subcomponent.Factory
interface Factory {
fun create(
- @BindsInstance p1: UnfoldTransitionProgressProvider,
- @BindsInstance p2: NaturalRotationUnfoldProgressProvider,
- @BindsInstance p3: ScopedUnfoldTransitionProgressProvider,
- @BindsInstance p4: UnfoldLatencyTracker,
+ @BindsInstance p1: UnfoldTransitionProgressProvider,
+ @BindsInstance p2: NaturalRotationUnfoldProgressProvider,
+ @BindsInstance p3: ScopedUnfoldTransitionProgressProvider,
+ @BindsInstance @UnfoldBg p4: UnfoldTransitionProgressProvider,
+ @BindsInstance p5: UnfoldLatencyTracker,
): SysUIUnfoldComponent
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 36a1e8a..b72c6f1 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -35,6 +35,9 @@
import android.view.SurfaceSession
import android.view.WindowManager
import android.view.WindowlessWindowManager
+import com.android.app.tracing.traceSection
+import com.android.keyguard.logging.ScrimLogger
+import com.android.systemui.Flags.unfoldAnimationBackgroundProgress
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -45,16 +48,16 @@
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation.AddOverlayReason.FOLD
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation.AddOverlayReason.UNFOLD
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.dagger.UnfoldBg
import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled
import com.android.systemui.util.concurrency.ThreadFactory
-import com.android.app.tracing.traceSection
-import com.android.keyguard.logging.ScrimLogger
import com.android.wm.shell.displayareahelper.DisplayAreaHelper
import java.util.Optional
import java.util.concurrent.Executor
import java.util.function.Consumer
import javax.inject.Inject
+import javax.inject.Provider
@SysUIUnfoldScope
class UnfoldLightRevealOverlayAnimation
@@ -65,11 +68,14 @@
private val deviceStateManager: DeviceStateManager,
private val contentResolver: ContentResolver,
private val displayManager: DisplayManager,
- private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
+ @UnfoldBg
+ private val unfoldTransitionBgProgressProvider: Provider<UnfoldTransitionProgressProvider>,
+ private val unfoldTransitionProgressProvider: Provider<UnfoldTransitionProgressProvider>,
private val displayAreaHelper: Optional<DisplayAreaHelper>,
@Main private val executor: Executor,
private val threadFactory: ThreadFactory,
- private val rotationChangeProvider: RotationChangeProvider,
+ @UnfoldBg private val rotationChangeProvider: RotationChangeProvider,
+ @UnfoldBg private val unfoldProgressHandler: Handler,
private val displayTracker: DisplayTracker,
private val scrimLogger: ScrimLogger,
) {
@@ -96,11 +102,15 @@
fun init() {
// This method will be called only on devices where this animation is enabled,
// so normally this thread won't be created
- bgHandler = threadFactory.buildHandlerOnNewThread(TAG)
+ bgHandler = unfoldProgressHandler
bgExecutor = threadFactory.buildDelayableExecutorOnHandler(bgHandler)
deviceStateManager.registerCallback(bgExecutor, FoldListener())
- unfoldTransitionProgressProvider.addCallback(transitionListener)
+ if (unfoldAnimationBackgroundProgress()) {
+ unfoldTransitionBgProgressProvider.get().addCallback(transitionListener)
+ } else {
+ unfoldTransitionProgressProvider.get().addCallback(transitionListener)
+ }
rotationChangeProvider.addCallback(rotationWatcher)
val containerBuilder =
@@ -169,8 +179,13 @@
overlayAddReason = reason
- val newRoot = SurfaceControlViewHost(context, context.display!!, wwm,
- "UnfoldLightRevealOverlayAnimation")
+ val newRoot =
+ SurfaceControlViewHost(
+ context,
+ context.display,
+ wwm,
+ "UnfoldLightRevealOverlayAnimation"
+ )
val params = getLayoutParams()
val newView =
LightRevealScrim(
@@ -353,12 +368,13 @@
}
private fun executeInBackground(f: () -> Unit) {
- check(Looper.myLooper() != bgHandler.looper) {
- "Trying to execute using background handler while already running" +
- " in the background handler"
+ // This is needed to allow progresses to be received both from the main thread (that will
+ // schedule a runnable on the bg thread), and from the bg thread directly (no reposting).
+ if (bgHandler.looper.isCurrentThread) {
+ f()
+ } else {
+ bgHandler.post(f)
}
- // The UiBackground executor is not used as it doesn't have a prepared looper.
- bgHandler.post(f)
}
private fun ensureInBackground() {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
index 12b8845..94912bf8 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
@@ -21,11 +21,14 @@
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.unfold.system.DeviceStateRepository
import com.android.systemui.unfold.updates.FoldStateRepository
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
+import kotlinx.coroutines.plus
/**
* Logs several unfold related details in a trace. Mainly used for debugging and investigate
@@ -37,7 +40,8 @@
constructor(
private val context: Context,
private val foldStateRepository: FoldStateRepository,
- @Application private val applicationScope: CoroutineScope,
+ @Application applicationScope: CoroutineScope,
+ @Background private val coroutineContext: CoroutineContext,
private val deviceStateRepository: DeviceStateRepository
) : CoreStartable {
private val isFoldable: Boolean
@@ -46,20 +50,22 @@
.getIntArray(com.android.internal.R.array.config_foldedDeviceStates)
.isNotEmpty()
+ private val bgScope = applicationScope.plus(coroutineContext)
+
override fun start() {
if (!isFoldable) return
- applicationScope.launch {
+ bgScope.launch {
val foldUpdateLogger = TraceStateLogger("FoldUpdate")
foldStateRepository.foldUpdate.collect { foldUpdateLogger.log(it.name) }
}
- applicationScope.launch {
+ bgScope.launch {
foldStateRepository.hingeAngle.collect {
Trace.traceCounter(Trace.TRACE_TAG_APP, "hingeAngle", it.toInt())
}
}
- applicationScope.launch {
+ bgScope.launch {
val foldedStateLogger = TraceStateLogger("FoldedState")
deviceStateRepository.isFolded.collect { isFolded ->
foldedStateLogger.log(
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 7b628f8..0531487 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -24,6 +24,7 @@
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.UnfoldMain
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
@@ -102,7 +103,7 @@
@Singleton
fun provideNaturalRotationProgressProvider(
context: Context,
- rotationChangeProvider: RotationChangeProvider,
+ @UnfoldMain rotationChangeProvider: RotationChangeProvider,
unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider>
): Optional<NaturalRotationUnfoldProgressProvider> =
unfoldTransitionProgressProvider.map { provider ->
@@ -153,7 +154,8 @@
return resultingProvider?.get()?.orElse(null)?.let { unfoldProgressProvider ->
UnfoldProgressProvider(unfoldProgressProvider, foldProvider)
- } ?: ShellUnfoldProgressProvider.NO_PROVIDER
+ }
+ ?: ShellUnfoldProgressProvider.NO_PROVIDER
}
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapper.kt
index a2a44e4..b2297d0 100644
--- a/packages/SystemUI/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapper.kt
@@ -30,14 +30,16 @@
private val loopedCallback = LoopedCallback()
+ private var isLoopedCallbackRegistered: Boolean = false
+
override fun start() {
animatable2.start()
- animatable2.registerAnimationCallback(loopedCallback)
+ setLoopingRegistered(true)
}
override fun stop() {
// stop looping if someone stops the animation
- animatable2.unregisterAnimationCallback(loopedCallback)
+ setLoopingRegistered(false)
animatable2.stop()
}
@@ -49,7 +51,25 @@
override fun unregisterAnimationCallback(callback: Animatable2.AnimationCallback): Boolean =
animatable2.unregisterAnimationCallback(callback)
- override fun clearAnimationCallbacks() = animatable2.clearAnimationCallbacks()
+ override fun clearAnimationCallbacks() {
+ animatable2.clearAnimationCallbacks()
+ // re-register looped callback to maintain looped behaviour. LoopedCallback is a static
+ // class and it has no extra references, so it doesn't provoke a memory leak.
+ isLoopedCallbackRegistered = false
+ setLoopingRegistered(true)
+ }
+
+ private fun setLoopingRegistered(isLooping: Boolean) {
+ if (isLooping == isLoopedCallbackRegistered) {
+ return
+ }
+ isLoopedCallbackRegistered = isLooping
+ if (isLooping) {
+ animatable2.registerAnimationCallback(loopedCallback)
+ } else {
+ animatable2.unregisterAnimationCallback(loopedCallback)
+ }
+ }
override fun getConstantState(): ConstantState? =
drawable!!.constantState?.let(LoopedAnimatable2DrawableWrapper::LoopedDrawableState)
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
index cc9335e..472f0ae 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
@@ -14,6 +14,7 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.plus
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
@@ -29,6 +30,14 @@
@Provides
@SysUISingleton
+ @Background
+ fun bgApplicationScope(
+ @Application applicationScope: CoroutineScope,
+ @Background coroutineContext: CoroutineContext,
+ ): CoroutineScope = applicationScope.plus(coroutineContext)
+
+ @Provides
+ @SysUISingleton
@Main
@Deprecated(
"Use @Main CoroutineContext instead",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index d8799e1..4395282 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -133,8 +133,8 @@
}
@Test
- public void setScale() throws RemoteException {
- mIWindowMagnificationConnection.setScale(TEST_DISPLAY, 3.0f);
+ public void setScaleForWindowMagnification() throws RemoteException {
+ mIWindowMagnificationConnection.setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
waitForIdleSync();
verify(mWindowMagnificationController).setScale(3.0f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index f4122d5..ea20d29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -338,6 +338,13 @@
waitForIdleSync()
assertThat(container.hasCredentialView()).isTrue()
+ assertThat(container.hasBiometricPrompt()).isFalse()
+
+ // Check credential view persists after new attachment
+ container.onAttachedToWindow()
+
+ assertThat(container.hasCredentialView()).isTrue()
+ assertThat(container.hasBiometricPrompt()).isFalse()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
index c825d2e..834179bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
@@ -38,6 +38,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -97,7 +98,8 @@
deviceStateManager,
displayManager,
handler,
- fakeExecutor
+ fakeExecutor,
+ UnconfinedTestDispatcher(),
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 1e80732..83fb17f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -319,10 +319,10 @@
@Test
fun imeHiddenEvent_isTriggered() =
testScope.runTest {
- val imeHiddenEvent by collectLastValue(underTest.onImeHidden)
+ val imeHiddenEvent by collectLastValue(underTest.onImeHiddenByUser)
runCurrent()
- underTest.onImeHidden()
+ underTest.onImeHiddenByUser()
runCurrent()
assertThat(imeHiddenEvent).isNotNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index 63c992b..45c186d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -23,8 +23,6 @@
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
@@ -76,18 +74,4 @@
underTest.onAuthenticateButtonClicked()
assertThat(animateFailure).isFalse()
}
-
- @Test
- fun onImeVisibilityChanged() =
- testScope.runTest {
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "")
- val onImeHidden by collectLastValue(bouncerInteractor.onImeHidden)
-
- underTest.onImeVisibilityChanged(true)
- assertThat(onImeHidden).isNull()
-
- underTest.onImeVisibilityChanged(false)
- assertThat(onImeHidden).isNotNull()
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 9b1e958..937c703 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -20,7 +20,9 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
@@ -43,7 +45,11 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val authenticationInteractor = utils.authenticationInteractor()
+ private val authenticationRepository = utils.authenticationRepository
+ private val authenticationInteractor =
+ utils.authenticationInteractor(
+ repository = authenticationRepository,
+ )
private val sceneInteractor = utils.sceneInteractor()
private val bouncerInteractor =
utils.bouncerInteractor(
@@ -207,6 +213,101 @@
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
+ @Test
+ fun onImeVisibilityChanged_false_doesNothing() =
+ testScope.runTest {
+ val events by collectValues(bouncerInteractor.onImeHiddenByUser)
+ assertThat(events).isEmpty()
+
+ underTest.onImeVisibilityChanged(isVisible = false)
+ assertThat(events).isEmpty()
+ }
+
+ @Test
+ fun onImeVisibilityChanged_falseAfterTrue_emitsOnImeHiddenByUserEvent() =
+ testScope.runTest {
+ val events by collectValues(bouncerInteractor.onImeHiddenByUser)
+ assertThat(events).isEmpty()
+
+ underTest.onImeVisibilityChanged(isVisible = true)
+ assertThat(events).isEmpty()
+
+ underTest.onImeVisibilityChanged(isVisible = false)
+ assertThat(events).hasSize(1)
+
+ underTest.onImeVisibilityChanged(isVisible = true)
+ assertThat(events).hasSize(1)
+
+ underTest.onImeVisibilityChanged(isVisible = false)
+ assertThat(events).hasSize(2)
+ }
+
+ @Test
+ fun onImeVisibilityChanged_falseAfterTrue_whileThrottling_doesNothing() =
+ testScope.runTest {
+ val events by collectValues(bouncerInteractor.onImeHiddenByUser)
+ assertThat(events).isEmpty()
+ underTest.onImeVisibilityChanged(isVisible = true)
+ setThrottling(true)
+
+ underTest.onImeVisibilityChanged(isVisible = false)
+
+ assertThat(events).isEmpty()
+ }
+
+ @Test
+ fun isTextFieldFocusRequested_initiallyTrue() =
+ testScope.runTest {
+ val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+ assertThat(isTextFieldFocusRequested).isTrue()
+ }
+
+ @Test
+ fun isTextFieldFocusRequested_focusGained_becomesFalse() =
+ testScope.runTest {
+ val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+
+ underTest.onTextFieldFocusChanged(isFocused = true)
+
+ assertThat(isTextFieldFocusRequested).isFalse()
+ }
+
+ @Test
+ fun isTextFieldFocusRequested_focusLost_becomesTrue() =
+ testScope.runTest {
+ val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+ underTest.onTextFieldFocusChanged(isFocused = true)
+
+ underTest.onTextFieldFocusChanged(isFocused = false)
+
+ assertThat(isTextFieldFocusRequested).isTrue()
+ }
+
+ @Test
+ fun isTextFieldFocusRequested_focusLostWhileThrottling_staysFalse() =
+ testScope.runTest {
+ val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+ underTest.onTextFieldFocusChanged(isFocused = true)
+ setThrottling(true)
+
+ underTest.onTextFieldFocusChanged(isFocused = false)
+
+ assertThat(isTextFieldFocusRequested).isFalse()
+ }
+
+ @Test
+ fun isTextFieldFocusRequested_throttlingCountdownEnds_becomesTrue() =
+ testScope.runTest {
+ val isTextFieldFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+ underTest.onTextFieldFocusChanged(isFocused = true)
+ setThrottling(true)
+ underTest.onTextFieldFocusChanged(isFocused = false)
+
+ setThrottling(false)
+
+ assertThat(isTextFieldFocusRequested).isTrue()
+ }
+
private fun TestScope.switchToScene(toScene: SceneKey) {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val bouncerShown = currentScene?.key != SceneKey.Bouncer && toScene == SceneKey.Bouncer
@@ -226,6 +327,35 @@
switchToScene(SceneKey.Bouncer)
}
+ private suspend fun TestScope.setThrottling(
+ isThrottling: Boolean,
+ failedAttemptCount: Int = 5,
+ ) {
+ if (isThrottling) {
+ repeat(failedAttemptCount) {
+ authenticationRepository.reportAuthenticationAttempt(false)
+ }
+ val remainingTimeMs = 30_000
+ authenticationRepository.setThrottleDuration(remainingTimeMs)
+ authenticationRepository.setThrottling(
+ AuthenticationThrottlingModel(
+ failedAttemptCount = failedAttemptCount,
+ remainingMs = remainingTimeMs,
+ )
+ )
+ } else {
+ authenticationRepository.reportAuthenticationAttempt(true)
+ authenticationRepository.setThrottling(
+ AuthenticationThrottlingModel(
+ failedAttemptCount = failedAttemptCount,
+ remainingMs = 0,
+ )
+ )
+ }
+
+ runCurrent()
+ }
+
companion object {
private const val ENTER_YOUR_PASSWORD = "Enter your password"
private const val WRONG_PASSWORD = "Wrong password"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt
index 034b802..112cec2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt
@@ -30,6 +30,8 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -44,102 +46,112 @@
private val configurationController: ConfigurationController = mock()
private val layoutInflater = TestLayoutInflater()
+ private val backgroundDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(backgroundDispatcher)
val underTest = ConfigurationState(configurationController, context, layoutInflater)
@Test
- fun reinflateAndBindLatest_inflatesWithoutEmission() = runTest {
- var callbackCount = 0
- backgroundScope.launch {
- underTest.reinflateAndBindLatest<View>(
- resource = 0,
- root = null,
- attachToRoot = false,
- ) {
- callbackCount++
- null
+ fun reinflateAndBindLatest_inflatesWithoutEmission() =
+ testScope.runTest {
+ var callbackCount = 0
+ backgroundScope.launch {
+ underTest.reinflateAndBindLatest<View>(
+ resource = 0,
+ root = null,
+ attachToRoot = false,
+ backgroundDispatcher,
+ ) {
+ callbackCount++
+ null
+ }
}
- }
- // Inflates without an emission
- runCurrent()
- assertThat(layoutInflater.inflationCount).isEqualTo(1)
- assertThat(callbackCount).isEqualTo(1)
- }
-
- @Test
- fun reinflateAndBindLatest_reinflatesOnThemeChanged() = runTest {
- var callbackCount = 0
- backgroundScope.launch {
- underTest.reinflateAndBindLatest<View>(
- resource = 0,
- root = null,
- attachToRoot = false,
- ) {
- callbackCount++
- null
- }
- }
- runCurrent()
-
- val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany {
- verify(configurationController, atLeastOnce()).addCallback(capture())
- }
-
- listOf(1, 2, 3).forEach { count ->
- assertThat(layoutInflater.inflationCount).isEqualTo(count)
- assertThat(callbackCount).isEqualTo(count)
- configListeners.forEach { it.onThemeChanged() }
+ // Inflates without an emission
runCurrent()
+ assertThat(layoutInflater.inflationCount).isEqualTo(1)
+ assertThat(callbackCount).isEqualTo(1)
}
- }
@Test
- fun reinflateAndBindLatest_reinflatesOnDensityOrFontScaleChanged() = runTest {
- var callbackCount = 0
- backgroundScope.launch {
- underTest.reinflateAndBindLatest<View>(
- resource = 0,
- root = null,
- attachToRoot = false,
- ) {
- callbackCount++
- null
+ fun reinflateAndBindLatest_reinflatesOnThemeChanged() =
+ testScope.runTest {
+ var callbackCount = 0
+ backgroundScope.launch {
+ underTest.reinflateAndBindLatest<View>(
+ resource = 0,
+ root = null,
+ attachToRoot = false,
+ backgroundDispatcher,
+ ) {
+ callbackCount++
+ null
+ }
}
- }
- runCurrent()
-
- val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany {
- verify(configurationController, atLeastOnce()).addCallback(capture())
- }
-
- listOf(1, 2, 3).forEach { count ->
- assertThat(layoutInflater.inflationCount).isEqualTo(count)
- assertThat(callbackCount).isEqualTo(count)
- configListeners.forEach { it.onDensityOrFontScaleChanged() }
runCurrent()
- }
- }
- @Test
- fun testReinflateAndBindLatest_disposesOnCancel() = runTest {
- var callbackCount = 0
- var disposed = false
- val job = launch {
- underTest.reinflateAndBindLatest<View>(
- resource = 0,
- root = null,
- attachToRoot = false,
- ) {
- callbackCount++
- DisposableHandle { disposed = true }
+ val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany {
+ verify(configurationController, atLeastOnce()).addCallback(capture())
+ }
+
+ listOf(1, 2, 3).forEach { count ->
+ assertThat(layoutInflater.inflationCount).isEqualTo(count)
+ assertThat(callbackCount).isEqualTo(count)
+ configListeners.forEach { it.onThemeChanged() }
+ runCurrent()
}
}
- runCurrent()
- job.cancelAndJoin()
- assertThat(disposed).isTrue()
- }
+ @Test
+ fun reinflateAndBindLatest_reinflatesOnDensityOrFontScaleChanged() =
+ testScope.runTest {
+ var callbackCount = 0
+ backgroundScope.launch {
+ underTest.reinflateAndBindLatest<View>(
+ resource = 0,
+ root = null,
+ attachToRoot = false,
+ backgroundDispatcher,
+ ) {
+ callbackCount++
+ null
+ }
+ }
+ runCurrent()
+
+ val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany {
+ verify(configurationController, atLeastOnce()).addCallback(capture())
+ }
+
+ listOf(1, 2, 3).forEach { count ->
+ assertThat(layoutInflater.inflationCount).isEqualTo(count)
+ assertThat(callbackCount).isEqualTo(count)
+ configListeners.forEach { it.onDensityOrFontScaleChanged() }
+ runCurrent()
+ }
+ }
+
+ @Test
+ fun testReinflateAndBindLatest_disposesOnCancel() =
+ testScope.runTest {
+ var callbackCount = 0
+ var disposed = false
+ val job = launch {
+ underTest.reinflateAndBindLatest<View>(
+ resource = 0,
+ root = null,
+ attachToRoot = false,
+ backgroundDispatcher,
+ ) {
+ callbackCount++
+ DisposableHandle { disposed = true }
+ }
+ }
+
+ runCurrent()
+ job.cancelAndJoin()
+ assertThat(disposed).isTrue()
+ }
inner class TestLayoutInflater : LayoutInflater(context) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
index 0004f52..910097e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
@@ -21,6 +21,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
@@ -56,6 +57,13 @@
)
@Test
+ fun canSwipeToEnter_startsNull() =
+ testScope.runTest {
+ val values by collectValues(underTest.canSwipeToEnter)
+ assertThat(values[0]).isNull()
+ }
+
+ @Test
fun isUnlocked_whenAuthMethodIsNoneAndLockscreenDisabled_isTrue() =
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index a58bc52..2b7221e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -34,6 +34,7 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -69,6 +70,7 @@
authController,
keyguardUpdateMonitor,
testScope.backgroundScope,
+ UnconfinedTestDispatcher(),
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
index 9be5558..ae6c5b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
@@ -26,6 +26,7 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -49,7 +50,11 @@
fun setup() {
MockitoAnnotations.initMocks(this)
testScope = TestScope()
- underTest = DevicePostureRepositoryImpl(postureController = devicePostureController)
+ underTest =
+ DevicePostureRepositoryImpl(
+ postureController = devicePostureController,
+ UnconfinedTestDispatcher()
+ )
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 9dfb5a5..e082ca8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -47,13 +47,13 @@
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -305,7 +305,11 @@
MediaOutputBaseDialogImpl(Context context, BroadcastSender broadcastSender,
MediaOutputController mediaOutputController) {
- super(context, broadcastSender, mediaOutputController);
+ super(
+ context,
+ broadcastSender,
+ mediaOutputController, /* includePlaybackAndAppMetadata */
+ true);
mAdapter = mMediaOutputBaseAdapter;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 379136b..d5dc502 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -49,13 +49,13 @@
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -394,8 +394,14 @@
@NonNull
private MediaOutputDialog makeTestDialog(MediaOutputController controller) {
- return new MediaOutputDialog(mContext, false, mBroadcastSender,
- controller, mDialogLaunchAnimator, mUiEventLogger);
+ return new MediaOutputDialog(
+ mContext,
+ false,
+ mBroadcastSender,
+ controller,
+ mDialogLaunchAnimator,
+ mUiEventLogger,
+ true);
}
private void withTestDialog(MediaOutputController controller, Consumer<MediaOutputDialog> c) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index cf43b2e..b7618d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -45,7 +45,6 @@
import androidx.test.ext.truth.content.IntentSubject.assertThat
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.notetask.NoteTaskController.Companion.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE
import com.android.systemui.notetask.NoteTaskController.Companion.SHORTCUT_ID
@@ -56,6 +55,7 @@
import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
+import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -162,6 +162,7 @@
noteTaskBubblesController =
FakeNoteTaskBubbleController(context, testDispatcher, Optional.ofNullable(bubbles)),
applicationScope = testScope,
+ bgCoroutineContext = testScope.backgroundScope.coroutineContext
)
// region onBubbleExpandChanged
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
index a9f8ea0..81d02b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
@@ -85,7 +85,7 @@
`when`(sharedPreferences.edit()).thenReturn(editor)
tile = Tile()
- customTileStatePersister = CustomTileStatePersister(mockContext)
+ customTileStatePersister = CustomTileStatePersisterImpl(mockContext)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
index 3808c7e..313ccb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
@@ -228,16 +228,16 @@
showPairNewDevice = true
)
- val seeAllLayout = bluetoothTileDialog.requireViewById<View>(R.id.see_all_layout_group)
- val pairNewLayout =
- bluetoothTileDialog.requireViewById<View>(R.id.pair_new_device_layout_group)
+ val seeAllButton = bluetoothTileDialog.requireViewById<View>(R.id.see_all_button)
+ val pairNewButton =
+ bluetoothTileDialog.requireViewById<View>(R.id.pair_new_device_button)
val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list)
val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter
- assertThat(seeAllLayout).isNotNull()
- assertThat(seeAllLayout.visibility).isEqualTo(GONE)
- assertThat(pairNewLayout).isNotNull()
- assertThat(pairNewLayout.visibility).isEqualTo(VISIBLE)
+ assertThat(seeAllButton).isNotNull()
+ assertThat(seeAllButton.visibility).isEqualTo(GONE)
+ assertThat(pairNewButton).isNotNull()
+ assertThat(pairNewButton.visibility).isEqualTo(VISIBLE)
assertThat(adapter.itemCount).isEqualTo(1)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
index fb5dd21..99993f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
@@ -30,7 +30,6 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -113,9 +112,7 @@
testScope.runTest {
bluetoothTileDialogViewModel.showDialog(context, null)
- assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
verify(dialogLaunchAnimator, never()).showFromView(any(), any(), any(), any())
- assertThat(bluetoothTileDialogViewModel.dialog?.isShowing).isTrue()
verify(uiEventLogger).log(BluetoothTileDialogUiEvent.BLUETOOTH_TILE_DIALOG_SHOWN)
}
}
@@ -125,7 +122,6 @@
testScope.runTest {
bluetoothTileDialogViewModel.showDialog(mContext, LinearLayout(mContext))
- assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
verify(dialogLaunchAnimator).showFromView(any(), any(), nullable(), anyBoolean())
}
}
@@ -136,7 +132,6 @@
backgroundExecutor.execute {
bluetoothTileDialogViewModel.showDialog(mContext, LinearLayout(mContext))
- assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
verify(dialogLaunchAnimator).showFromView(any(), any(), nullable(), anyBoolean())
}
}
@@ -147,7 +142,6 @@
testScope.runTest {
bluetoothTileDialogViewModel.showDialog(context, null)
- assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
verify(deviceItemInteractor).deviceItemUpdate
}
}
@@ -157,7 +151,6 @@
testScope.runTest {
bluetoothTileDialogViewModel.showDialog(context, null)
- assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
verify(bluetoothStateInteractor).bluetoothStateUpdate
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
index 4c173cc..e236f4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
@@ -220,45 +220,57 @@
@Test
fun testUpdateDeviceItemOnClick_connectedMedia_setActive() {
- `when`(deviceItem1.type).thenReturn(DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
+ testScope.runTest {
+ `when`(deviceItem1.type).thenReturn(DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
- interactor.updateDeviceItemOnClick(deviceItem1)
+ interactor.updateDeviceItemOnClick(deviceItem1)
- verify(cachedDevice1).setActive()
- verify(logger)
- .logDeviceClick(cachedDevice1.address, DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
+ verify(cachedDevice1).setActive()
+ verify(logger)
+ .logDeviceClick(
+ cachedDevice1.address,
+ DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE
+ )
+ }
}
@Test
fun testUpdateDeviceItemOnClick_activeMedia_disconnect() {
- `when`(deviceItem1.type).thenReturn(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
+ testScope.runTest {
+ `when`(deviceItem1.type).thenReturn(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
- interactor.updateDeviceItemOnClick(deviceItem1)
+ interactor.updateDeviceItemOnClick(deviceItem1)
- verify(cachedDevice1).disconnect()
- verify(logger)
- .logDeviceClick(cachedDevice1.address, DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
+ verify(cachedDevice1).disconnect()
+ verify(logger)
+ .logDeviceClick(cachedDevice1.address, DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
+ }
}
@Test
fun testUpdateDeviceItemOnClick_connectedOtherDevice_disconnect() {
- `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+ testScope.runTest {
+ `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
- interactor.updateDeviceItemOnClick(deviceItem1)
+ interactor.updateDeviceItemOnClick(deviceItem1)
- verify(cachedDevice1).disconnect()
- verify(logger)
- .logDeviceClick(cachedDevice1.address, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+ verify(cachedDevice1).disconnect()
+ verify(logger)
+ .logDeviceClick(cachedDevice1.address, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+ }
}
@Test
fun testUpdateDeviceItemOnClick_saved_connect() {
- `when`(deviceItem1.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
+ testScope.runTest {
+ `when`(deviceItem1.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
- interactor.updateDeviceItemOnClick(deviceItem1)
+ interactor.updateDeviceItemOnClick(deviceItem1)
- verify(cachedDevice1).connect()
- verify(logger).logDeviceClick(cachedDevice1.address, DeviceItemType.SAVED_BLUETOOTH_DEVICE)
+ verify(cachedDevice1).connect()
+ verify(logger)
+ .logDeviceClick(cachedDevice1.address, DeviceItemType.SAVED_BLUETOOTH_DEVICE)
+ }
}
private fun createFactory(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
new file mode 100644
index 0000000..cf076c5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.data.repository
+
+import android.content.ComponentName
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.qs.external.FakeCustomTileStatePersister
+import com.android.systemui.qs.external.TileServiceKey
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.impl.custom.commons.copy
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class CustomTileRepositoryTest : SysuiTestCase() {
+
+ private val testScope = TestScope()
+
+ private val persister = FakeCustomTileStatePersister()
+
+ private val underTest: CustomTileRepository =
+ CustomTileRepositoryImpl(
+ TileSpec.create(TEST_COMPONENT),
+ persister,
+ testScope.testScheduler,
+ )
+
+ @Test
+ fun persistableTileIsRestoredForUser() =
+ testScope.runTest {
+ persister.persistState(TEST_TILE_KEY_1, TEST_TILE_1)
+ persister.persistState(TEST_TILE_KEY_2, TEST_TILE_2)
+
+ underTest.restoreForTheUserIfNeeded(TEST_USER_1, true)
+ runCurrent()
+
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
+ }
+
+ @Test
+ fun notPersistableTileIsNotRestored() =
+ testScope.runTest {
+ persister.persistState(TEST_TILE_KEY_1, TEST_TILE_1)
+ val tiles = collectValues(underTest.getTiles(TEST_USER_1))
+
+ underTest.restoreForTheUserIfNeeded(TEST_USER_1, false)
+ runCurrent()
+
+ assertThat(tiles()).isEmpty()
+ }
+
+ @Test
+ fun emptyPersistedStateIsHandled() =
+ testScope.runTest {
+ val tiles = collectValues(underTest.getTiles(TEST_USER_1))
+
+ underTest.restoreForTheUserIfNeeded(TEST_USER_1, true)
+ runCurrent()
+
+ assertThat(tiles()).isEmpty()
+ }
+
+ @Test
+ fun updatingWithPersistableTilePersists() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ assertThat(persister.readState(TEST_TILE_KEY_1)).isEqualTo(TEST_TILE_1)
+ }
+
+ @Test
+ fun updatingWithNotPersistableTileDoesntPersist() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, false)
+ runCurrent()
+
+ assertThat(persister.readState(TEST_TILE_KEY_1)).isNull()
+ }
+
+ @Test
+ fun updateWithTileEmits() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
+ }
+
+ @Test
+ fun updatingPeristableWithDefaultsPersists() =
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
+
+ assertThat(persister.readState(TEST_TILE_KEY_1)).isEqualTo(TEST_TILE_1)
+ }
+
+ @Test
+ fun updatingNotPersistableWithDefaultsDoesntPersist() =
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, false)
+ runCurrent()
+
+ assertThat(persister.readState(TEST_TILE_KEY_1)).isNull()
+ }
+
+ @Test
+ fun updatingPeristableWithErrorDefaultsDoesntPersist() =
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, CustomTileDefaults.Error, true)
+ runCurrent()
+
+ assertThat(persister.readState(TEST_TILE_KEY_1)).isNull()
+ }
+
+ @Test
+ fun updateWithDefaultsEmits() =
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
+
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
+ }
+
+ @Test
+ fun getTileForAnotherUserReturnsNull() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ assertThat(underTest.getTile(TEST_USER_2)).isNull()
+ }
+
+ @Test
+ fun getTilesForAnotherUserEmpty() =
+ testScope.runTest {
+ val tiles = collectValues(underTest.getTiles(TEST_USER_2))
+
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ assertThat(tiles()).isEmpty()
+ }
+
+ @Test
+ fun updatingWithTileForTheSameUserAddsData() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ underTest.updateWithTile(TEST_USER_1, Tile().apply { subtitle = "test_subtitle" }, true)
+ runCurrent()
+
+ val expectedTile = TEST_TILE_1.copy().apply { subtitle = "test_subtitle" }
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(expectedTile)
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(expectedTile)
+ }
+
+ @Test
+ fun updatingWithTileForAnotherUserOverridesTile() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, TEST_TILE_1, true)
+ runCurrent()
+
+ val tiles = collectValues(underTest.getTiles(TEST_USER_2))
+ underTest.updateWithTile(TEST_USER_2, TEST_TILE_2, true)
+ runCurrent()
+
+ assertThat(underTest.getTile(TEST_USER_2)).isEqualTo(TEST_TILE_2)
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE_2)
+ }
+
+ @Test
+ fun updatingWithDefaultsForTheSameUserAddsData() =
+ testScope.runTest {
+ underTest.updateWithTile(TEST_USER_1, Tile().apply { subtitle = "test_subtitle" }, true)
+ runCurrent()
+
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
+
+ val expectedTile = TEST_TILE_1.copy().apply { subtitle = "test_subtitle" }
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(expectedTile)
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(expectedTile)
+ }
+
+ @Test
+ fun updatingWithDefaultsForAnotherUserOverridesTile() =
+ testScope.runTest {
+ underTest.updateWithDefaults(TEST_USER_1, TEST_DEFAULTS_1, true)
+ runCurrent()
+
+ val tiles = collectValues(underTest.getTiles(TEST_USER_2))
+ underTest.updateWithDefaults(TEST_USER_2, TEST_DEFAULTS_2, true)
+ runCurrent()
+
+ assertThat(underTest.getTile(TEST_USER_2)).isEqualTo(TEST_TILE_2)
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE_2)
+ }
+
+ private companion object {
+
+ val TEST_COMPONENT = ComponentName("test.pkg", "test.cls")
+
+ val TEST_USER_1 = UserHandle.of(1)!!
+ val TEST_TILE_1 =
+ Tile().apply {
+ label = "test_tile_1"
+ icon = Icon.createWithContentUri("file://test_1")
+ }
+ val TEST_TILE_KEY_1 = TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier)
+ val TEST_DEFAULTS_1 = CustomTileDefaults.Result(TEST_TILE_1.icon, TEST_TILE_1.label)
+
+ val TEST_USER_2 = UserHandle.of(2)!!
+ val TEST_TILE_2 =
+ Tile().apply {
+ label = "test_tile_2"
+ icon = Icon.createWithContentUri("file://test_2")
+ }
+ val TEST_TILE_KEY_2 = TileServiceKey(TEST_COMPONENT, TEST_USER_2.identifier)
+ val TEST_DEFAULTS_2 = CustomTileDefaults.Result(TEST_TILE_2.icon, TEST_TILE_2.label)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
new file mode 100644
index 0000000..eebb145
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.domain.interactor
+
+import android.content.ComponentName
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import android.text.format.DateUtils
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.qs.external.FakeCustomTileStatePersister
+import com.android.systemui.qs.external.TileServiceKey
+import com.android.systemui.qs.external.TileServiceManager
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileDefaultsRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileRepository
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class CustomTileInteractorTest : SysuiTestCase() {
+
+ @Mock private lateinit var tileServiceManager: TileServiceManager
+
+ private val testScope = TestScope()
+
+ private val defaultsRepository = FakeCustomTileDefaultsRepository()
+ private val customTileStatePersister = FakeCustomTileStatePersister()
+ private val customTileRepository =
+ FakeCustomTileRepository(
+ TEST_TILE_SPEC,
+ customTileStatePersister,
+ testScope.testScheduler,
+ )
+
+ private lateinit var underTest: CustomTileInteractor
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest =
+ CustomTileInteractor(
+ TEST_USER,
+ defaultsRepository,
+ customTileRepository,
+ tileServiceManager,
+ testScope.backgroundScope,
+ testScope.testScheduler,
+ )
+ }
+
+ @Test
+ fun activeTileIsAvailableAfterRestored() =
+ testScope.runTest {
+ whenever(tileServiceManager.isActiveTile).thenReturn(true)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+
+ underTest.init()
+
+ assertThat(underTest.tile).isEqualTo(TEST_TILE)
+ assertThat(underTest.tiles.first()).isEqualTo(TEST_TILE)
+ }
+
+ @Test
+ fun notActiveTileIsAvailableAfterUpdated() =
+ testScope.runTest {
+ whenever(tileServiceManager.isActiveTile).thenReturn(false)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+ val tiles = collectValues(underTest.tiles)
+ val initJob = launch { underTest.init() }
+
+ underTest.updateTile(TEST_TILE)
+ runCurrent()
+ initJob.join()
+
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE)
+ }
+
+ @Test
+ fun notActiveTileIsAvailableAfterDefaultsUpdated() =
+ testScope.runTest {
+ whenever(tileServiceManager.isActiveTile).thenReturn(false)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+ val tiles = collectValues(underTest.tiles)
+ val initJob = launch { underTest.init() }
+
+ defaultsRepository.putDefaults(TEST_USER, TEST_COMPONENT, TEST_DEFAULTS)
+ defaultsRepository.requestNewDefaults(TEST_USER, TEST_COMPONENT)
+ runCurrent()
+ initJob.join()
+
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun getTileBeforeInitThrows() = testScope.runTest { underTest.tile }
+
+ @Test
+ fun initSuspendsForActiveTileNotRestoredAndNotUpdated() =
+ testScope.runTest {
+ whenever(tileServiceManager.isActiveTile).thenReturn(true)
+ val tiles = collectValues(underTest.tiles)
+
+ val initJob = backgroundScope.launch { underTest.init() }
+ advanceTimeBy(1 * DateUtils.DAY_IN_MILLIS)
+
+ // Is still suspended
+ assertThat(initJob.isActive).isTrue()
+ assertThat(tiles()).isEmpty()
+ }
+
+ @Test
+ fun initSuspendedForNotActiveTileWithoutUpdates() =
+ testScope.runTest {
+ whenever(tileServiceManager.isActiveTile).thenReturn(false)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
+ TEST_TILE,
+ )
+ val tiles = collectValues(underTest.tiles)
+
+ val initJob = backgroundScope.launch { underTest.init() }
+ advanceTimeBy(1 * DateUtils.DAY_IN_MILLIS)
+
+ // Is still suspended
+ assertThat(initJob.isActive).isTrue()
+ assertThat(tiles()).isEmpty()
+ }
+
+ private companion object {
+
+ val TEST_COMPONENT = ComponentName("test.pkg", "test.cls")
+ val TEST_TILE_SPEC = TileSpec.create(TEST_COMPONENT)
+ val TEST_USER = UserHandle.of(1)!!
+ val TEST_TILE =
+ Tile().apply {
+ label = "test_tile_1"
+ icon = Icon.createWithContentUri("file://test_1")
+ }
+ val TEST_DEFAULTS = CustomTileDefaults.Result(TEST_TILE.icon, TEST_TILE.label)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index c3294ff..18b7168 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
@@ -61,7 +62,6 @@
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
@@ -273,6 +273,9 @@
}
@Test
+ fun startsInLockscreenScene() = testScope.runTest { assertCurrentScene(SceneKey.Lockscreen) }
+
+ @Test
fun clickLockButtonAndEnterCorrectPin_unlocksDevice() =
testScope.runTest {
emulateUserDrivenTransition(SceneKey.Bouncer)
@@ -336,7 +339,7 @@
testScope.runTest {
val upDestinationSceneKey by collectLastValue(shadeSceneViewModel.upDestinationSceneKey)
setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
- assertTrue(deviceEntryInteractor.canSwipeToEnter.value)
+ assertThat(deviceEntryInteractor.canSwipeToEnter.value).isTrue()
assertCurrentScene(SceneKey.Lockscreen)
// Emulate a user swipe to dismiss the lockscreen.
@@ -775,11 +778,11 @@
private suspend fun TestScope.dismissIme(
showImeBeforeDismissing: Boolean = true,
) {
- bouncerViewModel.authMethodViewModel.value?.apply {
+ (bouncerViewModel.authMethodViewModel.value as? PasswordBouncerViewModel)?.let {
if (showImeBeforeDismissing) {
- onImeVisibilityChanged(true)
+ it.onImeVisibilityChanged(true)
}
- onImeVisibilityChanged(false)
+ it.onImeVisibilityChanged(false)
runCurrent()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index c4ec56c..3cb97e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -145,6 +145,18 @@
}
@Test
+ fun startsInLockscreenScene() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
+ prepareState()
+
+ underTest.start()
+ runCurrent()
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ }
+
+ @Test
fun switchToLockscreenWhenDeviceLocks() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
@@ -467,7 +479,7 @@
underTest.start()
runCurrent()
- bouncerInteractor.onImeHidden()
+ bouncerInteractor.onImeHiddenByUser()
runCurrent()
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
index 5969bd8..0173c32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.flags.ResourceBooleanFlag
import com.android.systemui.flags.UnreleasedFlag
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
+import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
@@ -74,10 +75,15 @@
.forEach { flagToken ->
setFlagsRule.enableFlags(flagToken)
aconfigFlags.setFlag(flagToken, testCase.areAllFlagsSet)
+ overrideResource(
+ R.bool.config_sceneContainerFrameworkEnabled,
+ testCase.isResourceConfigEnabled
+ )
}
underTest =
SceneContainerFlagsImpl(
+ context = context,
featureFlagsClassic = featureFlags,
isComposeAvailable = testCase.isComposeAvailable,
)
@@ -91,13 +97,12 @@
internal data class TestCase(
val isComposeAvailable: Boolean,
val areAllFlagsSet: Boolean,
+ val isResourceConfigEnabled: Boolean,
val expectedEnabled: Boolean,
) {
override fun toString(): String {
- return """
- (compose=$isComposeAvailable + flags=$areAllFlagsSet) -> expected=$expectedEnabled
- """
- .trimIndent()
+ return "(compose=$isComposeAvailable + flags=$areAllFlagsSet) + XML" +
+ " config=$isResourceConfigEnabled -> expected=$expectedEnabled"
}
}
@@ -105,17 +110,20 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun testCases() = buildList {
- repeat(4) { combination ->
- val isComposeAvailable = combination and 0b10 != 0
- val areAllFlagsSet = combination and 0b01 != 0
+ repeat(8) { combination ->
+ val isComposeAvailable = combination and 0b100 != 0
+ val areAllFlagsSet = combination and 0b010 != 0
+ val isResourceConfigEnabled = combination and 0b001 != 0
- val expectedEnabled = isComposeAvailable && areAllFlagsSet
+ val expectedEnabled =
+ isComposeAvailable && areAllFlagsSet && isResourceConfigEnabled
add(
TestCase(
isComposeAvailable = isComposeAvailable,
areAllFlagsSet = areAllFlagsSet,
expectedEnabled = expectedEnabled,
+ isResourceConfigEnabled = isResourceConfigEnabled,
)
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
index 94dcf7a..0ba820f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
@@ -116,27 +116,27 @@
@Test
fun testMessageVisible_whenFilteredNotifications() =
testComponent.runTest {
- val message by collectLastValue(footerViewModel.message)
+ val visible by collectLastValue(footerViewModel.message.isVisible)
activeNotificationListRepository.hasFilteredOutSeenNotifications.value = true
- assertThat(message?.visible).isTrue()
+ assertThat(visible).isTrue()
}
@Test
fun testMessageVisible_whenNoFilteredNotifications() =
testComponent.runTest {
- val message by collectLastValue(footerViewModel.message)
+ val visible by collectLastValue(footerViewModel.message.isVisible)
activeNotificationListRepository.hasFilteredOutSeenNotifications.value = false
- assertThat(message?.visible).isFalse()
+ assertThat(visible).isFalse()
}
@Test
fun testClearAllButtonVisible_whenHasClearableNotifs() =
testComponent.runTest {
- val button by collectLastValue(footerViewModel.clearAllButton)
+ val visible by collectLastValue(footerViewModel.clearAllButton.isVisible)
activeNotificationListRepository.notifStats.value =
NotifStats(
@@ -148,13 +148,13 @@
)
runCurrent()
- assertThat(button?.isVisible?.value).isTrue()
+ assertThat(visible?.value).isTrue()
}
@Test
fun testClearAllButtonVisible_whenHasNoClearableNotifs() =
testComponent.runTest {
- val button by collectLastValue(footerViewModel.clearAllButton)
+ val visible by collectLastValue(footerViewModel.clearAllButton.isVisible)
activeNotificationListRepository.notifStats.value =
NotifStats(
@@ -166,13 +166,13 @@
)
runCurrent()
- assertThat(button?.isVisible?.value).isFalse()
+ assertThat(visible?.value).isFalse()
}
@Test
fun testClearAllButtonAnimating_whenShadeExpandedAndTouchable() =
testComponent.runTest {
- val button by collectLastValue(footerViewModel.clearAllButton)
+ val visible by collectLastValue(footerViewModel.clearAllButton.isVisible)
runCurrent()
// WHEN shade is expanded
@@ -200,13 +200,13 @@
runCurrent()
// THEN button visibility should animate
- assertThat(button?.isVisible?.isAnimating).isTrue()
+ assertThat(visible?.isAnimating).isTrue()
}
@Test
fun testClearAllButtonAnimating_whenShadeNotExpanded() =
testComponent.runTest {
- val button by collectLastValue(footerViewModel.clearAllButton)
+ val visible by collectLastValue(footerViewModel.clearAllButton.isVisible)
runCurrent()
// WHEN shade is collapsed
@@ -234,6 +234,6 @@
runCurrent()
// THEN button visibility should not animate
- assertThat(button?.isVisible?.isAnimating).isFalse()
+ assertThat(visible?.isAnimating).isFalse()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
new file mode 100644
index 0000000..ce47170
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy
+
+import android.hardware.devicestate.DeviceStateManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableResources
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_FLIPPED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN
+import com.android.systemui.statusbar.policy.DevicePostureController.SUPPORTED_POSTURES_SIZE
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class DevicePostureControllerImplTest : SysuiTestCase() {
+ private val useBaseStateDeviceState = SUPPORTED_POSTURES_SIZE
+ private val deviceStateToPostureMapping =
+ arrayOf(
+ "$DEVICE_POSTURE_UNKNOWN:$DEVICE_POSTURE_UNKNOWN",
+ "$DEVICE_POSTURE_CLOSED:$DEVICE_POSTURE_CLOSED",
+ "$DEVICE_POSTURE_HALF_OPENED:$DEVICE_POSTURE_HALF_OPENED",
+ "$DEVICE_POSTURE_OPENED:$DEVICE_POSTURE_OPENED",
+ "$DEVICE_POSTURE_FLIPPED:$DEVICE_POSTURE_FLIPPED",
+ "$useBaseStateDeviceState:1000"
+ )
+ @Mock private lateinit var deviceStateManager: DeviceStateManager
+ @Captor
+ private lateinit var deviceStateCallback: ArgumentCaptor<DeviceStateManager.DeviceStateCallback>
+
+ private lateinit var mainExecutor: FakeExecutor
+ private lateinit var testableResources: TestableResources
+ private lateinit var underTest: DevicePostureControllerImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ mainExecutor = FakeExecutor(FakeSystemClock())
+ testableResources = context.getOrCreateTestableResources()
+ testableResources.addOverride(
+ com.android.internal.R.array.config_device_state_postures,
+ deviceStateToPostureMapping
+ )
+ underTest =
+ DevicePostureControllerImpl(
+ context,
+ deviceStateManager,
+ mainExecutor,
+ )
+ verifyRegistersForDeviceStateCallback()
+ }
+
+ @Test
+ fun testPostureChanged_updates() {
+ var posture = -1
+ underTest.addCallback { posture = it }
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_UNKNOWN)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_UNKNOWN)
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_CLOSED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_CLOSED)
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_OPENED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_OPENED)
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_FLIPPED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_FLIPPED)
+ }
+
+ @Test
+ fun testPostureChanged_useBaseUpdate() {
+ var posture = -1
+ underTest.addCallback { posture = it }
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
+
+ // base state change doesn't change the posture
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_CLOSED)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
+
+ // WHEN the display state maps to using the base state, then posture updates
+ deviceStateCallback.value.onStateChanged(useBaseStateDeviceState)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_CLOSED)
+ }
+
+ @Test
+ fun baseStateChanges_doesNotUpdatePosture() {
+ var numPostureChanges = 0
+ underTest.addCallback { numPostureChanges++ }
+
+ deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+ assertThat(numPostureChanges).isEqualTo(1)
+
+ // base state changes doesn't send another posture update since the device state isn't
+ // useBaseStateDeviceState
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_CLOSED)
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_HALF_OPENED)
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_FLIPPED)
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_OPENED)
+ deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_UNKNOWN)
+ assertThat(numPostureChanges).isEqualTo(1)
+ }
+
+ private fun verifyRegistersForDeviceStateCallback() {
+ verify(deviceStateManager).registerCallback(eq(mainExecutor), deviceStateCallback.capture())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index c454b45..1123688 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -61,10 +61,12 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.monet.Style;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;
import com.google.common.util.concurrent.MoreExecutors;
@@ -88,7 +90,10 @@
private static final int USER_SYSTEM = UserHandle.USER_SYSTEM;
private static final int USER_SECONDARY = 10;
-
+ @Mock
+ private JavaAdapter mJavaAdapter;
+ @Mock
+ private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private ThemeOverlayController mThemeOverlayController;
@Mock
private Executor mBgExecutor;
@@ -150,11 +155,12 @@
.thenReturn(Color.YELLOW);
when(mResources.getColor(eq(android.R.color.system_neutral2_500), any()))
.thenReturn(Color.BLACK);
+
mThemeOverlayController = new ThemeOverlayController(mContext,
mBroadcastDispatcher, mBgHandler, mMainExecutor, mBgExecutor, mThemeOverlayApplier,
mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle,
- mUiModeManager) {
+ mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager) {
@VisibleForTesting
protected boolean isNightMode() {
return false;
@@ -736,7 +742,7 @@
mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier,
mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle,
- mUiModeManager) {
+ mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager) {
@VisibleForTesting
protected boolean isNightMode() {
return false;
@@ -776,7 +782,7 @@
mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier,
mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle,
- mUiModeManager) {
+ mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager) {
@VisibleForTesting
protected boolean isNightMode() {
return false;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
index 9fe2f56..14fb054 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
@@ -15,9 +15,10 @@
*/
package com.android.systemui.unfold.progress
+import android.os.Handler
+import android.os.HandlerThread
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
@@ -26,6 +27,8 @@
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
import com.android.systemui.unfold.util.TestFoldStateProvider
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,16 +40,28 @@
private val foldStateProvider: TestFoldStateProvider = TestFoldStateProvider()
private val listener = TestUnfoldProgressListener()
private lateinit var progressProvider: UnfoldTransitionProgressProvider
+ private val schedulerFactory =
+ mock<UnfoldFrameCallbackScheduler.Factory>().apply {
+ whenever(create()).then { UnfoldFrameCallbackScheduler() }
+ }
+ private val mockBgHandler = mock<Handler>()
+ private val fakeHandler = Handler(HandlerThread("UnfoldBg").apply { start() }.looper)
@Before
fun setUp() {
- progressProvider = PhysicsBasedUnfoldTransitionProgressProvider(context, foldStateProvider)
+ progressProvider =
+ PhysicsBasedUnfoldTransitionProgressProvider(
+ context,
+ schedulerFactory,
+ foldStateProvider = foldStateProvider,
+ progressHandler = fakeHandler
+ )
progressProvider.addCallback(listener)
}
@Test
fun testUnfold_emitsIncreasingTransitionEvents() {
- runOnMainThreadWithInterval(
+ runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) },
{ foldStateProvider.sendHingeAngleUpdate(10f) },
{ foldStateProvider.sendUnfoldedScreenAvailable() },
@@ -63,7 +78,7 @@
@Test
fun testUnfold_emitsFinishingEvent() {
- runOnMainThreadWithInterval(
+ runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) },
{ foldStateProvider.sendHingeAngleUpdate(10f) },
{ foldStateProvider.sendUnfoldedScreenAvailable() },
@@ -77,7 +92,7 @@
@Test
fun testUnfold_screenAvailableOnlyAfterFullUnfold_emitsIncreasingTransitionEvents() {
- runOnMainThreadWithInterval(
+ runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) },
{ foldStateProvider.sendHingeAngleUpdate(10f) },
{ foldStateProvider.sendHingeAngleUpdate(90f) },
@@ -94,7 +109,7 @@
@Test
fun testFold_emitsDecreasingTransitionEvents() {
- runOnMainThreadWithInterval(
+ runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_CLOSING) },
{ foldStateProvider.sendHingeAngleUpdate(170f) },
{ foldStateProvider.sendHingeAngleUpdate(90f) },
@@ -110,7 +125,7 @@
@Test
fun testUnfoldAndStopUnfolding_finishesTheUnfoldTransition() {
- runOnMainThreadWithInterval(
+ runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) },
{ foldStateProvider.sendUnfoldedScreenAvailable() },
{ foldStateProvider.sendHingeAngleUpdate(10f) },
@@ -126,7 +141,7 @@
@Test
fun testFoldImmediatelyAfterUnfold_runsFoldAnimation() {
- runOnMainThreadWithInterval(
+ runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) },
{ foldStateProvider.sendUnfoldedScreenAvailable() },
{ foldStateProvider.sendHingeAngleUpdate(10f) },
@@ -144,9 +159,12 @@
with(listener.ensureTransitionFinished()) { assertHasFoldAnimationAtTheEnd() }
}
- private fun runOnMainThreadWithInterval(vararg blocks: () -> Unit, intervalMillis: Long = 60) {
+ private fun runOnProgressThreadWithInterval(
+ vararg blocks: () -> Unit,
+ intervalMillis: Long = 60,
+ ) {
blocks.forEach {
- InstrumentationRegistry.getInstrumentation().runOnMainSync { it() }
+ fakeHandler.post(it)
Thread.sleep(intervalMillis)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index aa49287..552b60c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -37,7 +37,6 @@
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.fail
import java.util.concurrent.Executor
@@ -105,16 +104,15 @@
foldStateProvider =
DeviceFoldStateProvider(
- config,
- testHingeAngleProvider,
- screenOnStatusProvider,
- foldProvider,
- activityTypeProvider,
- unfoldKeyguardVisibilityProvider,
- rotationChangeProvider,
- context,
- context.mainExecutor,
- handler
+ config,
+ context,
+ screenOnStatusProvider,
+ activityTypeProvider,
+ unfoldKeyguardVisibilityProvider,
+ foldProvider,
+ testHingeAngleProvider,
+ rotationChangeProvider,
+ handler
)
foldStateProvider.addCallback(
@@ -151,6 +149,12 @@
null
}
+ whenever(handler.post(any<Runnable>())).then { invocationOnMock ->
+ val runnable = invocationOnMock.getArgument<Runnable>(0)
+ runnable.run()
+ null
+ }
+
// By default, we're on launcher.
setupForegroundActivityType(isHomeActivity = true)
setIsLargeScreen(true)
@@ -171,7 +175,7 @@
}
@Test
- fun testOnUnfold_hingeAngleDecreasesBeforeInnerScreenAvailable_emitsOnlyStartAndInnerScreenAvailableEvents() {
+ fun onUnfold_angleDecrBeforeInnerScrAvailable_emitsOnlyStartAndInnerScrAvailableEvents() {
setFoldState(folded = true)
foldUpdates.clear()
@@ -187,7 +191,7 @@
}
@Test
- fun testOnUnfold_hingeAngleDecreasesAfterInnerScreenAvailable_emitsStartInnerScreenAvailableAndStartClosingEvents() {
+ fun onUnfold_angleDecrAfterInnerScrAvailable_emitsStartInnerScrAvailableAndStartClosingEvnts() {
setFoldState(folded = true)
foldUpdates.clear()
@@ -690,7 +694,7 @@
callbacks.forEach { it.onFoldUpdated(isFolded) }
}
- fun getNumberOfCallbacks(): Int{
+ fun getNumberOfCallbacks(): Int {
return callbacks.size
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt
index 6d2f00d..080689a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt
@@ -59,13 +59,36 @@
}
@Test
+ fun multipleStartAddsTheCallbackOnce() {
+ underTest.start()
+ underTest.start()
+ underTest.start()
+ underTest.start()
+
+ verify(drawable).registerAnimationCallback(any())
+ }
+
+ @Test
fun stopRemovesTheCallback() {
+ underTest.start()
+
underTest.stop()
verify(drawable).unregisterAnimationCallback(any())
}
@Test
+ fun callbackSurvivesClearAnimationCallbacks() {
+ underTest.start()
+
+ underTest.clearAnimationCallbacks()
+
+ verify(drawable).clearAnimationCallbacks()
+ // start + re-add after #clearAnimationCallbacks
+ verify(drawable, times(2)).registerAnimationCallback(capture(callbackCaptor))
+ }
+
+ @Test
fun animationLooped() {
underTest.start()
verify(drawable).registerAnimationCallback(capture(callbackCaptor))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 8aa729c..52c25f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -522,7 +522,8 @@
(sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0;
});
- mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
+ mPositioner = new TestableBubblePositioner(mContext,
+ mContext.getSystemService(WindowManager.class));
mPositioner.setMaxBubbles(5);
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, mEducationController,
syncExecutor);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeCustomTileStatePersister.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeCustomTileStatePersister.kt
new file mode 100644
index 0000000..29702eb
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeCustomTileStatePersister.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.external
+
+import android.service.quicksettings.Tile
+
+class FakeCustomTileStatePersister : CustomTileStatePersister {
+
+ private val tiles: MutableMap<TileServiceKey, Tile> = mutableMapOf()
+
+ override fun readState(key: TileServiceKey): Tile? = tiles[key]
+
+ override fun persistState(key: TileServiceKey, tile: Tile) {
+ tiles[key] = tile
+ }
+
+ override fun removeState(key: TileServiceKey) {
+ tiles.remove(key)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/TileSubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/TileSubject.kt
new file mode 100644
index 0000000..d2351dc
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/TileSubject.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom
+
+import android.service.quicksettings.Tile
+import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.tiles
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.Subject
+import com.google.common.truth.Subject.Factory
+import com.google.common.truth.Truth
+
+/**
+ * [Tile]-specific extension for [Truth]. Use [assertThat] or [tiles] to get an instance of this
+ * subject.
+ */
+class TileSubject private constructor(failureMetadata: FailureMetadata, subject: Tile?) :
+ Subject(failureMetadata, subject) {
+
+ private val actual: Tile? = subject
+
+ /** Asserts if the [Tile] fields are the same. */
+ fun isEqualTo(other: Tile?) {
+ if (actual == null) {
+ check("other").that(other).isNull()
+ return
+ } else {
+ check("other").that(other).isNotNull()
+ other ?: return
+ }
+
+ check("icon").that(actual.icon).isEqualTo(other.icon)
+ check("label").that(actual.label).isEqualTo(other.label)
+ check("subtitle").that(actual.subtitle).isEqualTo(other.subtitle)
+ check("contentDescription")
+ .that(actual.contentDescription)
+ .isEqualTo(other.contentDescription)
+ check("stateDescription").that(actual.stateDescription).isEqualTo(other.stateDescription)
+ check("activityLaunchForClick")
+ .that(actual.activityLaunchForClick)
+ .isEqualTo(other.activityLaunchForClick)
+ check("state").that(actual.state).isEqualTo(other.state)
+ }
+
+ companion object {
+
+ /** Returns a factory to be used with [Truth.assertAbout]. */
+ fun tiles(): Factory<TileSubject, Tile?> {
+ return Factory { failureMetadata: FailureMetadata, subject: Tile? ->
+ TileSubject(failureMetadata, subject)
+ }
+ }
+
+ /** Shortcut for `Truth.assertAbout(tiles()).that(tile)`. */
+ fun assertThat(tile: Tile?): TileSubject = Truth.assertAbout(tiles()).that(tile)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
index 13910fd..ccba072 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
@@ -19,15 +19,20 @@
import android.content.ComponentName
import android.os.UserHandle
import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
class FakeCustomTileDefaultsRepository : CustomTileDefaultsRepository {
private val defaults: MutableMap<DefaultsKey, CustomTileDefaults> = mutableMapOf()
- private val defaultsFlow = MutableSharedFlow<DefaultsRequest>()
+ private val defaultsFlow =
+ MutableSharedFlow<DefaultsRequest>(
+ replay = 1,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST
+ )
private val mutableDefaultsRequests: MutableList<DefaultsRequest> = mutableListOf()
val defaultsRequests: List<DefaultsRequest> = mutableDefaultsRequests
@@ -41,7 +46,7 @@
old == new
}
}
- .map { defaults[DefaultsKey(it.user, it.componentName)]!! }
+ .mapNotNull { defaults[DefaultsKey(it.user, it.componentName)] }
override fun requestNewDefaults(
user: UserHandle,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
new file mode 100644
index 0000000..ccf0391
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.custom.data.repository
+
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import com.android.systemui.qs.external.FakeCustomTileStatePersister
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.flow.Flow
+
+class FakeCustomTileRepository(
+ tileSpec: TileSpec.CustomTileSpec,
+ customTileStatePersister: FakeCustomTileStatePersister,
+ testBackgroundContext: CoroutineContext,
+) : CustomTileRepository {
+
+ private val realDelegate: CustomTileRepository =
+ CustomTileRepositoryImpl(
+ tileSpec,
+ customTileStatePersister,
+ testBackgroundContext,
+ )
+
+ override suspend fun restoreForTheUserIfNeeded(user: UserHandle, isPersistable: Boolean) =
+ realDelegate.restoreForTheUserIfNeeded(user, isPersistable)
+
+ override fun getTiles(user: UserHandle): Flow<Tile> = realDelegate.getTiles(user)
+
+ override fun getTile(user: UserHandle): Tile? = realDelegate.getTile(user)
+
+ override suspend fun updateWithTile(
+ user: UserHandle,
+ newTile: Tile,
+ isPersistable: Boolean,
+ ) = realDelegate.updateWithTile(user, newTile, isPersistable)
+
+ override suspend fun updateWithDefaults(
+ user: UserHandle,
+ defaults: CustomTileDefaults,
+ isPersistable: Boolean,
+ ) = realDelegate.updateWithDefaults(user, defaults, isPersistable)
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt
index a639df5..2bc2db3 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt
@@ -16,9 +16,12 @@
package com.android.systemui.unfold
+import android.os.Handler
import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.dagger.UseReceivingFilter
import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver
+import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.unfold.util.ATraceLoggerTransitionProgressListener
import dagger.Module
import dagger.Provides
@@ -33,16 +36,25 @@
@Singleton
fun provideTransitionProvider(
config: UnfoldTransitionConfig,
- traceListener: ATraceLoggerTransitionProgressListener,
+ traceListener: ATraceLoggerTransitionProgressListener.Factory,
remoteReceiverProvider: Provider<RemoteUnfoldTransitionReceiver>,
): Optional<RemoteUnfoldTransitionReceiver> {
if (!config.isEnabled) {
return Optional.empty()
}
val remoteReceiver = remoteReceiverProvider.get()
- remoteReceiver.addCallback(traceListener)
+ remoteReceiver.addCallback(traceListener.create("remoteReceiver"))
return Optional.of(remoteReceiver)
}
@Provides @UseReceivingFilter fun useReceivingFilter(): Boolean = true
+
+ @Provides
+ @UnfoldMain
+ fun provideMainRotationChangeProvider(
+ rotationChangeProviderFactory: RotationChangeProvider.Factory,
+ @UnfoldMain mainHandler: Handler,
+ ): RotationChangeProvider {
+ return rotationChangeProviderFactory.create(mainHandler)
+ }
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
index c3a6cf0..31b7ccc 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
@@ -22,12 +22,12 @@
import android.hardware.display.DisplayManager
import android.os.Handler
import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.dagger.UnfoldBg
import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver
import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.updates.RotationChangeProvider
-import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.util.CurrentActivityTypeProvider
import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
@@ -63,13 +63,12 @@
@BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor,
@BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
@BindsInstance displayManager: DisplayManager,
- @BindsInstance contentResolver: ContentResolver = context.contentResolver
+ @BindsInstance @UnfoldBg bgHandler: Handler,
+ @BindsInstance contentResolver: ContentResolver = context.contentResolver,
): UnfoldSharedComponent
}
val unfoldTransitionProvider: Optional<UnfoldTransitionProgressProvider>
- val hingeAngleProvider: HingeAngleProvider
- val rotationChangeProvider: RotationChangeProvider
}
/**
@@ -94,7 +93,8 @@
}
val remoteTransitionProgress: Optional<RemoteUnfoldTransitionReceiver>
- val rotationChangeProvider: RotationChangeProvider
+
+ @UnfoldMain fun getRotationChangeProvider(): RotationChangeProvider
}
/**
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 7473ca6..42d31b3 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -16,7 +16,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.UnfoldMain
import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
@@ -24,6 +27,7 @@
import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.FoldStateRepository
import com.android.systemui.unfold.updates.FoldStateRepositoryImpl
+import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
@@ -38,16 +42,18 @@
import javax.inject.Provider
import javax.inject.Singleton
-@Module(includes = [UnfoldSharedInternalModule::class])
+@Module(
+ includes =
+ [
+ UnfoldSharedInternalModule::class,
+ UnfoldRotationProviderInternalModule::class,
+ HingeAngleProviderInternalModule::class,
+ FoldStateProviderModule::class,
+ ]
+)
class UnfoldSharedModule {
@Provides
@Singleton
- fun provideFoldStateProvider(
- deviceFoldStateProvider: DeviceFoldStateProvider
- ): FoldStateProvider = deviceFoldStateProvider
-
- @Provides
- @Singleton
fun unfoldKeyguardVisibilityProvider(
impl: UnfoldKeyguardVisibilityManagerImpl
): UnfoldKeyguardVisibilityProvider = impl
@@ -60,9 +66,7 @@
@Provides
@Singleton
- fun foldStateRepository(
- impl: FoldStateRepositoryImpl
- ): FoldStateRepository = impl
+ fun foldStateRepository(impl: FoldStateRepositoryImpl): FoldStateRepository = impl
}
/**
@@ -77,17 +81,69 @@
fun unfoldTransitionProgressProvider(
config: UnfoldTransitionConfig,
scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
+ tracingListener: ATraceLoggerTransitionProgressListener.Factory,
+ physicsBasedUnfoldTransitionProgressProvider:
+ PhysicsBasedUnfoldTransitionProgressProvider.Factory,
+ fixedTimingTransitionProgressProvider: Provider<FixedTimingTransitionProgressProvider>,
+ foldStateProvider: FoldStateProvider,
+ @UnfoldMain mainHandler: Handler,
+ ): Optional<UnfoldTransitionProgressProvider> {
+ return createOptionalUnfoldTransitionProgressProvider(
+ config = config,
+ scaleAwareProviderFactory = scaleAwareProviderFactory,
+ tracingListener = tracingListener.create("MainThread"),
+ physicsBasedUnfoldTransitionProgressProvider =
+ physicsBasedUnfoldTransitionProgressProvider,
+ fixedTimingTransitionProgressProvider = fixedTimingTransitionProgressProvider,
+ foldStateProvider = foldStateProvider,
+ progressHandler = mainHandler,
+ )
+ }
+
+ @Provides
+ @Singleton
+ @UnfoldBg
+ fun unfoldBgTransitionProgressProvider(
+ config: UnfoldTransitionConfig,
+ scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
+ tracingListener: ATraceLoggerTransitionProgressListener.Factory,
+ physicsBasedUnfoldTransitionProgressProvider:
+ PhysicsBasedUnfoldTransitionProgressProvider.Factory,
+ fixedTimingTransitionProgressProvider: Provider<FixedTimingTransitionProgressProvider>,
+ @UnfoldBg bgFoldStateProvider: FoldStateProvider,
+ @UnfoldBg bgHandler: Handler,
+ ): Optional<UnfoldTransitionProgressProvider> {
+ return createOptionalUnfoldTransitionProgressProvider(
+ config = config,
+ scaleAwareProviderFactory = scaleAwareProviderFactory,
+ tracingListener = tracingListener.create("BgThread"),
+ physicsBasedUnfoldTransitionProgressProvider =
+ physicsBasedUnfoldTransitionProgressProvider,
+ fixedTimingTransitionProgressProvider = fixedTimingTransitionProgressProvider,
+ foldStateProvider = bgFoldStateProvider,
+ progressHandler = bgHandler,
+ )
+ }
+
+ private fun createOptionalUnfoldTransitionProgressProvider(
+ config: UnfoldTransitionConfig,
+ scaleAwareProviderFactory: ScaleAwareTransitionProgressProvider.Factory,
tracingListener: ATraceLoggerTransitionProgressListener,
physicsBasedUnfoldTransitionProgressProvider:
- Provider<PhysicsBasedUnfoldTransitionProgressProvider>,
+ PhysicsBasedUnfoldTransitionProgressProvider.Factory,
fixedTimingTransitionProgressProvider: Provider<FixedTimingTransitionProgressProvider>,
+ foldStateProvider: FoldStateProvider,
+ progressHandler: Handler,
): Optional<UnfoldTransitionProgressProvider> {
if (!config.isEnabled) {
return Optional.empty()
}
val baseProgressProvider =
if (config.isHingeAngleEnabled) {
- physicsBasedUnfoldTransitionProgressProvider.get()
+ physicsBasedUnfoldTransitionProgressProvider.create(
+ foldStateProvider,
+ progressHandler
+ )
} else {
fixedTimingTransitionProgressProvider.get()
}
@@ -101,26 +157,105 @@
}
@Provides
+ @Singleton
+ fun provideProgressForwarder(
+ config: UnfoldTransitionConfig,
+ progressForwarder: Provider<UnfoldTransitionProgressForwarder>
+ ): Optional<UnfoldTransitionProgressForwarder> {
+ if (!config.isEnabled) {
+ return Optional.empty()
+ }
+ return Optional.of(progressForwarder.get())
+ }
+}
+
+/**
+ * Provides [FoldStateProvider]. The [UnfoldBg] annotated binding sends progress in the [UnfoldBg]
+ * handler.
+ */
+@Module
+internal class FoldStateProviderModule {
+ @Provides
+ @Singleton
+ fun provideFoldStateProvider(
+ factory: DeviceFoldStateProvider.Factory,
+ @UnfoldMain hingeAngleProvider: HingeAngleProvider,
+ @UnfoldMain rotationChangeProvider: RotationChangeProvider,
+ @UnfoldMain mainHandler: Handler,
+ ): FoldStateProvider =
+ factory.create(
+ hingeAngleProvider,
+ rotationChangeProvider,
+ progressHandler = mainHandler
+ )
+
+ @Provides
+ @Singleton
+ @UnfoldBg
+ fun provideBgFoldStateProvider(
+ factory: DeviceFoldStateProvider.Factory,
+ @UnfoldBg hingeAngleProvider: HingeAngleProvider,
+ @UnfoldBg rotationChangeProvider: RotationChangeProvider,
+ @UnfoldBg bgHandler: Handler,
+ ): FoldStateProvider =
+ factory.create(
+ hingeAngleProvider,
+ rotationChangeProvider,
+ progressHandler = bgHandler
+ )
+}
+
+/** Provides bindings for both [UnfoldMain] and [UnfoldBg] [HingeAngleProvider]. */
+@Module
+internal class HingeAngleProviderInternalModule {
+ @Provides
+ @UnfoldMain
fun hingeAngleProvider(
config: UnfoldTransitionConfig,
- hingeAngleSensorProvider: Provider<HingeSensorAngleProvider>
+ @UnfoldMain handler: Handler,
+ hingeAngleSensorProvider: HingeSensorAngleProvider.Factory
): HingeAngleProvider {
return if (config.isHingeAngleEnabled) {
- hingeAngleSensorProvider.get()
+ hingeAngleSensorProvider.create(handler)
} else {
EmptyHingeAngleProvider
}
}
@Provides
- @Singleton
- fun provideProgressForwarder(
- config: UnfoldTransitionConfig,
- progressForwarder: Provider<UnfoldTransitionProgressForwarder>
- ): Optional<UnfoldTransitionProgressForwarder> {
- if (!config.isEnabled) {
- return Optional.empty()
+ @UnfoldBg
+ fun hingeAngleProviderBg(
+ config: UnfoldTransitionConfig,
+ @UnfoldBg handler: Handler,
+ hingeAngleSensorProvider: HingeSensorAngleProvider.Factory
+ ): HingeAngleProvider {
+ return if (config.isHingeAngleEnabled) {
+ hingeAngleSensorProvider.create(handler)
+ } else {
+ EmptyHingeAngleProvider
}
- return Optional.of(progressForwarder.get())
+ }
+}
+
+@Module
+internal class UnfoldRotationProviderInternalModule {
+ @Provides
+ @Singleton
+ @UnfoldMain
+ fun provideRotationChangeProvider(
+ rotationChangeProviderFactory: RotationChangeProvider.Factory,
+ @UnfoldMain mainHandler: Handler,
+ ): RotationChangeProvider {
+ return rotationChangeProviderFactory.create(mainHandler)
+ }
+
+ @Provides
+ @Singleton
+ @UnfoldBg
+ fun provideBgRotationChangeProvider(
+ rotationChangeProviderFactory: RotationChangeProvider.Factory,
+ @UnfoldBg bgHandler: Handler,
+ ): RotationChangeProvider {
+ return rotationChangeProviderFactory.create(bgHandler)
}
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 1839919..1cbaf31 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -48,6 +48,7 @@
singleThreadBgExecutor: Executor,
tracingTagPrefix: String,
displayManager: DisplayManager,
+ bgHandler: Handler,
): UnfoldSharedComponent =
DaggerUnfoldSharedComponent.factory()
.create(
@@ -62,6 +63,7 @@
singleThreadBgExecutor,
tracingTagPrefix,
displayManager,
+ bgHandler,
)
/**
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBg.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBg.kt
new file mode 100644
index 0000000..7cd4419
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBg.kt
@@ -0,0 +1,20 @@
+/*
+ * 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.dagger
+
+import javax.inject.Qualifier
+
+/** Annotation for background computations related to unfold lib. */
+@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class UnfoldBg
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index f8f168b..907bf46 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -20,6 +20,7 @@
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
+import android.os.Handler
import android.os.Trace
import android.util.FloatProperty
import android.util.Log
@@ -38,13 +39,25 @@
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
import com.android.systemui.unfold.updates.name
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
-/** Maps fold updates to unfold transition progress using DynamicAnimation. */
+/**
+ * Maps fold updates to unfold transition progress using DynamicAnimation.
+ *
+ * Note that all variable accesses must be done in the [Handler] provided in the constructor, that
+ * might be different than [mainHandler]. When a custom handler is provided, the [SpringAnimation]
+ * uses a scheduler different than the default one.
+ */
class PhysicsBasedUnfoldTransitionProgressProvider
-@Inject
-constructor(context: Context, private val foldStateProvider: FoldStateProvider) :
- UnfoldTransitionProgressProvider, FoldUpdatesListener, DynamicAnimation.OnAnimationEndListener {
+@AssistedInject
+constructor(
+ context: Context,
+ private val schedulerFactory: UnfoldFrameCallbackScheduler.Factory,
+ @Assisted private val foldStateProvider: FoldStateProvider,
+ @Assisted private val progressHandler: Handler,
+) : UnfoldTransitionProgressProvider, FoldUpdatesListener, DynamicAnimation.OnAnimationEndListener {
private val emphasizedInterpolator =
loadInterpolator(context, android.R.interpolator.fast_out_extra_slow_in)
@@ -63,6 +76,7 @@
private var transitionProgress: Float = 0.0f
set(value) {
+ assertInProgressThread()
if (isTransitionRunning) {
listeners.forEach { it.onTransitionProgress(value) }
}
@@ -72,8 +86,14 @@
private val listeners: MutableList<TransitionProgressListener> = mutableListOf()
init {
- foldStateProvider.addCallback(this)
- foldStateProvider.start()
+ progressHandler.post {
+ // The scheduler needs to be created in the progress handler in order to get the correct
+ // choreographer and frame callbacks. This is because the choreographer can be get only
+ // as a thread local.
+ springAnimation.scheduler = schedulerFactory.create()
+ foldStateProvider.addCallback(this)
+ foldStateProvider.start()
+ }
}
override fun destroy() {
@@ -81,6 +101,8 @@
}
override fun onHingeAngleUpdate(angle: Float) {
+ assertInProgressThread()
+
if (!isTransitionRunning || isAnimatedCancelRunning) return
val progress = saturate(angle / FINAL_HINGE_ANGLE_POSITION)
springAnimation.animateToFinalPosition(progress)
@@ -90,6 +112,7 @@
if (amount < low) low else if (amount > high) high else amount
override fun onFoldUpdate(@FoldUpdate update: Int) {
+ assertInProgressThread()
when (update) {
FOLD_UPDATE_FINISH_FULL_OPEN,
FOLD_UPDATE_FINISH_HALF_OPEN -> {
@@ -148,6 +171,7 @@
}
private fun cancelTransition(endValue: Float, animate: Boolean) {
+ assertInProgressThread()
if (isTransitionRunning && animate) {
if (endValue == 1.0f && !isAnimatedCancelRunning) {
listeners.forEach { it.onTransitionFinishing() }
@@ -165,7 +189,6 @@
isAnimatedCancelRunning = false
isTransitionRunning = false
springAnimation.cancel()
-
cannedAnimator?.removeAllListeners()
cannedAnimator?.cancel()
cannedAnimator = null
@@ -182,7 +205,7 @@
animation: DynamicAnimation<out DynamicAnimation<*>>,
canceled: Boolean,
value: Float,
- velocity: Float
+ velocity: Float,
) {
if (isAnimatedCancelRunning) {
cancelTransition(value, animate = false)
@@ -202,6 +225,7 @@
}
private fun startTransition(startValue: Float) {
+ assertInProgressThread()
if (!isTransitionRunning) onStartTransition()
springAnimation.apply {
@@ -221,14 +245,16 @@
}
override fun addCallback(listener: TransitionProgressListener) {
- listeners.add(listener)
+ progressHandler.post { listeners.add(listener) }
}
override fun removeCallback(listener: TransitionProgressListener) {
- listeners.remove(listener)
+ progressHandler.post { listeners.remove(listener) }
}
private fun startCannedCancelAnimation() {
+ assertInProgressThread()
+
cannedAnimator?.cancel()
cannedAnimator = null
@@ -264,7 +290,7 @@
override fun setValue(
provider: PhysicsBasedUnfoldTransitionProgressProvider,
- value: Float
+ value: Float,
) {
provider.transitionProgress = value
}
@@ -272,6 +298,25 @@
override fun get(provider: PhysicsBasedUnfoldTransitionProgressProvider): Float =
provider.transitionProgress
}
+
+ private fun assertInProgressThread() {
+ check(progressHandler.looper.isCurrentThread) {
+ val progressThread = progressHandler.looper.thread
+ val thisThread = Thread.currentThread()
+ """should be called from the progress thread.
+ progressThread=$progressThread tid=${progressThread.id}
+ Thread.currentThread()=$thisThread tid=${thisThread.id}"""
+ .trimMargin()
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ foldStateProvider: FoldStateProvider,
+ handler: Handler,
+ ): PhysicsBasedUnfoldTransitionProgressProvider
+ }
}
private const val TAG = "PhysicsBasedUnfoldTransitionProgressProvider"
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldFrameCallbackScheduler.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldFrameCallbackScheduler.kt
new file mode 100644
index 0000000..1dffd84
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldFrameCallbackScheduler.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.view.Choreographer
+import androidx.dynamicanimation.animation.FrameCallbackScheduler
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/**
+ * Scheduler that posts animation progresses on a thread different than the ui one.
+ *
+ * The following is taken from [AnimationHandler.FrameCallbackScheduler16]. It is extracted here as
+ * there are no guarantees which implementation the [DynamicAnimation] class would use otherwise.
+ * This allows classes using [DynamicAnimation] to be created in any thread, but still use the
+ * scheduler for a specific thread.
+ *
+ * Technically the [AssistedInject] is not needed: it's just to have a nicer factory with a
+ * documentation snippet instead of using a plain dagger provider.
+ */
+class UnfoldFrameCallbackScheduler @AssistedInject constructor() : FrameCallbackScheduler {
+
+ private val choreographer = Choreographer.getInstance()
+ private val looper =
+ Looper.myLooper() ?: error("This should be created in a thread with a looper.")
+
+ override fun postFrameCallback(frameCallback: Runnable) {
+ choreographer.postFrameCallback { frameCallback.run() }
+ }
+
+ override fun isCurrentThread(): Boolean {
+ return looper.isCurrentThread
+ }
+
+ @AssistedFactory
+ interface Factory {
+ /**
+ * Creates a [FrameCallbackScheduler] that uses [Choreographer] to post frame callbacks.
+ *
+ * Note that the choreographer used depends on the thread this [create] is called on, as it
+ * is get from a thread static attribute.
+ */
+ fun create(): UnfoldFrameCallbackScheduler
+ }
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 003013e..77f637b 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -23,37 +23,34 @@
import androidx.core.util.Consumer
import com.android.systemui.unfold.compat.INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP
import com.android.systemui.unfold.config.UnfoldTransitionConfig
-import com.android.systemui.unfold.dagger.UnfoldMain
-import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
-import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
-import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
import com.android.systemui.unfold.updates.hinge.FULLY_CLOSED_DEGREES
import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.util.CurrentActivityTypeProvider
import com.android.systemui.unfold.util.UnfoldKeyguardVisibilityProvider
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.Executor
-import javax.inject.Inject
class DeviceFoldStateProvider
-@Inject
+@AssistedInject
constructor(
config: UnfoldTransitionConfig,
- private val hingeAngleProvider: HingeAngleProvider,
+ private val context: Context,
private val screenStatusProvider: ScreenStatusProvider,
- private val foldProvider: FoldProvider,
private val activityTypeProvider: CurrentActivityTypeProvider,
private val unfoldKeyguardVisibilityProvider: UnfoldKeyguardVisibilityProvider,
- private val rotationChangeProvider: RotationChangeProvider,
- private val context: Context,
- @UnfoldMain private val mainExecutor: Executor,
- @UnfoldMain private val handler: Handler
+ private val foldProvider: FoldProvider,
+ @Assisted private val hingeAngleProvider: HingeAngleProvider,
+ @Assisted private val rotationChangeProvider: RotationChangeProvider,
+ @Assisted private val progressHandler: Handler,
) : FoldStateProvider {
+ private val outputListeners = CopyOnWriteArrayList<FoldStateProvider.FoldUpdatesListener>()
- private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
-
- @FoldUpdate private var lastFoldUpdate: Int? = null
+ @FoldStateProvider.FoldUpdate private var lastFoldUpdate: Int? = null
@FloatRange(from = 0.0, to = 180.0) private var lastHingeAngle: Float = 0f
@FloatRange(from = 0.0, to = 180.0) private var lastHingeAngleBeforeTransition: Float = 0f
@@ -61,11 +58,9 @@
private val hingeAngleListener = HingeAngleListener()
private val screenListener = ScreenStatusListener()
private val foldStateListener = FoldStateListener()
- private val mainLooper = handler.looper
private val timeoutRunnable = Runnable { cancelAnimation() }
- private val rotationListener = RotationListener {
- if (isTransitionInProgress) cancelAnimation()
- }
+ private val rotationListener = FoldRotationListener()
+ private val progressExecutor = Executor { progressHandler.post(it) }
/**
* Time after which [FOLD_UPDATE_FINISH_HALF_OPEN] is emitted following a
@@ -80,9 +75,9 @@
private var isStarted = false
override fun start() {
- assertMainThread()
if (isStarted) return
- foldProvider.registerCallback(foldStateListener, mainExecutor)
+ foldProvider.registerCallback(foldStateListener, progressExecutor)
+ // TODO(b/277879146): get callbacks in the background
screenStatusProvider.addCallback(screenListener)
hingeAngleProvider.addCallback(hingeAngleListener)
rotationChangeProvider.addCallback(rotationListener)
@@ -91,7 +86,6 @@
}
override fun stop() {
- assertMainThread()
screenStatusProvider.removeCallback(screenListener)
foldProvider.unregisterCallback(foldStateListener)
hingeAngleProvider.removeCallback(hingeAngleListener)
@@ -101,11 +95,11 @@
isStarted = false
}
- override fun addCallback(listener: FoldUpdatesListener) {
+ override fun addCallback(listener: FoldStateProvider.FoldUpdatesListener) {
outputListeners.add(listener)
}
- override fun removeCallback(listener: FoldUpdatesListener) {
+ override fun removeCallback(listener: FoldStateProvider.FoldUpdatesListener) {
outputListeners.remove(listener)
}
@@ -121,6 +115,7 @@
lastFoldUpdate == FOLD_UPDATE_START_CLOSING
private fun onHingeAngle(angle: Float) {
+ assertInProgressThread()
if (DEBUG) {
Log.d(
TAG,
@@ -131,14 +126,14 @@
}
val currentDirection =
- if (angle < lastHingeAngle) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING
+ if (angle < lastHingeAngle) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING
if (isTransitionInProgress && currentDirection != lastFoldUpdate) {
lastHingeAngleBeforeTransition = lastHingeAngle
}
val isClosing = angle < lastHingeAngleBeforeTransition
val transitionUpdate =
- if (isClosing) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING
+ if (isClosing) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING
val angleChangeSurpassedThreshold =
Math.abs(angle - lastHingeAngleBeforeTransition) > HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES
val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES
@@ -150,12 +145,12 @@
angleChangeSurpassedThreshold && // Do not react immediately to small changes in angle
eventNotAlreadyDispatched && // we haven't sent transition event already
!isFullyOpened && // do not send transition event if we are in fully opened hinge
- // angle range as closing threshold could overlap this range
+ // angle range as closing threshold could overlap this range
screenAvailableEventSent && // do not send transition event if we are still in the
- // process of turning on the inner display
+ // process of turning on the inner display
isClosingThresholdMet(angle) && // hinge angle is below certain threshold.
isOnLargeScreen // Avoids sending closing event when on small screen.
- // Start event is sent regardless due to hall sensor.
+ // Start event is sent regardless due to hall sensor.
) {
notifyFoldUpdate(transitionUpdate, lastHingeAngle)
}
@@ -202,6 +197,7 @@
private inner class FoldStateListener : FoldProvider.FoldCallback {
override fun onFoldUpdated(isFolded: Boolean) {
+ assertInProgressThread()
this@DeviceFoldStateProvider.isFolded = isFolded
lastHingeAngle = FULLY_CLOSED_DEGREES
@@ -218,7 +214,14 @@
}
}
- private fun notifyFoldUpdate(@FoldUpdate update: Int, angle: Float) {
+ private inner class FoldRotationListener : RotationChangeProvider.RotationListener {
+ override fun onRotationChanged(newRotation: Int) {
+ assertInProgressThread()
+ if (isTransitionInProgress) cancelAnimation()
+ }
+ }
+
+ private fun notifyFoldUpdate(@FoldStateProvider.FoldUpdate update: Int, angle: Float) {
if (DEBUG) {
Log.d(TAG, update.name())
}
@@ -236,11 +239,11 @@
if (isTransitionInProgress) {
cancelTimeout()
}
- handler.postDelayed(timeoutRunnable, halfOpenedTimeoutMillis.toLong())
+ progressHandler.postDelayed(timeoutRunnable, halfOpenedTimeoutMillis.toLong())
}
private fun cancelTimeout() {
- handler.removeCallbacks(timeoutRunnable)
+ progressHandler.removeCallbacks(timeoutRunnable)
}
private fun cancelAnimation(): Unit =
@@ -249,42 +252,61 @@
private inner class ScreenStatusListener : ScreenStatusProvider.ScreenListener {
override fun onScreenTurnedOn() {
- // Trigger this event only if we are unfolded and this is the first screen
- // turned on event since unfold started. This prevents running the animation when
- // turning on the internal display using the power button.
- // Initially isUnfoldHandled is true so it will be reset to false *only* when we
- // receive 'folded' event. If SystemUI started when device is already folded it will
- // still receive 'folded' event on startup.
- if (!isFolded && !isUnfoldHandled) {
- outputListeners.forEach { it.onUnfoldedScreenAvailable() }
- isUnfoldHandled = true
+ executeInProgressThread {
+ // Trigger this event only if we are unfolded and this is the first screen
+ // turned on event since unfold started. This prevents running the animation when
+ // turning on the internal display using the power button.
+ // Initially isUnfoldHandled is true so it will be reset to false *only* when we
+ // receive 'folded' event. If SystemUI started when device is already folded it will
+ // still receive 'folded' event on startup.
+ if (!isFolded && !isUnfoldHandled) {
+ outputListeners.forEach { it.onUnfoldedScreenAvailable() }
+ isUnfoldHandled = true
+ }
}
}
override fun markScreenAsTurnedOn() {
- if (!isFolded) {
- isUnfoldHandled = true
+ executeInProgressThread {
+ if (!isFolded) {
+ isUnfoldHandled = true
+ }
}
}
override fun onScreenTurningOn() {
- isScreenOn = true
- updateHingeAngleProviderState()
+ executeInProgressThread {
+ isScreenOn = true
+ updateHingeAngleProviderState()
+ }
}
override fun onScreenTurningOff() {
- isScreenOn = false
- updateHingeAngleProviderState()
+ executeInProgressThread {
+ isScreenOn = false
+ updateHingeAngleProviderState()
+ }
+ }
+
+ /**
+ * Needed just for compatibility while not all data sources are providing data in the
+ * background.
+ *
+ * TODO(b/277879146): Remove once ScreeStatusProvider provides in the background.
+ */
+ private fun executeInProgressThread(f: () -> Unit) {
+ progressHandler.post { f() }
}
}
private fun isOnLargeScreen(): Boolean {
- return context.resources.configuration.smallestScreenWidthDp >
- INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP
+ return context.resources.configuration.smallestScreenWidthDp >
+ INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP
}
/** While the screen is off or the device is folded, hinge angle updates are not needed. */
private fun updateHingeAngleProviderState() {
+ assertInProgressThread()
if (isScreenOn && !isFolded) {
hingeAngleProvider.start()
} else {
@@ -294,20 +316,34 @@
private inner class HingeAngleListener : Consumer<Float> {
override fun accept(angle: Float) {
+ assertInProgressThread()
onHingeAngle(angle)
}
}
- private fun assertMainThread() {
- check(mainLooper.isCurrentThread) {
- ("should be called from the main thread." +
- " sMainLooper.threadName=" + mainLooper.thread.name +
- " Thread.currentThread()=" + Thread.currentThread().name)
+ private fun assertInProgressThread() {
+ check(progressHandler.looper.isCurrentThread) {
+ val progressThread = progressHandler.looper.thread
+ val thisThread = Thread.currentThread()
+ """should be called from the progress thread.
+ progressThread=$progressThread tid=${progressThread.id}
+ Thread.currentThread()=$thisThread tid=${thisThread.id}"""
+ .trimMargin()
}
}
+
+ @AssistedFactory
+ interface Factory {
+ /** Creates a [DeviceFoldStateProvider] using the provided dependencies. */
+ fun create(
+ hingeAngleProvider: HingeAngleProvider,
+ rotationChangeProvider: RotationChangeProvider,
+ progressHandler: Handler,
+ ): DeviceFoldStateProvider
+ }
}
-fun @receiver:FoldUpdate Int.name() =
+fun @receiver:FoldStateProvider.FoldUpdate Int.name() =
when (this) {
FOLD_UPDATE_START_OPENING -> "START_OPENING"
FOLD_UPDATE_START_CLOSING -> "START_CLOSING"
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
index ce8f1a1..82ea362 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
@@ -20,20 +20,21 @@
import android.hardware.display.DisplayManager
import android.os.Handler
import android.os.RemoteException
-import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.util.CallbackController
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
/**
- * Allows to subscribe to rotation changes. Updates are provided for the display associated
- * to [context].
+ * Allows to subscribe to rotation changes. Updates are provided for the display associated to
+ * [context].
*/
class RotationChangeProvider
-@Inject
+@AssistedInject
constructor(
private val displayManager: DisplayManager,
private val context: Context,
- @UnfoldMain private val mainHandler: Handler,
+ @Assisted private val handler: Handler,
) : CallbackController<RotationChangeProvider.RotationListener> {
private val listeners = mutableListOf<RotationListener>()
@@ -42,7 +43,7 @@
private var lastRotation: Int? = null
override fun addCallback(listener: RotationListener) {
- mainHandler.post {
+ handler.post {
if (listeners.isEmpty()) {
subscribeToRotation()
}
@@ -51,7 +52,7 @@
}
override fun removeCallback(listener: RotationListener) {
- mainHandler.post {
+ handler.post {
listeners -= listener
if (listeners.isEmpty()) {
unsubscribeToRotation()
@@ -62,7 +63,7 @@
private fun subscribeToRotation() {
try {
- displayManager.registerDisplayListener(displayListener, mainHandler)
+ displayManager.registerDisplayListener(displayListener, handler)
} catch (e: RemoteException) {
throw e.rethrowFromSystemServer()
}
@@ -100,4 +101,10 @@
override fun onDisplayRemoved(displayId: Int) {}
}
+
+ @AssistedFactory
+ interface Factory {
+ /** Creates a new [RotationChangeProvider] that provides updated using [handler]. */
+ fun create(handler: Handler): RotationChangeProvider
+ }
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
index 89fb12e..14c4cc0 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
@@ -18,21 +18,26 @@
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
+import android.os.Handler
import android.os.Trace
import androidx.core.util.Consumer
import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.Executor
-import javax.inject.Inject
internal class HingeSensorAngleProvider
-@Inject
+@AssistedInject
constructor(
private val sensorManager: SensorManager,
- @UnfoldSingleThreadBg private val singleThreadBgExecutor: Executor
+ @UnfoldSingleThreadBg private val singleThreadBgExecutor: Executor,
+ @Assisted private val listenerHandler: Handler,
) : HingeAngleProvider {
private val sensorListener = HingeAngleSensorListener()
- private val listeners: MutableList<Consumer<Float>> = arrayListOf()
+ private val listeners: MutableList<Consumer<Float>> = CopyOnWriteArrayList()
var started = false
override fun start() {
@@ -43,7 +48,8 @@
sensorManager.registerListener(
sensorListener,
sensor,
- SensorManager.SENSOR_DELAY_FASTEST
+ SensorManager.SENSOR_DELAY_FASTEST,
+ listenerHandler
)
Trace.endSection()
@@ -75,4 +81,10 @@
listeners.forEach { it.accept(event.values[0]) }
}
}
+
+ @AssistedFactory
+ interface Factory {
+ /** Creates an [HingeSensorAngleProvider] that sends updates using [handler]. */
+ fun create(handler: Handler): HingeSensorAngleProvider
+ }
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
index d8bc018..a31896a 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
@@ -16,7 +16,9 @@
import android.os.Trace
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import javax.inject.Qualifier
/**
@@ -26,11 +28,11 @@
* for each fold/unfold: in (1) systemui and (2) launcher process.
*/
class ATraceLoggerTransitionProgressListener
-@Inject
-internal constructor(@UnfoldTransitionATracePrefix tracePrefix: String) :
+@AssistedInject
+internal constructor(@UnfoldTransitionATracePrefix tracePrefix: String, @Assisted details: String) :
TransitionProgressListener {
- private val traceName = "$tracePrefix#$UNFOLD_TRANSITION_TRACE_NAME"
+ private val traceName = "$tracePrefix$details#$UNFOLD_TRANSITION_TRACE_NAME"
override fun onTransitionStarted() {
Trace.beginAsyncSection(traceName, /* cookie= */ 0)
@@ -43,6 +45,12 @@
override fun onTransitionProgress(progress: Float) {
Trace.setCounter(traceName, (progress * 100).toLong())
}
+
+ @AssistedFactory
+ interface Factory {
+ /** Creates an [ATraceLoggerTransitionProgressListener] with [details] in the track name. */
+ fun create(details: String): ATraceLoggerTransitionProgressListener
+ }
}
private const val UNFOLD_TRANSITION_TRACE_NAME = "FoldUnfoldTransitionInProgress"
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 3f46ab8..e013a3e 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -34,6 +34,7 @@
"framework-minus-apex.ravenwood",
"junit",
],
+ sdk_version: "core_current",
visibility: ["//frameworks/base"],
}
diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt
index c70c171..79bfa44 100644
--- a/ravenwood/framework-minus-apex-ravenwood-policies.txt
+++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt
@@ -1,5 +1,8 @@
# Ravenwood "policy" file for framework-minus-apex.
+# Keep all AIDL interfaces
+class :aidl stubclass
+
# Collections
class android.util.ArrayMap stubclass
class android.util.ArraySet stubclass
diff --git a/services/Android.bp b/services/Android.bp
index 02a7a78..5cb8ec6 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -140,6 +140,7 @@
":services.voiceinteraction-sources",
":services.wallpapereffectsgeneration-sources",
":services.wifi-sources",
+ ":framework-pm-common-shared-srcs",
],
visibility: ["//visibility:private"],
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 74538ac..6cac6a4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -668,7 +668,7 @@
final Context uiContext = displayContext.createWindowContext(
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
magnificationGestureHandler = new WindowMagnificationGestureHandler(uiContext,
- mAms.getWindowMagnificationMgr(), mAms.getTraceManager(),
+ mAms.getMagnificationConnectionManager(), mAms.getTraceManager(),
mAms.getMagnificationController(),
detectControlGestures,
detectTwoFingerTripleTap,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index b5e8c84..2eecb4d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -158,10 +158,10 @@
import com.android.server.AccessibilityManagerInternal;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.accessibility.magnification.MagnificationConnectionManager;
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationProcessor;
import com.android.server.accessibility.magnification.MagnificationScaleProvider;
-import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
@@ -3442,7 +3442,7 @@
&& (userState.getMagnificationCapabilitiesLocked()
!= Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)
|| userHasMagnificationServicesLocked(userState);
- getWindowMagnificationMgr().requestConnection(connect);
+ getMagnificationConnectionManager().requestConnection(connect);
}
/**
@@ -4122,17 +4122,17 @@
mSecurityPolicy.enforceCallingOrSelfPermission(
android.Manifest.permission.STATUS_BAR_SERVICE);
- getWindowMagnificationMgr().setConnection(connection);
+ getMagnificationConnectionManager().setConnection(connection);
}
/**
- * Getter of {@link WindowMagnificationManager}.
+ * Getter of {@link MagnificationConnectionManager}.
*
- * @return WindowMagnificationManager
+ * @return MagnificationManager
*/
- public WindowMagnificationManager getWindowMagnificationMgr() {
+ public MagnificationConnectionManager getMagnificationConnectionManager() {
synchronized (mLock) {
- return mMagnificationController.getWindowMagnificationMgr();
+ return mMagnificationController.getMagnificationConnectionManager();
}
}
@@ -4423,7 +4423,7 @@
pw.println();
}
pw.append("hasWindowMagnificationConnection=").append(
- String.valueOf(getWindowMagnificationMgr().isConnected()));
+ String.valueOf(getMagnificationConnectionManager().isConnected()));
pw.println();
mMagnificationProcessor.dump(pw, getValidDisplayList());
final int userCount = mUserStates.size();
@@ -5144,7 +5144,7 @@
for (int i = 0; i < displays.size(); i++) {
final int displayId = displays.get(i).getDisplayId();
- getWindowMagnificationMgr().removeMagnificationButton(displayId);
+ getMagnificationConnectionManager().removeMagnificationButton(displayId);
}
}
}
@@ -5580,6 +5580,8 @@
@Override
public void injectInputEventToInputFilter(InputEvent event) {
+ mSecurityPolicy.enforceCallingPermission(Manifest.permission.INJECT_EVENTS,
+ "injectInputEventToInputFilter");
synchronized (mLock) {
final long endMillis =
SystemClock.uptimeMillis() + WAIT_INPUT_FILTER_INSTALL_TIMEOUT_MS;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index d31b1ef..e3797c9 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -258,6 +258,11 @@
public void logMagnificationTripleTap(boolean enabled) {
AccessibilityStatsLogUtils.logMagnificationTripleTap(enabled);
}
+
+ @Override
+ public void logMagnificationTwoFingerTripleTap(boolean enabled) {
+ AccessibilityStatsLogUtils.logMagnificationTwoFingerTripleTap(enabled);
+ }
};
}
@@ -419,6 +424,7 @@
/** An interface that allows testing magnification log events. */
interface MagnificationLogger {
void logMagnificationTripleTap(boolean enabled);
+ void logMagnificationTwoFingerTripleTap(boolean enabled);
}
interface State {
@@ -987,12 +993,14 @@
mDisplayId, event.getX(), event.getY())) {
transitionToDelegatingStateAndClear();
+ } else if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 3, event)) {
+ // Placing multiple fingers before a single finger, because achieving a
+ // multi finger multi tap also means achieving a single finger triple tap
+ onTripleTap(event);
+
} else if (isMultiTapTriggered(3 /* taps */)) {
onTripleTap(/* up */ event);
- } else if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 3, event)) {
- onTripleTap(event);
-
} else if (
// Possible to be false on: 3tap&drag -> scale -> PTR_UP -> UP
isFingerDown()
@@ -1026,6 +1034,11 @@
mCompletedTapCount++;
mIsTwoFingerCountReached = false;
}
+
+ if (mDetectTwoFingerTripleTap && mCompletedTapCount > 2) {
+ final boolean enabled = !isActivated();
+ mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
+ }
return mDetectTwoFingerTripleTap && mCompletedTapCount == targetTapCount;
}
@@ -1037,6 +1050,29 @@
mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
}
+
+ void transitionToViewportDraggingStateAndClear(MotionEvent down) {
+
+ if (DEBUG_DETECTING) Slog.i(mLogTag, "onTripleTapAndHold()");
+ final boolean shortcutTriggered = mShortcutTriggered;
+
+ // Only log the 3tap and hold event
+ if (!shortcutTriggered) {
+ final boolean enabled = !isActivated();
+ if (mCompletedTapCount == 2) {
+ // Two finger triple tap and hold
+ mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
+ } else {
+ // Triple tap and hold also belongs to triple tap event
+ mMagnificationLogger.logMagnificationTripleTap(enabled);
+ }
+ }
+ clear();
+
+ mViewportDraggingState.prepareForZoomInTemporary(shortcutTriggered);
+ zoomInTemporary(down.getX(), down.getY(), shortcutTriggered);
+ transitionTo(mViewportDraggingState);
+ }
}
/**
@@ -1416,8 +1452,6 @@
// Only log the 3tap and hold event
if (!shortcutTriggered) {
- // TODO:(b/309534286): Add metrics for two-finger triple-tap and fix
- // the log two-finger bug before enabling the flag
// Triple tap and hold also belongs to triple tap event
final boolean enabled = !isActivated();
mMagnificationLogger.logMagnificationTripleTap(enabled);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
similarity index 94%
rename from services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
rename to services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
index 3ea805b..5a3c070 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
@@ -60,19 +60,19 @@
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
/**
- * A class to manipulate window magnification through {@link MagnificationConnectionWrapper}
+ * A class to manipulate magnification through {@link MagnificationConnectionWrapper}
* create by {@link #setConnection(IWindowMagnificationConnection)}. To set the connection with
* SysUI, call {@code StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)}.
* The applied magnification scale is constrained by
* {@link MagnificationScaleProvider#constrainScale(float)}
*/
-public class WindowMagnificationManager implements
+public class MagnificationConnectionManager implements
PanningScalingHandler.MagnificationDelegate,
WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks {
private static final boolean DBG = false;
- private static final String TAG = "WindowMagnificationMgr";
+ private static final String TAG = "MagnificationConnectionManager";
/**
* Indicate that the magnification window is at the magnification center.
@@ -208,7 +208,7 @@
private final AccessibilityTraceManager mTrace;
private final MagnificationScaleProvider mScaleProvider;
- public WindowMagnificationManager(Context context, Object lock, @NonNull Callback callback,
+ public MagnificationConnectionManager(Context context, Object lock, @NonNull Callback callback,
AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider) {
mContext = context;
mLock = lock;
@@ -1040,7 +1040,7 @@
private float mScale = MagnificationScaleProvider.MIN_SCALE;
private boolean mEnabled;
- private final WindowMagnificationManager mWindowMagnificationManager;
+ private final MagnificationConnectionManager mMagnificationConnectionManager;
// Records the bounds of window magnification.
private final Rect mBounds = new Rect();
// The magnified bounds on the screen.
@@ -1058,11 +1058,15 @@
"mTrackingTypingFocusSumTime");
private volatile long mTrackingTypingFocusSumTime = 0;
- WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
+ WindowMagnifier(int displayId,
+ MagnificationConnectionManager magnificationConnectionManager) {
mDisplayId = displayId;
- mWindowMagnificationManager = windowMagnificationManager;
+ mMagnificationConnectionManager = magnificationConnectionManager;
}
+ // TODO(b/312324808): Investigating whether
+ // mMagnificationConnectionManager#enableWindowMagnificationInternal requires a sync lock
+ @SuppressWarnings("GuardedBy")
boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY,
@Nullable MagnificationAnimationCallback animationCallback,
@WindowPosition int windowPosition, int id) {
@@ -1072,8 +1076,8 @@
}
final float normScale = MagnificationScaleProvider.constrainScale(scale);
setMagnificationFrameOffsetRatioByWindowPosition(windowPosition);
- if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
- centerX, centerY, mMagnificationFrameOffsetRatio.x,
+ if (mMagnificationConnectionManager.enableWindowMagnificationInternal(mDisplayId,
+ normScale, centerX, centerY, mMagnificationFrameOffsetRatio.x,
mMagnificationFrameOffsetRatio.y, animationCallback)) {
mScale = normScale;
mEnabled = true;
@@ -1096,12 +1100,15 @@
}
}
+ // TODO(b/312324808): Investigating whether
+ // mMagnificationConnectionManager#disableWindowMagnificationInternal requires a sync lock
+ @SuppressWarnings("GuardedBy")
boolean disableWindowMagnificationInternal(
@Nullable MagnificationAnimationCallback animationResultCallback) {
if (!mEnabled) {
return false;
}
- if (mWindowMagnificationManager.disableWindowMagnificationInternal(
+ if (mMagnificationConnectionManager.disableWindowMagnificationInternal(
mDisplayId, animationResultCallback)) {
mEnabled = false;
mIdOfLastServiceToControl = INVALID_SERVICE_ID;
@@ -1112,6 +1119,10 @@
return false;
}
+ // ErrorProne says the access of mMagnificationConnectionManager#setScaleInternal should
+ // be guarded by 'this.mMagnificationConnectionManager.mLock' which is the same one as
+ // 'mLock'. Therefore, we'll put @SuppressWarnings here.
+ @SuppressWarnings("GuardedBy")
@GuardedBy("mLock")
void setScale(float scale) {
if (!mEnabled) {
@@ -1119,7 +1130,8 @@
}
final float normScale = MagnificationScaleProvider.constrainScale(scale);
if (Float.compare(mScale, normScale) != 0
- && mWindowMagnificationManager.setScaleInternal(mDisplayId, scale)) {
+ && mMagnificationConnectionManager
+ .setScaleForWindowMagnificationInternal(mDisplayId, scale)) {
mScale = normScale;
}
}
@@ -1159,8 +1171,8 @@
}
void setTrackingTypingFocusEnabled(boolean trackingTypingFocusEnabled) {
- if (mWindowMagnificationManager.isWindowMagnifierEnabled(mDisplayId)
- && mWindowMagnificationManager.isImeVisible(mDisplayId)
+ if (mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId)
+ && mMagnificationConnectionManager.isImeVisible(mDisplayId)
&& trackingTypingFocusEnabled) {
startTrackingTypingFocusRecord();
}
@@ -1206,7 +1218,7 @@
Slog.d(TAG, "stop and log: session duration = " + duration
+ ", elapsed = " + elapsed);
}
- mWindowMagnificationManager.logTrackingTypingFocus(duration);
+ mMagnificationConnectionManager.logTrackingTypingFocus(duration);
mTrackingTypingFocusStartTime = 0;
mTrackingTypingFocusSumTime = 0;
}
@@ -1216,9 +1228,14 @@
return mEnabled;
}
+ // ErrorProne says the access of mMagnificationConnectionManager#moveWindowMagnifierInternal
+ // should be guarded by 'this.mMagnificationConnectionManager.mLock' which is the same one
+ // as 'mLock'. Therefore, we'll put @SuppressWarnings here.
+ @SuppressWarnings("GuardedBy")
@GuardedBy("mLock")
void move(float offsetX, float offsetY) {
- mWindowMagnificationManager.moveWindowMagnifierInternal(mDisplayId, offsetX, offsetY);
+ mMagnificationConnectionManager.moveWindowMagnifierInternal(
+ mDisplayId, offsetX, offsetY);
}
@GuardedBy("mLock")
@@ -1270,8 +1287,10 @@
animationCallback);
}
- private boolean setScaleInternal(int displayId, float scale) {
- return mConnectionWrapper != null && mConnectionWrapper.setScale(displayId, scale);
+ @GuardedBy("mLock")
+ private boolean setScaleForWindowMagnificationInternal(int displayId, float scale) {
+ return mConnectionWrapper != null
+ && mConnectionWrapper.setScaleForWindowMagnification(displayId, scale);
}
@GuardedBy("mLock")
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
index f0c44d6..20538f1 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java
@@ -82,16 +82,16 @@
return true;
}
- boolean setScale(int displayId, float scale) {
+ boolean setScaleForWindowMagnification(int displayId, float scale) {
if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
mTrace.logTrace(TAG + ".setScale", FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
"displayId=" + displayId + ";scale=" + scale);
}
try {
- mConnection.setScale(displayId, scale);
+ mConnection.setScaleForWindowMagnification(displayId, scale);
} catch (RemoteException e) {
if (DBG) {
- Slog.e(TAG, "Error calling setScale()", e);
+ Slog.e(TAG, "Error calling setScaleForWindowMagnification()", e);
}
return false;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index effd873..52e123a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -77,7 +77,7 @@
* <b>Note</b> Updates magnification switch UI when magnification mode transition
* is done and before invoking {@link TransitionCallBack#onResult}.
*/
-public class MagnificationController implements WindowMagnificationManager.Callback,
+public class MagnificationController implements MagnificationConnectionManager.Callback,
MagnificationGestureHandler.Callback,
FullScreenMagnificationController.MagnificationInfoChangedCallback,
WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks {
@@ -96,7 +96,7 @@
private final AlwaysOnMagnificationFeatureFlag mAlwaysOnMagnificationFeatureFlag;
private final MagnificationScaleProvider mScaleProvider;
private FullScreenMagnificationController mFullScreenMagnificationController;
- private WindowMagnificationManager mWindowMagnificationMgr;
+ private MagnificationConnectionManager mMagnificationConnectionManager;
private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
/** Whether the platform supports window magnification feature. */
private final boolean mSupportWindowMagnification;
@@ -164,11 +164,11 @@
@VisibleForTesting
public MagnificationController(AccessibilityManagerService ams, Object lock,
Context context, FullScreenMagnificationController fullScreenMagnificationController,
- WindowMagnificationManager windowMagnificationManager,
+ MagnificationConnectionManager magnificationConnectionManager,
MagnificationScaleProvider scaleProvider, Executor backgroundExecutor) {
this(ams, lock, context, scaleProvider, backgroundExecutor);
mFullScreenMagnificationController = fullScreenMagnificationController;
- mWindowMagnificationMgr = windowMagnificationManager;
+ mMagnificationConnectionManager = magnificationConnectionManager;
}
@Override
@@ -179,10 +179,10 @@
if (updatePersistence) {
getFullScreenMagnificationController().persistScale(displayId);
}
- } else if (getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId)) {
- getWindowMagnificationMgr().setScale(displayId, scale);
+ } else if (getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId)) {
+ getMagnificationConnectionManager().setScale(displayId, scale);
if (updatePersistence) {
- getWindowMagnificationMgr().persistScale(displayId);
+ getMagnificationConnectionManager().persistScale(displayId);
}
}
}
@@ -222,15 +222,15 @@
}
if (showModeSwitchButton) {
- getWindowMagnificationMgr().showMagnificationButton(displayId, mode);
+ getMagnificationConnectionManager().showMagnificationButton(displayId, mode);
} else {
- getWindowMagnificationMgr().removeMagnificationButton(displayId);
+ getMagnificationConnectionManager().removeMagnificationButton(displayId);
}
if (!enableSettingsPanel) {
// Whether the settings panel needs to be shown is controlled in system UI.
// Here, we only guarantee that the settings panel is closed when it is not needed.
- getWindowMagnificationMgr().removeMagnificationSettingsPanel(displayId);
+ getMagnificationConnectionManager().removeMagnificationSettingsPanel(displayId);
}
}
@@ -284,7 +284,8 @@
final FullScreenMagnificationController screenMagnificationController =
getFullScreenMagnificationController();
- final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
+ final MagnificationConnectionManager magnificationConnectionManager =
+ getMagnificationConnectionManager();
final float scale = getTargetModeScaleFromCurrentMagnification(displayId, targetMode);
final DisableMagnificationCallback animationEndCallback =
new DisableMagnificationCallback(transitionCallBack, displayId, targetMode,
@@ -295,7 +296,7 @@
if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
screenMagnificationController.reset(displayId, animationEndCallback);
} else {
- windowMagnificationMgr.disableWindowMagnification(displayId, false,
+ magnificationConnectionManager.disableWindowMagnification(displayId, false,
animationEndCallback);
}
}
@@ -340,7 +341,8 @@
}
final FullScreenMagnificationController screenMagnificationController =
getFullScreenMagnificationController();
- final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
+ final MagnificationConnectionManager magnificationConnectionManager =
+ getMagnificationConnectionManager();
final float targetScale = Float.isNaN(config.getScale())
? getTargetModeScaleFromCurrentMagnification(displayId, targetMode)
: config.getScale();
@@ -353,14 +355,15 @@
if (targetMode == MAGNIFICATION_MODE_WINDOW) {
screenMagnificationController.reset(displayId, false);
if (targetActivated) {
- windowMagnificationMgr.enableWindowMagnification(displayId,
+ magnificationConnectionManager.enableWindowMagnification(displayId,
targetScale, magnificationCenter.x, magnificationCenter.y,
magnificationAnimationCallback, id);
} else {
- windowMagnificationMgr.disableWindowMagnification(displayId, false);
+ magnificationConnectionManager.disableWindowMagnification(displayId, false);
}
} else if (targetMode == MAGNIFICATION_MODE_FULLSCREEN) {
- windowMagnificationMgr.disableWindowMagnification(displayId, false, null);
+ magnificationConnectionManager.disableWindowMagnification(
+ displayId, false, null);
if (targetActivated) {
if (!screenMagnificationController.isRegistered(displayId)) {
screenMagnificationController.register(displayId);
@@ -409,7 +412,7 @@
if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
return getFullScreenMagnificationController().getScale(displayId);
} else {
- return getWindowMagnificationMgr().getScale(displayId);
+ return getMagnificationConnectionManager().getScale(displayId);
}
}
@@ -441,7 +444,8 @@
mAccessibilityCallbacksDelegateArray.put(displayId,
getFullScreenMagnificationController());
} else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
- mAccessibilityCallbacksDelegateArray.put(displayId, getWindowMagnificationMgr());
+ mAccessibilityCallbacksDelegateArray.put(
+ displayId, getMagnificationConnectionManager());
} else {
mAccessibilityCallbacksDelegateArray.delete(displayId);
}
@@ -462,13 +466,13 @@
@Override
public void onRequestMagnificationSpec(int displayId, int serviceId) {
- final WindowMagnificationManager windowMagnificationManager;
+ final MagnificationConnectionManager magnificationConnectionManager;
synchronized (mLock) {
updateMagnificationUIControls(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
- windowMagnificationManager = mWindowMagnificationMgr;
+ magnificationConnectionManager = mMagnificationConnectionManager;
}
- if (windowMagnificationManager != null) {
- mWindowMagnificationMgr.disableWindowMagnification(displayId, false);
+ if (magnificationConnectionManager != null) {
+ mMagnificationConnectionManager.disableWindowMagnification(displayId, false);
}
}
@@ -491,7 +495,7 @@
setCurrentMagnificationModeAndSwitchDelegate(displayId,
ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
duration = SystemClock.uptimeMillis() - mWindowModeEnabledTimeArray.get(displayId);
- scale = mWindowMagnificationMgr.getLastActivatedScale(displayId);
+ scale = mMagnificationConnectionManager.getLastActivatedScale(displayId);
}
logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, duration, scale);
}
@@ -507,13 +511,14 @@
public void onSourceBoundsChanged(int displayId, Rect bounds) {
if (shouldNotifyMagnificationChange(displayId, MAGNIFICATION_MODE_WINDOW)) {
// notify sysui the magnification scale changed on window magnifier
- mWindowMagnificationMgr.onUserMagnificationScaleChanged(
- mUserId, displayId, getWindowMagnificationMgr().getScale(displayId));
+ mMagnificationConnectionManager.onUserMagnificationScaleChanged(
+ mUserId, displayId, getMagnificationConnectionManager().getScale(displayId));
final MagnificationConfig config = new MagnificationConfig.Builder()
.setMode(MAGNIFICATION_MODE_WINDOW)
- .setActivated(getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId))
- .setScale(getWindowMagnificationMgr().getScale(displayId))
+ .setActivated(
+ getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId))
+ .setScale(getMagnificationConnectionManager().getScale(displayId))
.setCenterX(bounds.exactCenterX())
.setCenterY(bounds.exactCenterY()).build();
mAms.notifyMagnificationChanged(displayId, new Region(bounds), config);
@@ -525,7 +530,7 @@
@NonNull MagnificationConfig config) {
if (shouldNotifyMagnificationChange(displayId, MAGNIFICATION_MODE_FULLSCREEN)) {
// notify sysui the magnification scale changed on fullscreen magnifier
- mWindowMagnificationMgr.onUserMagnificationScaleChanged(
+ mMagnificationConnectionManager.onUserMagnificationScaleChanged(
mUserId, displayId, config.getScale());
mAms.notifyMagnificationChanged(displayId, region, config);
@@ -548,8 +553,8 @@
synchronized (mLock) {
final boolean fullScreenActivated = mFullScreenMagnificationController != null
&& mFullScreenMagnificationController.isActivated(displayId);
- final boolean windowEnabled = mWindowMagnificationMgr != null
- && mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
+ final boolean windowEnabled = mMagnificationConnectionManager != null
+ && mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId);
final Integer transitionMode = mTransitionModes.get(displayId);
if (((changeMode == MAGNIFICATION_MODE_FULLSCREEN && fullScreenActivated)
|| (changeMode == MAGNIFICATION_MODE_WINDOW && windowEnabled))
@@ -608,10 +613,10 @@
}
private void disableWindowMagnificationIfNeeded(int displayId) {
- final WindowMagnificationManager windowMagnificationManager =
- getWindowMagnificationMgr();
+ final MagnificationConnectionManager magnificationConnectionManager =
+ getMagnificationConnectionManager();
if (isActivated(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)) {
- windowMagnificationManager.disableWindowMagnification(displayId, false);
+ magnificationConnectionManager.disableWindowMagnification(displayId, false);
}
}
@@ -620,7 +625,7 @@
synchronized (mLock) {
mIsImeVisibleArray.put(displayId, shown);
}
- getWindowMagnificationMgr().onImeWindowVisibilityChanged(displayId, shown);
+ getMagnificationConnectionManager().onImeWindowVisibilityChanged(displayId, shown);
logMagnificationModeWithImeOnIfNeeded(displayId);
}
@@ -661,7 +666,7 @@
/**
* Updates the active user ID of {@link FullScreenMagnificationController} and {@link
- * WindowMagnificationManager}.
+ * MagnificationConnectionManager}.
*
* @param userId the currently active user ID
*/
@@ -671,10 +676,10 @@
}
mUserId = userId;
final FullScreenMagnificationController fullMagnificationController;
- final WindowMagnificationManager windowMagnificationManager;
+ final MagnificationConnectionManager magnificationConnectionManager;
synchronized (mLock) {
fullMagnificationController = mFullScreenMagnificationController;
- windowMagnificationManager = mWindowMagnificationMgr;
+ magnificationConnectionManager = mMagnificationConnectionManager;
mAccessibilityCallbacksDelegateArray.clear();
mCurrentMagnificationModeArray.clear();
mLastMagnificationActivatedModeArray.clear();
@@ -685,8 +690,8 @@
if (fullMagnificationController != null) {
fullMagnificationController.resetAllIfNeeded(false);
}
- if (windowMagnificationManager != null) {
- windowMagnificationManager.disableAllWindowMagnifiers();
+ if (magnificationConnectionManager != null) {
+ magnificationConnectionManager.disableAllWindowMagnifiers();
}
}
@@ -700,8 +705,8 @@
if (mFullScreenMagnificationController != null) {
mFullScreenMagnificationController.onDisplayRemoved(displayId);
}
- if (mWindowMagnificationMgr != null) {
- mWindowMagnificationMgr.onDisplayRemoved(displayId);
+ if (mMagnificationConnectionManager != null) {
+ mMagnificationConnectionManager.onDisplayRemoved(displayId);
}
mAccessibilityCallbacksDelegateArray.delete(displayId);
mCurrentMagnificationModeArray.delete(displayId);
@@ -728,7 +733,7 @@
* @param enabled Enable the following typing focus feature
*/
public void setMagnificationFollowTypingEnabled(boolean enabled) {
- getWindowMagnificationMgr().setMagnificationFollowTypingEnabled(enabled);
+ getMagnificationConnectionManager().setMagnificationFollowTypingEnabled(enabled);
getFullScreenMagnificationController().setMagnificationFollowTypingEnabled(enabled);
}
@@ -805,29 +810,29 @@
}
/**
- * Getter of {@link WindowMagnificationManager}.
+ * Getter of {@link MagnificationConnectionManager}.
*
- * @return {@link WindowMagnificationManager}.
+ * @return {@link MagnificationConnectionManager}.
*/
- public WindowMagnificationManager getWindowMagnificationMgr() {
+ public MagnificationConnectionManager getMagnificationConnectionManager() {
synchronized (mLock) {
- if (mWindowMagnificationMgr == null) {
- mWindowMagnificationMgr = new WindowMagnificationManager(mContext,
+ if (mMagnificationConnectionManager == null) {
+ mMagnificationConnectionManager = new MagnificationConnectionManager(mContext,
mLock, this, mAms.getTraceManager(),
mScaleProvider);
}
- return mWindowMagnificationMgr;
+ return mMagnificationConnectionManager;
}
}
private @Nullable PointF getCurrentMagnificationCenterLocked(int displayId, int targetMode) {
if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
- if (mWindowMagnificationMgr == null
- || !mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId)) {
+ if (mMagnificationConnectionManager == null
+ || !mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId)) {
return null;
}
- mTempPoint.set(mWindowMagnificationMgr.getCenterX(displayId),
- mWindowMagnificationMgr.getCenterY(displayId));
+ mTempPoint.set(mMagnificationConnectionManager.getCenterX(displayId),
+ mMagnificationConnectionManager.getCenterY(displayId));
} else {
if (mFullScreenMagnificationController == null
|| !mFullScreenMagnificationController.isActivated(displayId)) {
@@ -858,10 +863,10 @@
}
} else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
synchronized (mLock) {
- if (mWindowMagnificationMgr == null) {
+ if (mMagnificationConnectionManager == null) {
return false;
}
- isActivated = mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
+ isActivated = mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId);
}
}
return isActivated;
@@ -980,7 +985,7 @@
mCurrentCenter.x, mCurrentCenter.y, mAnimate,
MAGNIFICATION_GESTURE_HANDLER_ID);
} else {
- getWindowMagnificationMgr().enableWindowMagnification(mDisplayId,
+ getMagnificationConnectionManager().enableWindowMagnification(mDisplayId,
mCurrentScale, mCurrentCenter.x,
mCurrentCenter.y, mAnimate ? STUB_ANIMATION_CALLBACK : null,
MAGNIFICATION_GESTURE_HANDLER_ID);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index 5cf2a63..ed8f1ab 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -84,13 +84,13 @@
.setCenterX(fullScreenMagnificationController.getCenterX(displayId))
.setCenterY(fullScreenMagnificationController.getCenterY(displayId));
} else if (mode == MAGNIFICATION_MODE_WINDOW) {
- final WindowMagnificationManager windowMagnificationManager =
- mController.getWindowMagnificationMgr();
+ final MagnificationConnectionManager magnificationConnectionManager =
+ mController.getMagnificationConnectionManager();
builder.setMode(mode)
.setActivated(mController.isActivated(displayId, MAGNIFICATION_MODE_WINDOW))
- .setScale(windowMagnificationManager.getScale(displayId))
- .setCenterX(windowMagnificationManager.getCenterX(displayId))
- .setCenterY(windowMagnificationManager.getCenterY(displayId));
+ .setScale(magnificationConnectionManager.getScale(displayId))
+ .setCenterX(magnificationConnectionManager.getCenterX(displayId))
+ .setCenterY(magnificationConnectionManager.getCenterY(displayId));
} else {
// For undefined mode, set enabled to false
builder.setActivated(false);
@@ -135,12 +135,12 @@
}
} else if (configMode == MAGNIFICATION_MODE_WINDOW) {
if (configActivated) {
- return mController.getWindowMagnificationMgr().enableWindowMagnification(displayId,
- config.getScale(), config.getCenterX(), config.getCenterY(),
+ return mController.getMagnificationConnectionManager().enableWindowMagnification(
+ displayId, config.getScale(), config.getCenterX(), config.getCenterY(),
animate ? STUB_ANIMATION_CALLBACK : null,
id);
} else {
- return mController.getWindowMagnificationMgr()
+ return mController.getMagnificationConnectionManager()
.disableWindowMagnification(displayId, false);
}
}
@@ -256,7 +256,7 @@
if (currentMode == MAGNIFICATION_MODE_FULLSCREEN) {
getFullscreenMagnificationRegion(displayId, outRegion, canControlMagnification);
} else if (currentMode == MAGNIFICATION_MODE_WINDOW) {
- mController.getWindowMagnificationMgr().getMagnificationSourceBounds(displayId,
+ mController.getMagnificationConnectionManager().getMagnificationSourceBounds(displayId,
outRegion);
}
}
@@ -297,8 +297,8 @@
if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
return mController.getFullScreenMagnificationController().reset(displayId, animate);
} else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().disableWindowMagnification(displayId,
- false, animate ? STUB_ANIMATION_CALLBACK : null);
+ return mController.getMagnificationConnectionManager().disableWindowMagnification(
+ displayId, false, animate ? STUB_ANIMATION_CALLBACK : null);
}
return false;
}
@@ -325,19 +325,20 @@
*/
public void resetAllIfNeeded(int connectionId) {
mController.getFullScreenMagnificationController().resetAllIfNeeded(connectionId);
- mController.getWindowMagnificationMgr().resetAllIfNeeded(connectionId);
+ mController.getMagnificationConnectionManager().resetAllIfNeeded(connectionId);
}
/**
* {@link FullScreenMagnificationController#isActivated(int)}
- * {@link WindowMagnificationManager#isWindowMagnifierEnabled(int)}
+ * {@link MagnificationConnectionManager#isWindowMagnifierEnabled(int)}
*/
public boolean isMagnifying(int displayId) {
int mode = getControllingMode(displayId);
if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
return mController.getFullScreenMagnificationController().isActivated(displayId);
} else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId);
+ return mController.getMagnificationConnectionManager().isWindowMagnifierEnabled(
+ displayId);
}
return false;
}
@@ -416,22 +417,23 @@
pw.append(" SupportWindowMagnification="
+ mController.supportWindowMagnification()).println();
pw.append(" WindowMagnificationConnectionState="
- + mController.getWindowMagnificationMgr().getConnectionState()).println();
+ + mController.getMagnificationConnectionManager().getConnectionState()).println();
}
private int getIdOfLastServiceToMagnify(int mode, int displayId) {
return (mode == MAGNIFICATION_MODE_FULLSCREEN)
? mController.getFullScreenMagnificationController()
.getIdOfLastServiceToMagnify(displayId)
- : mController.getWindowMagnificationMgr().getIdOfLastServiceToMagnify(
+ : mController.getMagnificationConnectionManager().getIdOfLastServiceToMagnify(
displayId);
}
private void dumpTrackingTypingFocusEnabledState(final PrintWriter pw, int displayId,
int mode) {
if (mode == MAGNIFICATION_MODE_WINDOW) {
- pw.append(" TrackingTypingFocusEnabled=" + mController
- .getWindowMagnificationMgr().isTrackingTypingFocusEnabled(displayId))
+ pw.append(" TrackingTypingFocusEnabled="
+ + mController.getMagnificationConnectionManager()
+ .isTrackingTypingFocusEnabled(displayId))
.println();
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
index 9455628..6b48d2b 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
@@ -45,6 +45,7 @@
private static final String TAG = "PanningScalingHandler";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ // TODO(b/312372035): Revisit the scope of usage of the interface
interface MagnificationDelegate {
boolean processScroll(int displayId, float distanceX, float distanceY);
void setScale(int displayId, float scale);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 36e75118..73c267a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -79,7 +79,7 @@
private static final float MIN_SCALE = 1.0f;
private static final float MAX_SCALE = MagnificationScaleProvider.MAX_SCALE;
- private final WindowMagnificationManager mWindowMagnificationMgr;
+ private final MagnificationConnectionManager mMagnificationConnectionManager;
@VisibleForTesting
final DelegatingState mDelegatingState;
@VisibleForTesting
@@ -101,7 +101,7 @@
private long mTripleTapAndHoldStartedTime = 0;
public WindowMagnificationGestureHandler(@UiContext Context context,
- WindowMagnificationManager windowMagnificationMgr,
+ MagnificationConnectionManager magnificationConnectionManager,
AccessibilityTraceManager trace,
Callback callback,
boolean detectSingleFingerTripleTap,
@@ -115,7 +115,7 @@
"WindowMagnificationGestureHandler() , displayId = " + displayId + ")");
}
mContext = context;
- mWindowMagnificationMgr = windowMagnificationMgr;
+ mMagnificationConnectionManager = magnificationConnectionManager;
mMotionEventDispatcherDelegate = new MotionEventDispatcherDelegate(context,
(event, rawEvent, policyFlags) -> dispatchTransformedEvent(event, rawEvent,
policyFlags));
@@ -128,18 +128,18 @@
@Override
public boolean processScroll(int displayId, float distanceX,
float distanceY) {
- return mWindowMagnificationMgr.processScroll(displayId, distanceX,
- distanceY);
+ return mMagnificationConnectionManager.processScroll(
+ displayId, distanceX, distanceY);
}
@Override
public void setScale(int displayId, float scale) {
- mWindowMagnificationMgr.setScale(displayId, scale);
+ mMagnificationConnectionManager.setScale(displayId, scale);
}
@Override
public float getScale(int displayId) {
- return mWindowMagnificationMgr.getScale(displayId);
+ return mMagnificationConnectionManager.getScale(displayId);
}
}));
@@ -167,7 +167,7 @@
Slog.i(mLogTag, "onDestroy(); delayed = "
+ mDetectingState.toString());
}
- mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true);
+ mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, true);
resetToDetectState();
}
@@ -176,7 +176,7 @@
final Point screenSize = mTempPoint;
getScreenSize(mTempPoint);
toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f,
- WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
}
private void getScreenSize(Point outSize) {
@@ -190,28 +190,29 @@
}
private void enableWindowMagnifier(float centerX, float centerY,
- @WindowMagnificationManager.WindowPosition int windowPosition) {
+ @MagnificationConnectionManager.WindowPosition int windowPosition) {
if (DEBUG_ALL) {
Slog.i(mLogTag, "enableWindowMagnifier :"
+ centerX + ", " + centerY + ", " + windowPosition);
}
final float scale = MathUtils.constrain(
- mWindowMagnificationMgr.getPersistedScale(mDisplayId), MIN_SCALE, MAX_SCALE);
- mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY,
- windowPosition);
+ mMagnificationConnectionManager.getPersistedScale(mDisplayId),
+ MIN_SCALE, MAX_SCALE);
+ mMagnificationConnectionManager.enableWindowMagnification(
+ mDisplayId, scale, centerX, centerY, windowPosition);
}
private void disableWindowMagnifier() {
if (DEBUG_ALL) {
Slog.i(mLogTag, "disableWindowMagnifier()");
}
- mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, false);
+ mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, false);
}
private void toggleMagnification(float centerX, float centerY,
- @WindowMagnificationManager.WindowPosition int windowPosition) {
- if (mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)) {
+ @MagnificationConnectionManager.WindowPosition int windowPosition) {
+ if (mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId)) {
disableWindowMagnifier();
} else {
enableWindowMagnifier(centerX, centerY, windowPosition);
@@ -223,7 +224,7 @@
Slog.i(mLogTag, "onTripleTap()");
}
toggleMagnification(up.getX(), up.getY(),
- WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
}
@VisibleForTesting
@@ -232,9 +233,9 @@
Slog.i(mLogTag, "onTripleTapAndHold()");
}
mViewportDraggingState.mEnabledBeforeDrag =
- mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId);
+ mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId);
enableWindowMagnifier(up.getX(), up.getY(),
- WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT);
+ MagnificationConnectionManager.WINDOW_POSITION_AT_TOP_LEFT);
mTripleTapAndHoldStartedTime = SystemClock.uptimeMillis();
transitionTo(mViewportDraggingState);
}
@@ -242,7 +243,7 @@
@VisibleForTesting
void releaseTripleTapAndHold() {
if (!mViewportDraggingState.mEnabledBeforeDrag) {
- mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true);
+ mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, true);
}
transitionTo(mDetectingState);
if (mTripleTapAndHoldStartedTime != 0) {
@@ -331,7 +332,7 @@
@Override
public void onExit() {
mPanningScalingHandler.setEnabled(false);
- mWindowMagnificationMgr.persistScale(mDisplayId);
+ mMagnificationConnectionManager.persistScale(mDisplayId);
clear();
}
@@ -404,7 +405,7 @@
if (!Float.isNaN(mLastX) && !Float.isNaN(mLastY)) {
float offsetX = event.getX() - mLastX;
float offsetY = event.getY() - mLastY;
- mWindowMagnificationMgr.moveWindowMagnification(mDisplayId, offsetX,
+ mMagnificationConnectionManager.moveWindowMagnification(mDisplayId, offsetX,
offsetY);
}
mLastX = event.getX();
@@ -522,7 +523,7 @@
@Override
public boolean shouldStopDetection(MotionEvent motionEvent) {
- return !mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)
+ return !mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId)
&& !mDetectSingleFingerTripleTap
&& !(mDetectTwoFingerTripleTap
&& Flags.enableMagnificationMultipleFingerMultipleTapGesture());
@@ -540,7 +541,8 @@
"onGestureDetected : delayedEventQueue = " + delayedEventQueue);
}
if (gestureId == MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE
- && mWindowMagnificationMgr.pointersInWindow(mDisplayId, motionEvent) > 0) {
+ && mMagnificationConnectionManager
+ .pointersInWindow(mDisplayId, motionEvent) > 0) {
transitionTo(mObservePanningScalingState);
} else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP) {
onTripleTap(motionEvent);
@@ -584,7 +586,7 @@
+ ", mMagnifiedInteractionState=" + mObservePanningScalingState
+ ", mCurrentState=" + State.nameOf(mCurrentState)
+ ", mPreviousState=" + State.nameOf(mPreviousState)
- + ", mWindowMagnificationMgr=" + mWindowMagnificationMgr
+ + ", mMagnificationConnectionManager=" + mMagnificationConnectionManager
+ ", mDisplayId=" + mDisplayId
+ '}';
}
diff --git a/services/companion/java/com/android/server/companion/virtual/OWNERS b/services/companion/java/com/android/server/companion/virtual/OWNERS
index 5295ec8..4fe0592 100644
--- a/services/companion/java/com/android/server/companion/virtual/OWNERS
+++ b/services/companion/java/com/android/server/companion/virtual/OWNERS
@@ -2,7 +2,7 @@
set noparent
-ogunwale@google.com
-michaelwr@google.com
+marvinramin@google.com
vladokom@google.com
-marvinramin@google.com
\ No newline at end of file
+ogunwale@google.com
+michaelwr@google.com
\ No newline at end of file
diff --git a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
index 3583a78..a159a5e 100644
--- a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
+++ b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
@@ -55,5 +55,15 @@
}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsVirtualDevicesCameraTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
]
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index e6bfeb7..45d7314 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -39,7 +39,6 @@
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
-import android.app.compat.CompatChanges;
import android.companion.AssociationInfo;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDeviceActivityListener;
@@ -55,8 +54,6 @@
import android.companion.virtual.flags.Flags;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorEvent;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
@@ -81,7 +78,6 @@
import android.hardware.input.VirtualTouchEvent;
import android.hardware.input.VirtualTouchscreenConfig;
import android.os.Binder;
-import android.os.Build;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.Looper;
@@ -122,22 +118,6 @@
private static final String TAG = "VirtualDeviceImpl";
- /**
- * Virtual displays created by a {@code VirtualDeviceManager.VirtualDevice} are more consistent
- * with virtual displays created via {@link android.hardware.display.DisplayManager} and allow
- * for the creation of private, auto-mirror, and fixed orientation displays since
- * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}.
- *
- * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC
- * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
- * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
- * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final long MAKE_VIRTUAL_DISPLAY_FLAGS_CONSISTENT_WITH_DISPLAY_MANAGER =
- 294837146L;
-
private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS =
DisplayManager.VIRTUAL_DISPLAY_FLAG_TOUCH_FEEDBACK_DISABLED
| DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL
@@ -365,8 +345,7 @@
}
int flags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
- if (!CompatChanges.isChangeEnabled(
- MAKE_VIRTUAL_DISPLAY_FLAGS_CONSISTENT_WITH_DISPLAY_MANAGER, mOwnerUid)) {
+ if (!Flags.consistentDisplayFlags()) {
flags |= DEFAULT_VIRTUAL_DISPLAY_FLAGS_PRE_VIC;
}
if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) {
@@ -960,7 +939,7 @@
if (mVirtualCameraController == null) {
throw new UnsupportedOperationException("Virtual camera controller is not available");
}
- mVirtualCameraController.registerCamera(Objects.requireNonNull(cameraConfig));
+ mVirtualCameraController.registerCamera(cameraConfig);
}
@Override // Binder call
@@ -972,7 +951,19 @@
if (mVirtualCameraController == null) {
throw new UnsupportedOperationException("Virtual camera controller is not available");
}
- mVirtualCameraController.unregisterCamera(Objects.requireNonNull(cameraConfig));
+ mVirtualCameraController.unregisterCamera(cameraConfig);
+ }
+
+ @Override // Binder call
+ @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public int getVirtualCameraId(@NonNull VirtualCameraConfig cameraConfig)
+ throws RemoteException {
+ super.getVirtualCameraId_enforcePermission();
+ Objects.requireNonNull(cameraConfig);
+ if (mVirtualCameraController == null) {
+ throw new UnsupportedOperationException("Virtual camera controller is not available");
+ }
+ return mVirtualCameraController.getCameraId(cameraConfig);
}
@Override
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index e51ef29..9b78ed4 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -187,9 +187,7 @@
CompanionDeviceManager cdm =
getContext().getSystemService(CompanionDeviceManager.class);
if (cdm != null) {
- synchronized (mVirtualDeviceManagerLock) {
- mActiveAssociations = cdm.getAllAssociations(UserHandle.USER_ALL);
- }
+ onCdmAssociationsChanged(cdm.getAllAssociations(UserHandle.USER_ALL));
cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(),
this::onCdmAssociationsChanged, UserHandle.USER_ALL);
} else {
@@ -345,19 +343,21 @@
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
void onCdmAssociationsChanged(List<AssociationInfo> associations) {
+ List<AssociationInfo> vdmAssociations = new ArrayList<>();
+ Set<Integer> activeAssociationIds = new HashSet<>();
+ for (int i = 0; i < associations.size(); ++i) {
+ AssociationInfo association = associations.get(i);
+ if (VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES.contains(association.getDeviceProfile())) {
+ vdmAssociations.add(association);
+ activeAssociationIds.add(association.getId());
+ }
+ }
Set<VirtualDeviceImpl> virtualDevicesToRemove = new HashSet<>();
Set<String> removedPersistentDeviceIds = new HashSet<>();
synchronized (mVirtualDeviceManagerLock) {
- Set<Integer> activeAssociationIds = new HashSet<>(associations.size());
- for (int i = 0; i < associations.size(); ++i) {
- activeAssociationIds.add(associations.get(i).getId());
- }
-
for (int i = 0; i < mActiveAssociations.size(); ++i) {
AssociationInfo associationInfo = mActiveAssociations.get(i);
- if (!activeAssociationIds.contains(associationInfo.getId())
- && VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES.contains(
- associationInfo.getDeviceProfile())) {
+ if (!activeAssociationIds.contains(associationInfo.getId())) {
removedPersistentDeviceIds.add(
VirtualDeviceImpl.createPersistentDeviceId(associationInfo.getId()));
}
@@ -370,7 +370,7 @@
}
}
- mActiveAssociations = associations;
+ mActiveAssociations = vdmAssociations;
}
for (VirtualDeviceImpl virtualDevice : virtualDevicesToRemove) {
@@ -460,11 +460,11 @@
synchronized (mVirtualDeviceManagerLock) {
if (!Flags.persistentDeviceIdApi() && mVirtualDevices.size() == 0) {
- final long callindId = Binder.clearCallingIdentity();
+ final long callingId = Binder.clearCallingIdentity();
try {
registerCdmAssociationListener();
} finally {
- Binder.restoreCallingIdentity(callindId);
+ Binder.restoreCallingIdentity(callingId);
}
}
mVirtualDevices.put(deviceId, virtualDevice);
@@ -498,13 +498,16 @@
synchronized (mVirtualDeviceManagerLock) {
virtualDeviceImpl = mVirtualDevices.get(virtualDevice.getDeviceId());
if (virtualDeviceImpl == null) {
- throw new SecurityException("Invalid VirtualDevice");
+ throw new SecurityException(
+ "Invalid VirtualDevice (deviceId = " + virtualDevice.getDeviceId()
+ + ")");
}
}
if (virtualDeviceImpl.getOwnerUid() != callingUid) {
throw new SecurityException(
"uid " + callingUid
- + " is not the owner of the supplied VirtualDevice");
+ + " is not the owner of the supplied VirtualDevice (deviceId = "
+ + virtualDevice.getDeviceId() + ")");
}
return virtualDeviceImpl.createVirtualDisplay(
@@ -851,6 +854,11 @@
}
@Override
+ public int getDeviceIdForDisplayId(int displayId) {
+ return mImpl.getDeviceIdForDisplayId(displayId);
+ }
+
+ @Override
public @Nullable String getPersistentIdForDevice(int deviceId) {
if (deviceId == Context.DEVICE_ID_DEFAULT) {
return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
@@ -869,12 +877,8 @@
synchronized (mVirtualDeviceManagerLock) {
for (int i = 0; i < mActiveAssociations.size(); ++i) {
AssociationInfo associationInfo = mActiveAssociations.get(i);
- if (VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES.contains(
- associationInfo.getDeviceProfile())) {
- persistentIds.add(
- VirtualDeviceImpl.createPersistentDeviceId(
- associationInfo.getId()));
- }
+ persistentIds.add(
+ VirtualDeviceImpl.createPersistentDeviceId(associationInfo.getId()));
}
}
return persistentIds;
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
index 06be3f3..d089b05 100644
--- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
@@ -27,14 +27,14 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.util.ArraySet;
+import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
-import java.util.Set;
+import java.util.Map;
/**
* Manages the registration and removal of virtual camera from the server side.
@@ -47,10 +47,13 @@
private static final String VIRTUAL_CAMERA_SERVICE_NAME = "virtual_camera";
private static final String TAG = "VirtualCameraController";
+ private final Object mServiceLock = new Object();
+
+ @GuardedBy("mServiceLock")
@Nullable private IVirtualCameraService mVirtualCameraService;
@GuardedBy("mCameras")
- private final Set<VirtualCameraConfig> mCameras = new ArraySet<>();
+ private final Map<IBinder, CameraDescriptor> mCameras = new ArrayMap<>();
public VirtualCameraController() {
connectVirtualCameraService();
@@ -67,19 +70,16 @@
* @param cameraConfig The {@link VirtualCameraConfig} sent by the client.
*/
public void registerCamera(@NonNull VirtualCameraConfig cameraConfig) {
- // Try to connect to service if not connected already.
- if (mVirtualCameraService == null) {
- connectVirtualCameraService();
- }
- // Throw exception if we are unable to connect to service.
- if (mVirtualCameraService == null) {
- throw new IllegalStateException("Virtual camera service is not connected.");
- }
+ connectVirtualCameraServiceIfNeeded();
try {
if (registerCameraWithService(cameraConfig)) {
+ CameraDescriptor cameraDescriptor =
+ new CameraDescriptor(cameraConfig);
+ IBinder binder = cameraConfig.getCallback().asBinder();
+ binder.linkToDeath(cameraDescriptor, 0 /* flags */);
synchronized (mCameras) {
- mCameras.add(cameraConfig);
+ mCameras.put(binder, cameraDescriptor);
}
} else {
// TODO(b/310857519): Revisit this to find a better way of indicating failure.
@@ -96,24 +96,44 @@
* @param cameraConfig The {@link VirtualCameraConfig} sent by the client.
*/
public void unregisterCamera(@NonNull VirtualCameraConfig cameraConfig) {
- try {
- if (mVirtualCameraService == null) {
- Slog.w(TAG, "Virtual camera service is not connected.");
+ synchronized (mCameras) {
+ IBinder binder = cameraConfig.getCallback().asBinder();
+ if (!mCameras.containsKey(binder)) {
+ Slog.w(TAG, "Virtual camera was not registered.");
} else {
- mVirtualCameraService.unregisterCamera(cameraConfig.getCallback().asBinder());
+ connectVirtualCameraServiceIfNeeded();
+
+ try {
+ synchronized (mServiceLock) {
+ mVirtualCameraService.unregisterCamera(binder);
+ }
+ mCameras.remove(binder);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
- synchronized (mCameras) {
- mCameras.remove(cameraConfig);
+ }
+ }
+
+ /** Return the id of the virtual camera with the given config. */
+ public int getCameraId(@NonNull VirtualCameraConfig cameraConfig) {
+ connectVirtualCameraServiceIfNeeded();
+
+ try {
+ synchronized (mServiceLock) {
+ return mVirtualCameraService.getCameraId(cameraConfig.getCallback().asBinder());
}
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@Override
public void binderDied() {
Slog.d(TAG, "Virtual camera service died.");
- mVirtualCameraService = null;
+ synchronized (mServiceLock) {
+ mVirtualCameraService = null;
+ }
synchronized (mCameras) {
mCameras.clear();
}
@@ -122,32 +142,48 @@
/** Release resources associated with this controller. */
public void close() {
synchronized (mCameras) {
- if (mVirtualCameraService == null) {
- Slog.w(TAG, "Virtual camera service is not connected.");
- } else {
- for (VirtualCameraConfig config : mCameras) {
- try {
- mVirtualCameraService.unregisterCamera(config.getCallback().asBinder());
- } catch (RemoteException e) {
- Slog.w(TAG, "close(): Camera failed to be removed on camera "
- + "service.", e);
+ if (!mCameras.isEmpty()) {
+ connectVirtualCameraServiceIfNeeded();
+
+ synchronized (mServiceLock) {
+ for (IBinder binder : mCameras.keySet()) {
+ try {
+ mVirtualCameraService.unregisterCamera(binder);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "close(): Camera failed to be removed on camera "
+ + "service.", e);
+ }
}
}
+ mCameras.clear();
}
- mCameras.clear();
}
- mVirtualCameraService = null;
+ synchronized (mServiceLock) {
+ mVirtualCameraService = null;
+ }
}
/** Dumps information about this {@link VirtualCameraController} for debugging purposes. */
public void dump(PrintWriter fout, String indent) {
fout.println(indent + "VirtualCameraController:");
indent += indent;
- fout.printf("%sService:%s\n", indent, mVirtualCameraService);
synchronized (mCameras) {
fout.printf("%sRegistered cameras:%d%n\n", indent, mCameras.size());
- for (VirtualCameraConfig config : mCameras) {
- fout.printf("%s token: %s\n", indent, config);
+ for (CameraDescriptor descriptor : mCameras.values()) {
+ fout.printf("%s token: %s\n", indent, descriptor.mConfig);
+ }
+ }
+ }
+
+ private void connectVirtualCameraServiceIfNeeded() {
+ synchronized (mServiceLock) {
+ // Try to connect to service if not connected already.
+ if (mVirtualCameraService == null) {
+ connectVirtualCameraService();
+ }
+ // Throw exception if we are unable to connect to service.
+ if (mVirtualCameraService == null) {
+ throw new IllegalStateException("Virtual camera service is not connected.");
}
}
}
@@ -173,7 +209,24 @@
private boolean registerCameraWithService(VirtualCameraConfig config) throws RemoteException {
VirtualCameraConfiguration serviceConfiguration = getServiceCameraConfiguration(config);
- return mVirtualCameraService.registerCamera(config.getCallback().asBinder(),
- serviceConfiguration);
+ synchronized (mServiceLock) {
+ return mVirtualCameraService.registerCamera(config.getCallback().asBinder(),
+ serviceConfiguration);
+ }
+ }
+
+ private final class CameraDescriptor implements IBinder.DeathRecipient {
+
+ private final VirtualCameraConfig mConfig;
+
+ CameraDescriptor(VirtualCameraConfig config) {
+ mConfig = config;
+ }
+
+ @Override
+ public void binderDied() {
+ Slog.d(TAG, "Virtual camera binder died");
+ unregisterCamera(mConfig);
+ }
}
}
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index fc56511..23c008e 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -27,6 +27,7 @@
import android.content.LocusId;
import android.content.res.Configuration;
import android.os.IBinder;
+import android.os.PersistableBundle;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -134,6 +135,18 @@
@Nullable LocusId locusId, @NonNull IBinder appToken);
/**
+ * Report a user interaction event to UsageStatsManager
+ *
+ * @param pkgName The package for which this user interaction event occurred.
+ * @param userId The user id to which component belongs to.
+ * @param extras The extra details about this user interaction event.
+ * {@link UsageEvents.Event#USER_INTERACTION}
+ * {@link UsageStatsManager#reportUserInteraction(String, int, PersistableBundle)}
+ */
+ public abstract void reportUserInteractionEvent(@NonNull String pkgName, @UserIdInt int userId,
+ @NonNull PersistableBundle extras);
+
+ /**
* Prepares the UsageStatsService for shutdown.
*/
public abstract void prepareShutdown();
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 77b6d583..eb3ec24 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -1466,8 +1466,11 @@
if (android.security.Flags.binaryTransparencySepolicyHash()) {
byte[] sepolicyHash = PackageUtils.computeSha256DigestForLargeFileAsBytes(
"/sys/fs/selinux/policy", PackageUtils.createLargeFileBuffer());
- String sepolicyHashEncoded = HexEncoding.encodeToString(sepolicyHash, false);
- Slog.d(TAG, "sepolicy hash: " + sepolicyHashEncoded);
+ String sepolicyHashEncoded = null;
+ if (sepolicyHash != null) {
+ sepolicyHashEncoded = HexEncoding.encodeToString(sepolicyHash, false);
+ Slog.d(TAG, "sepolicy hash: " + sepolicyHashEncoded);
+ }
FrameworkStatsLog.write(FrameworkStatsLog.BOOT_INTEGRITY_INFO_REPORTED,
sepolicyHashEncoded, mVbmetaDigest);
}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 987507f..c258370 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,5 +1,5 @@
-# BootReceiver
-per-file BootReceiver.java = gaillard@google.com
+# BootReceiver / Watchdog
+per-file BootReceiver.java,Watchdog.java = gaillard@google.com
# Connectivity / Networking
per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkManagementService.java,NsdService.java,VpnManagerService.java = file:/services/core/java/com/android/server/net/OWNERS
@@ -35,7 +35,7 @@
per-file MmsServiceBroker.java = file:/telephony/OWNERS
per-file NetIdManager.java = file:/services/core/java/com/android/server/net/OWNERS
per-file PackageWatchdog.java, RescueParty.java = file:/services/core/java/com/android/server/rollback/OWNERS
-per-file PinnerService.java = file:/apct-tests/perftests/OWNERS
+per-file PinnerService.java = file:/core/java/android/app/pinner/OWNERS
per-file RescueParty.java = shuc@google.com, ancr@google.com, harshitmahajan@google.com
per-file SystemClockTime.java = file:/services/core/java/com/android/server/timedetector/OWNERS
per-file SystemTimeZone.java = file:/services/core/java/com/android/server/timezonedetector/OWNERS
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 0e7b4aa..23a30f9 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -20,6 +20,9 @@
import static android.app.ActivityManager.UID_OBSERVER_GONE;
import static android.os.Process.SYSTEM_UID;
+import static com.android.server.flags.Flags.pinWebview;
+
+import android.annotation.EnforcePermission;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,6 +31,8 @@
import android.app.IActivityManager;
import android.app.SearchManager;
import android.app.UidObserver;
+import android.app.pinner.IPinnerService;
+import android.app.pinner.PinnedFileStat;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -48,6 +53,7 @@
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -83,6 +89,8 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -111,16 +119,15 @@
private static final int KEY_ASSISTANT = 2;
// Pin using pinlist.meta when pinning apps.
- private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean(
- "pinner.use_pinlist", true);
- // Pin the whole odex/vdex/etc file when pinning apps.
- private static boolean PROP_PIN_ODEX = SystemProperties.getBoolean(
- "pinner.whole_odex", true);
+ private static boolean PROP_PIN_PINLIST =
+ SystemProperties.getBoolean("pinner.use_pinlist", true);
private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app.
private static final int MAX_HOME_PIN_SIZE = 6 * (1 << 20); // 6MB max for home app.
private static final int MAX_ASSISTANT_PIN_SIZE = 60 * (1 << 20); // 60MB max for assistant app.
+ public static final String ANON_REGION_STAT_NAME = "[anon]";
+
@IntDef({KEY_CAMERA, KEY_HOME, KEY_ASSISTANT})
@Retention(RetentionPolicy.SOURCE)
public @interface AppKey {}
@@ -135,8 +142,7 @@
private SearchManager mSearchManager;
/** The list of the statically pinned files. */
- @GuardedBy("this")
- private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<>();
+ @GuardedBy("this") private final ArrayMap<String, PinnedFile> mPinnedFiles = new ArrayMap<>();
/** The list of the pinned apps. This is a map from {@link AppKey} to a pinned app. */
@GuardedBy("this")
@@ -175,6 +181,7 @@
private final boolean mConfiguredToPinCamera;
private final boolean mConfiguredToPinHome;
private final boolean mConfiguredToPinAssistant;
+ private final int mConfiguredWebviewPinBytes;
private BinderService mBinderService;
private PinnerHandler mPinnerHandler = null;
@@ -214,6 +221,11 @@
protected void publishBinderService(PinnerService service, Binder binderService) {
service.publishBinderService("pinner", binderService);
}
+
+ protected PinnedFile pinFileInternal(String fileToPin,
+ int maxBytesToPin, boolean attemptPinIntrospection) {
+ return PinnerService.pinFileInternal(fileToPin, maxBytesToPin, attemptPinIntrospection);
+ }
}
public PinnerService(Context context) {
@@ -233,6 +245,8 @@
com.android.internal.R.bool.config_pinnerHomeApp);
mConfiguredToPinAssistant = context.getResources().getBoolean(
com.android.internal.R.bool.config_pinnerAssistantApp);
+ mConfiguredWebviewPinBytes = context.getResources().getInteger(
+ com.android.internal.R.integer.config_pinnerWebviewPinBytes);
mPinKeys = createPinKeys();
mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());
@@ -322,7 +336,7 @@
public List<PinnedFileStats> dumpDataForStatsd() {
List<PinnedFileStats> pinnedFileStats = new ArrayList<>();
synchronized (PinnerService.this) {
- for (PinnedFile pinnedFile : mPinnedFiles) {
+ for (PinnedFile pinnedFile : mPinnedFiles.values()) {
pinnedFileStats.add(new PinnedFileStats(SYSTEM_UID, pinnedFile));
}
@@ -358,39 +372,17 @@
com.android.internal.R.array.config_defaultPinnerServiceFiles);
// Continue trying to pin each file even if we fail to pin some of them
for (String fileToPin : filesToPin) {
- PinnedFile pf = pinFile(fileToPin,
- Integer.MAX_VALUE,
- /*attemptPinIntrospection=*/false);
+ PinnedFile pf = mInjector.pinFileInternal(fileToPin, Integer.MAX_VALUE,
+ /*attemptPinIntrospection=*/false);
if (pf == null) {
Slog.e(TAG, "Failed to pin file = " + fileToPin);
continue;
}
synchronized (this) {
- mPinnedFiles.add(pf);
+ mPinnedFiles.put(pf.fileName, pf);
}
- if (fileToPin.endsWith(".jar") | fileToPin.endsWith(".apk")) {
- // Check whether the runtime has compilation artifacts to pin.
- String arch = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
- String[] files = null;
- try {
- files = DexFile.getDexFileOutputPaths(fileToPin, arch);
- } catch (IOException ioe) { }
- if (files == null) {
- continue;
- }
- for (String file : files) {
- PinnedFile df = pinFile(file,
- Integer.MAX_VALUE,
- /*attemptPinIntrospection=*/false);
- if (df == null) {
- Slog.i(TAG, "Failed to pin ART file = " + file);
- continue;
- }
- synchronized (this) {
- mPinnedFiles.add(df);
- }
- }
- }
+ pf.groupName = "system";
+ pinOptimizedDexDependencies(pf, Integer.MAX_VALUE, null);
}
refreshPinAnonConfig();
@@ -487,7 +479,7 @@
pinnedAppFiles = new ArrayList<>(app.mFiles);
}
for (PinnedFile pinnedFile : pinnedAppFiles) {
- pinnedFile.close();
+ unpinFile(pinnedFile.fileName);
}
}
@@ -495,6 +487,19 @@
return ResolverActivity.class.getName().equals(info.name);
}
+ public int getWebviewPinQuota() {
+ if (!pinWebview()) {
+ return 0;
+ }
+ int quota = mConfiguredWebviewPinBytes;
+ int overrideQuota = SystemProperties.getInt("pinner.pin_webview_size", -1);
+ if (overrideQuota != -1) {
+ // Quota was overridden
+ quota = overrideQuota;
+ }
+ return quota;
+ }
+
private ApplicationInfo getCameraInfo(int userHandle) {
Intent cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
ApplicationInfo info = getApplicationInfoForIntent(cameraIntent, userHandle,
@@ -728,7 +733,7 @@
case KEY_ASSISTANT:
return "Assistant";
default:
- return null;
+ return "";
}
}
@@ -868,11 +873,12 @@
continue;
}
- PinnedFile pf = pinFile(apk, apkPinSizeLimit, /*attemptPinIntrospection=*/true);
+ PinnedFile pf = mInjector.pinFileInternal(apk, apkPinSizeLimit, /*attemptPinIntrospection=*/true);
if (pf == null) {
Slog.e(TAG, "Failed to pin " + apk);
continue;
}
+ pf.groupName = getNameForKey(key);
if (DEBUG) {
Slog.i(TAG, "Pinned " + pf.fileName);
@@ -882,40 +888,118 @@
}
apkPinSizeLimit -= pf.bytesPinned;
+ if (apk.equals(appInfo.sourceDir)) {
+ pinOptimizedDexDependencies(pf, apkPinSizeLimit, appInfo);
+ }
}
+ }
- // determine the ABI from either ApplicationInfo or Build
- String abi = appInfo.primaryCpuAbi != null ? appInfo.primaryCpuAbi :
- Build.SUPPORTED_ABIS[0];
- String arch = VMRuntime.getInstructionSet(abi);
- // get the path to the odex or oat file
- String baseCodePath = appInfo.getBaseCodePath();
- String[] files = null;
- try {
- files = DexFile.getDexFileOutputPaths(baseCodePath, arch);
- } catch (IOException ioe) {}
- if (files == null) {
- return;
+ /**
+ * Pin file or apk to memory.
+ *
+ * Prefer to use this method instead of {@link #pinFileInternal(String, int, boolean)} as it
+ * takes care of accounting and if pinning an apk, it also pins any extra optimized art files
+ * that related to the file but not within itself.
+ *
+ * @param fileToPin File to pin
+ * @param maxBytesToPin maximum quota allowed for pinning
+ * @return total bytes that were pinned.
+ */
+ public int pinFile(String fileToPin, int maxBytesToPin, @Nullable ApplicationInfo appInfo,
+ @Nullable String groupName) {
+ PinnedFile existingPin;
+ synchronized(this) {
+ existingPin = mPinnedFiles.get(fileToPin);
}
-
- //not pinning the oat/odex is not a fatal error
- for (String file : files) {
- PinnedFile pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false);
- if (pf != null) {
- synchronized (this) {
- if (PROP_PIN_ODEX) {
- pinnedApp.mFiles.add(pf);
- }
- }
+ if (existingPin != null) {
+ if (existingPin.bytesPinned == maxBytesToPin) {
+ // Duplicate pin requesting same amount of bytes, lets just bail out.
+ return 0;
+ } else {
+ // User decided to pin a different amount of bytes than currently pinned
+ // so this is a valid pin request. Unpin the previous version before repining.
if (DEBUG) {
- if (PROP_PIN_ODEX) {
- Slog.i(TAG, "Pinned " + pf.fileName);
- } else {
- Slog.i(TAG, "Pinned [skip] " + pf.fileName);
- }
+ Slog.d(TAG, "Unpinning file prior to repin: " + fileToPin);
+ }
+ unpinFile(fileToPin);
+ }
+ }
+
+ boolean isApk = fileToPin.endsWith(".apk");
+ int bytesPinned = 0;
+ PinnedFile pf = mInjector.pinFileInternal(fileToPin, maxBytesToPin,
+ /*attemptPinIntrospection=*/isApk);
+ if (pf == null) {
+ Slog.e(TAG, "Failed to pin file = " + fileToPin);
+ return 0;
+ }
+ pf.groupName = groupName != null ? groupName : "";
+
+ maxBytesToPin -= bytesPinned;
+ bytesPinned += pf.bytesPinned;
+
+ synchronized (this) {
+ mPinnedFiles.put(pf.fileName, pf);
+ }
+ if (maxBytesToPin > 0) {
+ pinOptimizedDexDependencies(pf, maxBytesToPin, appInfo);
+ }
+ return bytesPinned;
+ }
+
+ /**
+ * Pin any dependency optimized files generated by ART.
+ * @param pinnedFile An already pinned file whose dependencies we want pinned.
+ * @param maxBytesToPin Maximum amount of bytes to pin.
+ * @param appInfo Used to determine the ABI in case the application has one custom set, when set
+ * to null it will use the default supported ABI by the device.
+ * @return total bytes pinned.
+ */
+ private int pinOptimizedDexDependencies(
+ PinnedFile pinnedFile, int maxBytesToPin, @Nullable ApplicationInfo appInfo) {
+ if (pinnedFile == null) {
+ return 0;
+ }
+
+ int bytesPinned = 0;
+ if (pinnedFile.fileName.endsWith(".jar") | pinnedFile.fileName.endsWith(".apk")) {
+ String abi = null;
+ if (appInfo != null) {
+ abi = appInfo.primaryCpuAbi;
+ }
+ if (abi == null) {
+ abi = Build.SUPPORTED_ABIS[0];
+ }
+ // Check whether the runtime has compilation artifacts to pin.
+ String arch = VMRuntime.getInstructionSet(abi);
+ String[] files = null;
+ try {
+ files = DexFile.getDexFileOutputPaths(pinnedFile.fileName, arch);
+ } catch (IOException ioe) {
+ }
+ if (files == null) {
+ return bytesPinned;
+ }
+ for (String file : files) {
+ // Unpin if it was already pinned prior to re-pinning.
+ unpinFile(file);
+
+ PinnedFile df = mInjector.pinFileInternal(file, Integer.MAX_VALUE,
+ /*attemptPinIntrospection=*/false);
+ if (df == null) {
+ Slog.i(TAG, "Failed to pin ART file = " + file);
+ return bytesPinned;
+ }
+ df.groupName = pinnedFile.groupName;
+ pinnedFile.pinnedDeps.add(df);
+ maxBytesToPin -= df.bytesPinned;
+ bytesPinned += df.bytesPinned;
+ synchronized (this) {
+ mPinnedFiles.put(df.fileName, df);
}
}
}
+ return bytesPinned;
}
/** mlock length bytes of fileToPin in memory
@@ -955,9 +1039,12 @@
* zip in order to extract the
* @return Pinned memory resource owner thing or null on error
*/
- private static PinnedFile pinFile(String fileToPin,
- int maxBytesToPin,
- boolean attemptPinIntrospection) {
+ private static PinnedFile pinFileInternal(
+ String fileToPin, int maxBytesToPin, boolean attemptPinIntrospection) {
+ if (DEBUG) {
+ Slog.d(TAG, "pin file: " + fileToPin + " use-pinlist: " + attemptPinIntrospection);
+ }
+ Trace.beginSection("pinFile:" + fileToPin);
ZipFile fileAsZip = null;
InputStream pinRangeStream = null;
try {
@@ -968,16 +1055,19 @@
if (fileAsZip != null) {
pinRangeStream = maybeOpenPinMetaInZip(fileAsZip, fileToPin);
}
-
- Slog.d(TAG, "pinRangeStream: " + pinRangeStream);
-
- PinRangeSource pinRangeSource = (pinRangeStream != null)
- ? new PinRangeSourceStream(pinRangeStream)
- : new PinRangeSourceStatic(0, Integer.MAX_VALUE /* will be clipped */);
- return pinFileRanges(fileToPin, maxBytesToPin, pinRangeSource);
+ boolean use_pinlist = (pinRangeStream != null);
+ PinRangeSource pinRangeSource = use_pinlist
+ ? new PinRangeSourceStream(pinRangeStream)
+ : new PinRangeSourceStatic(0, Integer.MAX_VALUE /* will be clipped */);
+ PinnedFile pinnedFile = pinFileRanges(fileToPin, maxBytesToPin, pinRangeSource);
+ if (pinnedFile != null) {
+ pinnedFile.used_pinlist = use_pinlist;
+ }
+ return pinnedFile;
} finally {
safeClose(pinRangeStream);
safeClose(fileAsZip); // Also closes any streams we've opened
+ Trace.endSection();
}
}
@@ -1013,9 +1103,23 @@
return null;
}
+ // Looking at root directory is the old behavior but still some apps rely on it so keeping
+ // for backward compatibility. As doing a single item lookup is cheap in the root.
ZipEntry pinMetaEntry = zipFile.getEntry(PIN_META_FILENAME);
+
+ if (pinMetaEntry == null) {
+ // It is usually within an apk's control to include files in assets/ directory
+ // so this would be the expected point to have the pinlist.meta coming from.
+ // we explicitly avoid doing an exhaustive search because it may be expensive so
+ // prefer to have a good known location to retrieve the file.
+ pinMetaEntry = zipFile.getEntry("assets/" + PIN_META_FILENAME);
+ }
+
InputStream pinMetaStream = null;
if (pinMetaEntry != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Found pinlist.meta for " + fileName);
+ }
try {
pinMetaStream = zipFile.getInputStream(pinMetaEntry);
} catch (IOException ex) {
@@ -1024,6 +1128,10 @@
fileName),
ex);
}
+ } else {
+ Slog.w(TAG,
+ String.format(
+ "Could not find pinlist.meta for \"%s\": pinning as blob", fileName));
}
return pinMetaStream;
}
@@ -1160,6 +1268,49 @@
}
}
}
+ private List<PinnedFile> getAllPinsForGroup(String group) {
+ List<PinnedFile> filesInGroup;
+ synchronized (this) {
+ filesInGroup = mPinnedFiles.values()
+ .stream()
+ .filter(pf -> pf.groupName.equals(group))
+ .toList();
+ }
+ return filesInGroup;
+ }
+ public void unpinGroup(String group) {
+ List<PinnedFile> pinnedFiles = getAllPinsForGroup(group);
+ for (PinnedFile pf : pinnedFiles) {
+ unpinFile(pf.fileName);
+ }
+ }
+
+ public void unpinFile(String filename) {
+ PinnedFile pinnedFile;
+ synchronized (this) {
+ pinnedFile = mPinnedFiles.get(filename);
+ }
+ if (pinnedFile == null) {
+ // File not pinned, nothing to do.
+ return;
+ }
+ pinnedFile.close();
+ synchronized (this) {
+ if (DEBUG) {
+ Slog.d(TAG, "Unpinned file: " + filename);
+ }
+ mPinnedFiles.remove(pinnedFile.fileName);
+ for (PinnedFile dep : pinnedFile.pinnedDeps) {
+ if (dep == null) {
+ continue;
+ }
+ mPinnedFiles.remove(dep.fileName);
+ if (DEBUG) {
+ Slog.d(TAG, "Unpinned dependency: " + dep.fileName);
+ }
+ }
+ }
+ }
private static int clamp(int min, int value, int max) {
return Math.max(min, Math.min(value, max));
@@ -1205,17 +1356,44 @@
}
}
- private final class BinderService extends Binder {
+ public List<PinnedFileStat> getPinnerStats() {
+ ArrayList<PinnedFileStat> stats = new ArrayList<>();
+ Collection<PinnedApp> pinnedApps;
+ synchronized(this) {
+ pinnedApps = mPinnedApps.values();
+ }
+ for (PinnedApp pinnedApp : pinnedApps) {
+ for (PinnedFile pf : pinnedApp.mFiles) {
+ PinnedFileStat stat =
+ new PinnedFileStat(pf.fileName, pf.bytesPinned, pf.groupName);
+ stats.add(stat);
+ }
+ }
+
+ Collection<PinnedFile> pinnedFiles;
+ synchronized(this) {
+ pinnedFiles = mPinnedFiles.values();
+ }
+ for (PinnedFile pf : pinnedFiles) {
+ PinnedFileStat stat = new PinnedFileStat(pf.fileName, pf.bytesPinned, pf.groupName);
+ stats.add(stat);
+ }
+ if (mCurrentlyPinnedAnonSize > 0) {
+ stats.add(new PinnedFileStat(ANON_REGION_STAT_NAME,
+ mCurrentlyPinnedAnonSize, ANON_REGION_STAT_NAME));
+ }
+ return stats;
+ }
+
+ public final class BinderService extends IPinnerService.Stub {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ HashSet<PinnedFile> shownPins = new HashSet<>();
+ HashSet<String> groups = new HashSet<>();
+ final int bytesPerMB = 1024 * 1024;
synchronized (PinnerService.this) {
long totalSize = 0;
- for (PinnedFile pinnedFile : mPinnedFiles) {
- pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
- totalSize += pinnedFile.bytesPinned;
- }
- pw.println();
for (int key : mPinnedApps.keySet()) {
PinnedApp app = mPinnedApps.get(key);
pw.print(getNameForKey(key));
@@ -1223,14 +1401,53 @@
pw.print(" active="); pw.print(app.active);
pw.println();
for (PinnedFile pf : mPinnedApps.get(key).mFiles) {
- pw.print(" "); pw.format("%s %s\n", pf.fileName, pf.bytesPinned);
+ pw.print(" ");
+ pw.format("%s pinned:%d bytes (%d MB) pinlist:%b\n", pf.fileName,
+ pf.bytesPinned, pf.bytesPinned / bytesPerMB, pf.used_pinlist);
totalSize += pf.bytesPinned;
+ shownPins.add(pf);
+ for (PinnedFile dep : pf.pinnedDeps) {
+ pw.print(" ");
+ pw.format("%s pinned:%d bytes (%d MB) pinlist:%b (Dependency)\n", dep.fileName,
+ dep.bytesPinned, dep.bytesPinned / bytesPerMB, dep.used_pinlist);
+ totalSize += dep.bytesPinned;
+ shownPins.add(dep);
+ }
}
}
- if (mPinAnonAddress != 0) {
- pw.format("Pinned anon region: %s\n", mCurrentlyPinnedAnonSize);
+ pw.println();
+ for (PinnedFile pinnedFile : mPinnedFiles.values()) {
+ if (!groups.contains(pinnedFile.groupName)) {
+ groups.add(pinnedFile.groupName);
+ }
}
- pw.format("Total size: %s\n", totalSize);
+ boolean firstPinInGroup = true;
+ for (String group : groups) {
+ List<PinnedFile> groupPins = getAllPinsForGroup(group);
+ for (PinnedFile pinnedFile : groupPins) {
+ if (shownPins.contains(pinnedFile)) {
+ // Already showed in the dump and accounted for, skip.
+ continue;
+ }
+ if (firstPinInGroup) {
+ firstPinInGroup = false;
+ // Ensure we only print when there are pins for groups not yet shown
+ // in the pinned app section.
+ pw.print("Group:" + group);
+ pw.println();
+ }
+ pw.format(" %s pinned:%d bytes (%d MB) pinlist:%b\n", pinnedFile.fileName,
+ pinnedFile.bytesPinned, pinnedFile.bytesPinned / bytesPerMB,
+ pinnedFile.used_pinlist);
+ totalSize += pinnedFile.bytesPinned;
+ }
+ }
+ pw.println();
+ if (mPinAnonAddress != 0) {
+ pw.format("Pinned anon region: %d (%d MB)\n", mCurrentlyPinnedAnonSize, mCurrentlyPinnedAnonSize / bytesPerMB);
+ totalSize += mCurrentlyPinnedAnonSize;
+ }
+ pw.format("Total pinned: %s bytes (%s MB)\n", totalSize, totalSize / bytesPerMB);
pw.println();
if (!mPendingRepin.isEmpty()) {
pw.print("Pending repin: ");
@@ -1277,14 +1494,29 @@
resultReceiver.send(0, null);
}
+
+ @EnforcePermission(android.Manifest.permission.DUMP)
+ @Override
+ public List<PinnedFileStat> getPinnerStats() {
+ getPinnerStats_enforcePermission();
+ return PinnerService.this.getPinnerStats();
+ }
}
- private static final class PinnedFile implements AutoCloseable {
+ @VisibleForTesting
+ public static final class PinnedFile implements AutoCloseable {
private long mAddress;
final int mapSize;
final String fileName;
final int bytesPinned;
+ // Whether this file was pinned using a pinlist
+ boolean used_pinlist;
+
+ // User defined group name for pinner accounting
+ String groupName = "";
+ ArrayList<PinnedFile> pinnedDeps = new ArrayList<>();
+
PinnedFile(long address, int mapSize, String fileName, int bytesPinned) {
mAddress = address;
this.mapSize = mapSize;
@@ -1298,6 +1530,11 @@
safeMunmap(mAddress, mapSize);
mAddress = -1;
}
+ for (PinnedFile dep : pinnedDeps) {
+ if (dep != null) {
+ dep.close();
+ }
+ }
}
@Override
@@ -1355,5 +1592,4 @@
}
}
}
-
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index f6835fe..39b8643 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -215,7 +215,7 @@
public static final int FAILED_MOUNT_RESET_TIMEOUT_SECONDS = 10;
/** Extended timeout for the system server watchdog. */
- private static final int SLOW_OPERATION_WATCHDOG_TIMEOUT_MS = 60 * 1000;
+ private static final int SLOW_OPERATION_WATCHDOG_TIMEOUT_MS = 20 * 1000;
/** Extended timeout for the system server watchdog for vold#partition operation. */
private static final int PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS = 3 * 60 * 1000;
@@ -1235,11 +1235,16 @@
}
}
+ private void extendWatchdogTimeout(String reason) {
+ Watchdog w = Watchdog.getInstance();
+ w.pauseWatchingMonitorsFor(SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, reason);
+ w.pauseWatchingCurrentThreadFor(SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, reason);
+ }
+
private void onUserStopped(int userId) {
Slog.d(TAG, "onUserStopped " + userId);
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow");
+ extendWatchdogTimeout("#onUserStopped might be slow");
try {
mVold.onUserStopped(userId);
mStoraged.onUserStopped(userId);
@@ -1322,8 +1327,7 @@
unlockedUsers.add(userId);
}
}
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow");
+ extendWatchdogTimeout("#onUserStopped might be slow");
for (Integer userId : unlockedUsers) {
try {
mVold.onUserStopped(userId);
@@ -2343,8 +2347,7 @@
try {
// TODO(b/135341433): Remove cautious logging when FUSE is stable
Slog.i(TAG, "Mounting volume " + vol);
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#mount might be slow");
+ extendWatchdogTimeout("#mount might be slow");
mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() {
@Override
public boolean onVolumeChecking(FileDescriptor fd, String path,
@@ -2474,8 +2477,7 @@
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
+ extendWatchdogTimeout("#partition might be slow");
try {
mVold.partition(diskId, IVold.PARTITION_TYPE_PUBLIC, -1);
waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS);
@@ -2493,8 +2495,7 @@
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
+ extendWatchdogTimeout("#partition might be slow");
try {
mVold.partition(diskId, IVold.PARTITION_TYPE_PRIVATE, -1);
waitForLatch(latch, "partitionPrivate", 3 * DateUtils.MINUTE_IN_MILLIS);
@@ -2512,8 +2513,7 @@
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
+ extendWatchdogTimeout("#partition might be slow");
try {
mVold.partition(diskId, IVold.PARTITION_TYPE_MIXED, ratio);
waitForLatch(latch, "partitionMixed", 3 * DateUtils.MINUTE_IN_MILLIS);
@@ -3622,8 +3622,7 @@
@Override
public ParcelFileDescriptor open() throws AppFuseMountException {
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#open might be slow");
+ extendWatchdogTimeout("#open might be slow");
try {
final FileDescriptor fd = mVold.mountAppFuse(uid, mountId);
mMounted = true;
@@ -3636,8 +3635,7 @@
@Override
public ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
throws AppFuseMountException {
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#openFile might be slow");
+ extendWatchdogTimeout("#openFile might be slow");
try {
return new ParcelFileDescriptor(
mVold.openAppFuseFile(uid, mountId, fileId, flags));
@@ -3648,8 +3646,7 @@
@Override
public void close() throws Exception {
- Watchdog.getInstance().pauseWatchingMonitorsFor(
- SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#close might be slow");
+ extendWatchdogTimeout("#close might be slow");
if (mMounted) {
BackgroundThread.getHandler().post(() -> {
try {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index c718d39..9eb35fd 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2096,14 +2096,48 @@
/**
* Send a notification to registrants about the data activity state.
*
+ * @param subId the subscriptionId for the data connection
+ * @param state indicates the latest data activity type
+ * e.g.,{@link TelephonyManager#DATA_ACTIVITY_IN}
+ *
+ */
+
+ public void notifyDataActivityForSubscriber(int subId, int state) {
+ if (!checkNotifyPermission("notifyDataActivity()")) {
+ return;
+ }
+ int phoneId = getPhoneIdFromSubId(subId);
+ synchronized (mRecords) {
+ if (validatePhoneId(phoneId)) {
+ mDataActivity[phoneId] = state;
+ for (Record r : mRecords) {
+ // Notify by correct subId.
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+ try {
+ r.callback.onDataActivity(state);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
+ /**
+ * Send a notification to registrants about the data activity state.
+ *
* @param phoneId the phoneId carrying the data connection
* @param subId the subscriptionId for the data connection
* @param state indicates the latest data activity type
* e.g.,{@link TelephonyManager#DATA_ACTIVITY_IN}
*
*/
- public void notifyDataActivityForSubscriber(int phoneId, int subId, int state) {
- if (!checkNotifyPermission("notifyDataActivity()" )) {
+ public void notifyDataActivityForSubscriberWithSlot(int phoneId, int subId, int state) {
+ if (!checkNotifyPermission("notifyDataActivityWithSlot()")) {
return;
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 003046a..3135650 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -98,10 +98,16 @@
// 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 WAITED_UNTIL_PRE_WATCHDOG = 2;
private static final int OVERDUE = 3;
// Track watchdog timeout history and break the crash loop if there is.
@@ -310,10 +316,10 @@
return COMPLETED;
} else {
long latency = SystemClock.uptimeMillis() - mStartTimeMillis;
- if (latency < mWaitMaxMillis / 2) {
+ if (latency < mWaitMaxMillis / PRE_WATCHDOG_TIMEOUT_RATIO) {
return WAITING;
} else if (latency < mWaitMaxMillis) {
- return WAITED_HALF;
+ return WAITED_UNTIL_PRE_WATCHDOG;
}
}
return OVERDUE;
@@ -368,8 +374,9 @@
public void pauseForLocked(int pauseMillis, String reason) {
mPauseEndTimeMillis = SystemClock.uptimeMillis() + 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 +386,9 @@
public void pauseLocked(String reason) {
mPauseCount++;
// Mark as completed, because there's a chance we called this after the watchog
- // thread loop called Object#wait after 'WAITED_HALF'. In that case we want to ensure
- // the next call to #getCompletionStateLocked for this checker returns 'COMPLETED'
+ // thread loop called Object#wait after 'WAITED_UNTIL_PRE_WATCHDOG'. In that case we
+ // want to ensure the next call to #getCompletionStateLocked for this checker returns
+ // 'COMPLETED'
mCompleted = true;
Slog.i(TAG, "Pausing HandlerChecker: " + mName + " for reason: "
+ reason + ". Pause count: " + mPauseCount);
@@ -797,11 +805,11 @@
String subject = "";
boolean allowRestart = true;
int debuggerWasConnected = 0;
- boolean doWaitedHalfDump = false;
+ boolean doWaitedPreDump = false;
// The value of mWatchdogTimeoutMillis might change while we are executing the loop.
// We store the current value to use a consistent value for all handlers.
final long watchdogTimeoutMillis = mWatchdogTimeoutMillis;
- final long checkIntervalMillis = watchdogTimeoutMillis / 2;
+ final long checkIntervalMillis = watchdogTimeoutMillis / PRE_WATCHDOG_TIMEOUT_RATIO;
final ArrayList<Integer> pids;
synchronized (mLock) {
long timeout = checkIntervalMillis;
@@ -848,15 +856,16 @@
} else if (waitState == WAITING) {
// still waiting but within their configured intervals; back off and recheck
continue;
- } else if (waitState == WAITED_HALF) {
+ } else if (waitState == WAITED_UNTIL_PRE_WATCHDOG) {
if (!waitedHalf) {
- Slog.i(TAG, "WAITED_HALF");
+ Slog.i(TAG, "WAITED_UNTIL_PRE_WATCHDOG");
waitedHalf = true;
- // We've waited half, but we'd need to do the stack trace dump w/o the lock.
- blockedCheckers = getCheckersWithStateLocked(WAITED_HALF);
+ // We've waited until the pre-watchdog, but we'd need to do the stack trace
+ // dump w/o the lock.
+ blockedCheckers = getCheckersWithStateLocked(WAITED_UNTIL_PRE_WATCHDOG);
subject = describeCheckersLocked(blockedCheckers);
pids = new ArrayList<>(mInterestingJavaPids);
- doWaitedHalfDump = true;
+ doWaitedPreDump = true;
} else {
continue;
}
@@ -874,12 +883,12 @@
// First collect stack traces from all threads of the system process.
//
// Then, if we reached the full timeout, kill this process so that the system will
- // restart. If we reached half of the timeout, just log some information and continue.
- logWatchog(doWaitedHalfDump, subject, pids);
+ // restart. If we reached pre-watchdog timeout, just log some information and continue.
+ logWatchog(doWaitedPreDump, subject, pids);
- if (doWaitedHalfDump) {
- // We have waited for only half of the timeout, we continue to wait for the duration
- // of the full timeout before killing the process.
+ if (doWaitedPreDump) {
+ // We have waited for only pre-watchdog timeout, we continue to wait for the
+ // duration of the full timeout before killing the process.
continue;
}
@@ -928,8 +937,8 @@
}
}
- private void logWatchog(boolean halfWatchdog, String subject, ArrayList<Integer> pids) {
- // Get critical event log before logging the half watchdog so that it doesn't
+ private void logWatchog(boolean preWatchdog, String subject, ArrayList<Integer> pids) {
+ // Get critical event log before logging the pre-watchdog so that it doesn't
// occur in the log.
String criticalEvents =
CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
@@ -941,7 +950,7 @@
}
final String dropboxTag;
- if (halfWatchdog) {
+ if (preWatchdog) {
dropboxTag = "pre_watchdog";
CriticalEventLog.getInstance().logHalfWatchdog(subject);
FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_SERVER_PRE_WATCHDOG_OCCURRED);
@@ -971,7 +980,7 @@
report.append(processCpuTracker.printCurrentState(anrTime, 10));
report.append(tracesFileException.getBuffer());
- if (!halfWatchdog) {
+ if (!preWatchdog) {
// Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the
// kernel log
doSysRq('w');
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c64fb23..b87d02d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -79,7 +79,6 @@
import static android.os.PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
import static android.os.PowerExemptionManager.REASON_BOOT_COMPLETED;
import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER;
-import static android.os.PowerExemptionManager.REASON_DENIED;
import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
import static android.os.PowerExemptionManager.REASON_PROC_STATE_BTOP;
@@ -4852,8 +4851,10 @@
} else {
Slog.wtf(TAG, "Mismatched or missing ProcessRecord: " + app + ". Pid: " + pid
+ ". Uid: " + uid);
- killProcess(pid);
- killProcessGroup(uid, pid);
+ if (pid > 0) {
+ killProcess(pid);
+ killProcessGroup(uid, pid);
+ }
mProcessList.noteAppKill(pid, uid,
ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
ApplicationExitInfo.SUBREASON_UNKNOWN,
@@ -5049,7 +5050,7 @@
REASON_LOCKED_BOOT_COMPLETED);
}
// Send BOOT_COMPLETED if the user is unlocked
- if (StorageManager.isUserKeyUnlocked(app.userId)) {
+ if (StorageManager.isCeStorageUnlocked(app.userId)) {
sendBootBroadcastToAppLocked(app, new Intent(Intent.ACTION_BOOT_COMPLETED),
REASON_BOOT_COMPLETED);
}
@@ -14148,7 +14149,8 @@
|| action.startsWith("android.intent.action.PACKAGE_")
|| action.startsWith("android.intent.action.UID_")
|| action.startsWith("android.intent.action.EXTERNAL_")
- || action.startsWith("android.bluetooth.")) {
+ || action.startsWith("android.bluetooth.")
+ || action.equals(Intent.ACTION_SHUTDOWN)) {
if (DEBUG_BROADCAST) {
Slog.wtf(TAG,
"System internals registering for " + filter.toLongString()
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 36356bd..1928780 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -120,12 +120,15 @@
import com.android.server.power.optimization.Flags;
import com.android.server.power.stats.AggregatedPowerStatsConfig;
import com.android.server.power.stats.BatteryExternalStatsWorker;
+import com.android.server.power.stats.BatteryStatsDumpHelperImpl;
import com.android.server.power.stats.BatteryStatsImpl;
import com.android.server.power.stats.BatteryUsageStatsProvider;
import com.android.server.power.stats.CpuAggregatedPowerStatsProcessor;
import com.android.server.power.stats.PowerStatsAggregator;
+import com.android.server.power.stats.PowerStatsExporter;
import com.android.server.power.stats.PowerStatsScheduler;
import com.android.server.power.stats.PowerStatsStore;
+import com.android.server.power.stats.PowerStatsUidResolver;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import com.android.server.power.stats.wakeups.CpuWakeupStats;
@@ -181,6 +184,8 @@
private final BatteryExternalStatsWorker mWorker;
private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
private final AtomicFile mConfigFile;
+ private final BatteryStats.BatteryStatsDumpHelper mDumpHelper;
+ private final PowerStatsUidResolver mPowerStatsUidResolver;
private volatile boolean mMonitorEnabled = true;
@@ -408,9 +413,10 @@
.setResetOnUnplugAfterSignificantCharge(resetOnUnplugAfterSignificantCharge)
.setPowerStatsThrottlePeriodCpu(powerStatsThrottlePeriodCpu)
.build();
+ mPowerStatsUidResolver = new PowerStatsUidResolver();
mStats = new BatteryStatsImpl(mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
systemDir, handler, this, this, mUserManagerUserInfoProvider, mPowerProfile,
- mCpuScalingPolicies);
+ mCpuScalingPolicies, mPowerStatsUidResolver);
mWorker = new BatteryExternalStatsWorker(context, mStats);
mStats.setExternalStatsSyncLocked(mWorker);
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
@@ -419,8 +425,6 @@
AggregatedPowerStatsConfig aggregatedPowerStatsConfig = getAggregatedPowerStatsConfig();
mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, aggregatedPowerStatsConfig);
- mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats,
- mPowerStatsStore);
mPowerStatsAggregator = new PowerStatsAggregator(aggregatedPowerStatsConfig,
mStats.getHistory());
final long aggregatedPowerStatsSpanDuration = context.getResources().getInteger(
@@ -429,7 +433,14 @@
com.android.internal.R.integer.config_powerStatsAggregationPeriod);
mPowerStatsScheduler = new PowerStatsScheduler(context, mPowerStatsAggregator,
aggregatedPowerStatsSpanDuration, powerStatsAggregationPeriod, mPowerStatsStore,
- Clock.SYSTEM_CLOCK, mMonotonicClock, mHandler, mStats, mBatteryUsageStatsProvider);
+ Clock.SYSTEM_CLOCK, mMonotonicClock, mHandler, mStats);
+ PowerStatsExporter powerStatsExporter =
+ new PowerStatsExporter(mPowerStatsStore, mPowerStatsAggregator);
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context,
+ powerStatsExporter, mPowerProfile, mCpuScalingPolicies,
+ mPowerStatsStore, Clock.SYSTEM_CLOCK);
+ mStats.saveBatteryUsageStatsOnReset(mBatteryUsageStatsProvider, mPowerStatsStore);
+ mDumpHelper = new BatteryStatsDumpHelperImpl(mBatteryUsageStatsProvider);
mCpuWakeupStats = new CpuWakeupStats(context, R.xml.irq_device_map, mHandler);
mConfigFile = new AtomicFile(new File(systemDir, "battery_usage_stats_config"));
}
@@ -469,9 +480,10 @@
}
public void systemServicesReady() {
+ mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(Flags.streamlinedBatteryStats());
+ mWorker.systemServicesReady();
mStats.systemServicesReady(mContext);
mCpuWakeupStats.systemServicesReady();
- mWorker.systemServicesReady();
final INetworkManagementService nms = INetworkManagementService.Stub.asInterface(
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
@@ -775,25 +787,15 @@
}
void addIsolatedUid(final int isolatedUid, final int appUid) {
- synchronized (mLock) {
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- final long uptime = SystemClock.uptimeMillis();
- mHandler.post(() -> {
- synchronized (mStats) {
- mStats.addIsolatedUidLocked(isolatedUid, appUid, elapsedRealtime, uptime);
- }
- });
- }
+ mPowerStatsUidResolver.noteIsolatedUidAdded(isolatedUid, appUid);
+ FrameworkStatsLog.write(FrameworkStatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid,
+ FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
}
void removeIsolatedUid(final int isolatedUid, final int appUid) {
- synchronized (mLock) {
- mHandler.post(() -> {
- synchronized (mStats) {
- mStats.scheduleRemoveIsolatedUidLocked(isolatedUid, appUid);
- }
- });
- }
+ mPowerStatsUidResolver.noteIsolatedUidRemoved(isolatedUid, appUid);
+ FrameworkStatsLog.write(FrameworkStatsLog.ISOLATED_UID_CHANGED, -1, isolatedUid,
+ FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__REMOVED);
}
void noteProcessStart(final String name, final int uid) {
@@ -877,12 +879,15 @@
awaitCompletion();
- if (mBatteryUsageStatsProvider.shouldUpdateStats(queries,
+ if (BatteryUsageStatsProvider.shouldUpdateStats(queries,
+ SystemClock.elapsedRealtime(),
mWorker.getLastCollectionTimeStamp())) {
syncStats("get-stats", BatteryExternalStatsWorker.UPDATE_ALL);
}
- return mBatteryUsageStatsProvider.getBatteryUsageStats(queries);
+ synchronized (mStats) {
+ return mBatteryUsageStatsProvider.getBatteryUsageStats(mStats, queries);
+ }
}
/** Register callbacks for statsd pulled atoms. */
@@ -2723,7 +2728,7 @@
synchronized (mStats) {
mStats.prepareForDumpLocked();
BatteryUsageStats batteryUsageStats =
- mBatteryUsageStatsProvider.getBatteryUsageStats(query);
+ mBatteryUsageStatsProvider.getBatteryUsageStats(mStats, query);
if (proto) {
batteryUsageStats.dumpToProto(fd);
} else {
@@ -3008,11 +3013,11 @@
mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
null, mStats.mHandler, null, null,
mUserManagerUserInfoProvider, mPowerProfile,
- mCpuScalingPolicies);
+ mCpuScalingPolicies, new PowerStatsUidResolver());
checkinStats.readSummaryFromParcel(in);
in.recycle();
- checkinStats.dumpProtoLocked(
- mContext, fd, apps, flags, historyStart);
+ checkinStats.dumpProtoLocked(mContext, fd, apps, flags,
+ historyStart, mDumpHelper);
mStats.mCheckinFile.delete();
return;
}
@@ -3026,7 +3031,7 @@
if (DBG) Slog.d(TAG, "begin dumpProtoLocked from UID " + Binder.getCallingUid());
awaitCompletion();
synchronized (mStats) {
- mStats.dumpProtoLocked(mContext, fd, apps, flags, historyStart);
+ mStats.dumpProtoLocked(mContext, fd, apps, flags, historyStart, mDumpHelper);
if (writeData) {
mStats.writeAsyncLocked();
}
@@ -3050,11 +3055,11 @@
mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
null, mStats.mHandler, null, null,
mUserManagerUserInfoProvider, mPowerProfile,
- mCpuScalingPolicies);
+ mCpuScalingPolicies, new PowerStatsUidResolver());
checkinStats.readSummaryFromParcel(in);
in.recycle();
checkinStats.dumpCheckin(mContext, pw, apps, flags,
- historyStart);
+ historyStart, mDumpHelper);
mStats.mCheckinFile.delete();
return;
}
@@ -3067,7 +3072,7 @@
}
if (DBG) Slog.d(TAG, "begin dumpCheckin from UID " + Binder.getCallingUid());
awaitCompletion();
- mStats.dumpCheckin(mContext, pw, apps, flags, historyStart);
+ mStats.dumpCheckin(mContext, pw, apps, flags, historyStart, mDumpHelper);
if (writeData) {
mStats.writeAsyncLocked();
}
@@ -3076,7 +3081,7 @@
if (DBG) Slog.d(TAG, "begin dump from UID " + Binder.getCallingUid());
awaitCompletion();
- mStats.dump(mContext, pw, flags, reqUid, historyStart);
+ mStats.dump(mContext, pw, flags, reqUid, historyStart, mDumpHelper);
if (writeData) {
mStats.writeAsyncLocked();
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index cb2b5fb..4ff34b1 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -34,7 +34,7 @@
import static android.os.Process.getTotalMemory;
import static android.os.Process.killProcessQuiet;
import static android.os.Process.startWebView;
-import static android.system.OsConstants.*;
+import static android.system.OsConstants.EAGAIN;
import static com.android.sdksandbox.flags.Flags.selinuxSdkSandboxAudit;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
@@ -133,7 +133,6 @@
import com.android.internal.app.ProcessMap;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.MemInfoReader;
import com.android.server.AppStateTracker;
import com.android.server.LocalServices;
@@ -3299,8 +3298,6 @@
// about the process state of the isolated UID *before* it is registered with the
// owning application.
mService.mBatteryStatsService.addIsolatedUid(uid, info.uid);
- FrameworkStatsLog.write(FrameworkStatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
- FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
}
final ProcessRecord r = new ProcessRecord(mService, info, proc, uid,
sdkSandboxClientAppPackage,
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 192fd6f..69e3aaf 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -152,11 +152,13 @@
"mainline_sdk",
"media_audio",
"media_drm",
+ "media_reliability",
"media_tv",
"media_solutions",
"nfc",
"pdf_viewer",
"pixel_audio_android",
+ "pixel_bluetooth",
"pixel_system_sw_touch",
"pixel_watch",
"platform_security",
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
index 0ee7d9c..0916967 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
@@ -20,6 +20,7 @@
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.OP_SCHEDULE_EXACT_ALARM;
import static android.app.AppOpsManager.OP_USE_FULL_SCREEN_INTENT;
+import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -150,7 +151,7 @@
}
@Override
- public SparseIntArray getNonDefaultUidModes(int uid) {
+ public SparseIntArray getNonDefaultUidModes(int uid, String persistentDeviceId) {
synchronized (mLock) {
SparseIntArray opModes = mUidModes.get(uid, null);
if (opModes == null) {
@@ -176,7 +177,7 @@
}
@Override
- public int getUidMode(int uid, int op) {
+ public int getUidMode(int uid, String persistentDeviceId, int op) {
synchronized (mLock) {
SparseIntArray opModes = mUidModes.get(uid, null);
if (opModes == null) {
@@ -187,7 +188,7 @@
}
@Override
- public boolean setUidMode(int uid, int op, int mode) {
+ public boolean setUidMode(int uid, String persistentDeviceId, int op, int mode) {
final int defaultMode = AppOpsManager.opToDefaultMode(op);
List<AppOpsModeChangedListener> listenersCopy;
synchronized (mLock) {
@@ -329,7 +330,7 @@
}
@Override
- public SparseBooleanArray getForegroundOps(int uid) {
+ public SparseBooleanArray getForegroundOps(int uid, String persistentDeviceId) {
SparseBooleanArray result = new SparseBooleanArray();
synchronized (mLock) {
SparseIntArray modes = mUidModes.get(uid);
@@ -606,9 +607,17 @@
for (final String pkg : packagesDeclaringPermission) {
for (int userId : userIds) {
final int uid = pmi.getPackageUid(pkg, 0, userId);
- final int oldMode = getUidMode(uid, OP_SCHEDULE_EXACT_ALARM);
+ final int oldMode =
+ getUidMode(
+ uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ OP_SCHEDULE_EXACT_ALARM);
if (oldMode == AppOpsManager.opToDefaultMode(OP_SCHEDULE_EXACT_ALARM)) {
- setUidMode(uid, OP_SCHEDULE_EXACT_ALARM, MODE_ALLOWED);
+ setUidMode(
+ uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ OP_SCHEDULE_EXACT_ALARM,
+ MODE_ALLOWED);
}
}
// This appop is meant to be controlled at a uid level. So we leave package modes as
@@ -641,7 +650,10 @@
final int flags = permissionManager.getPermissionFlags(pkg, permissionName,
UserHandle.of(userId));
if ((flags & PackageManager.FLAG_PERMISSION_USER_SET) == 0) {
- setUidMode(uid, OP_USE_FULL_SCREEN_INTENT,
+ setUidMode(
+ uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ OP_USE_FULL_SCREEN_INTENT,
AppOpsManager.opToDefaultMode(OP_USE_FULL_SCREEN_INTENT));
}
}
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java
index f6e6bc0..f056f6b 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceInterface.java
@@ -59,8 +59,9 @@
* Returns a copy of non-default app-ops with op as keys and their modes as values for a uid.
* Returns an empty SparseIntArray if nothing is set.
* @param uid for which we need the app-ops and their modes.
+ * @param persistentDeviceId device for which we need the app-ops and their modes
*/
- SparseIntArray getNonDefaultUidModes(int uid);
+ SparseIntArray getNonDefaultUidModes(int uid, String persistentDeviceId);
/**
* Returns a copy of non-default app-ops with op as keys and their modes as values for a package
@@ -75,20 +76,22 @@
* Returns the app-op mode for a particular app-op of a uid.
* Returns default op mode if the op mode for particular uid and op is not set.
* @param uid user id for which we need the mode.
+ * @param persistentDeviceId device for which we need the mode
* @param op app-op for which we need the mode.
* @return mode of the app-op.
*/
- int getUidMode(int uid, int op);
+ int getUidMode(int uid, String persistentDeviceId, int op);
/**
* Set the app-op mode for a particular uid and op.
* The mode is not set if the mode is the same as the default mode for the op.
* @param uid user id for which we want to set the mode.
+ * @param persistentDeviceId device for which we want to set the mode.
* @param op app-op for which we want to set the mode.
* @param mode mode for the app-op.
* @return true if op mode is changed.
*/
- boolean setUidMode(int uid, int op, @Mode int mode);
+ boolean setUidMode(int uid, String persistentDeviceId, int op, @Mode int mode);
/**
* Gets the app-op mode for a particular package.
@@ -130,10 +133,11 @@
/**
* @param uid UID to query foreground ops for.
+ * @param persistentDeviceId device to query foreground ops for
* @return SparseBooleanArray where the keys are the op codes for which their modes are
* MODE_FOREGROUND for the passed UID.
*/
- SparseBooleanArray getForegroundOps(int uid);
+ SparseBooleanArray getForegroundOps(int uid, String persistentDeviceId);
/**
*
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java
index ccdf3a5..f6da166 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceLoggingDecorator.java
@@ -60,9 +60,9 @@
}
@Override
- public SparseIntArray getNonDefaultUidModes(int uid) {
+ public SparseIntArray getNonDefaultUidModes(int uid, String persistentDeviceId) {
Log.i(LOG_TAG, "getNonDefaultUidModes(uid = " + uid + ")");
- return mService.getNonDefaultUidModes(uid);
+ return mService.getNonDefaultUidModes(uid, persistentDeviceId);
}
@Override
@@ -73,15 +73,15 @@
}
@Override
- public int getUidMode(int uid, int op) {
+ public int getUidMode(int uid, String persistentDeviceId, int op) {
Log.i(LOG_TAG, "getUidMode(uid = " + uid + ", op = " + op + ")");
- return mService.getUidMode(uid, op);
+ return mService.getUidMode(uid, persistentDeviceId, op);
}
@Override
- public boolean setUidMode(int uid, int op, int mode) {
+ public boolean setUidMode(int uid, String persistentDeviceId, int op, int mode) {
Log.i(LOG_TAG, "setUidMode(uid = " + uid + ", op = " + op + ", mode = " + mode + ")");
- return mService.setUidMode(uid, op, mode);
+ return mService.setUidMode(uid, persistentDeviceId, op, mode);
}
@Override
@@ -117,9 +117,9 @@
}
@Override
- public SparseBooleanArray getForegroundOps(int uid) {
+ public SparseBooleanArray getForegroundOps(int uid, String persistentDeviceId) {
Log.i(LOG_TAG, "getForegroundOps(uid = " + uid + ")");
- return mService.getForegroundOps(uid);
+ return mService.getForegroundOps(uid, persistentDeviceId);
}
@Override
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
index c3a02a8..55cf7ed 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceTracingDecorator.java
@@ -81,11 +81,11 @@
}
@Override
- public SparseIntArray getNonDefaultUidModes(int uid) {
+ public SparseIntArray getNonDefaultUidModes(int uid, String persistentDeviceId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingAppOpsCheckingServiceInterfaceImpl#getNonDefaultUidModes");
try {
- return mService.getNonDefaultUidModes(uid);
+ return mService.getNonDefaultUidModes(uid, persistentDeviceId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -103,20 +103,21 @@
}
@Override
- public int getUidMode(int uid, int op) {
+ public int getUidMode(int uid, String persistentDeviceId, int op) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingAppOpsCheckingServiceInterfaceImpl#getUidMode");
try {
- return mService.getUidMode(uid, op);
+ return mService.getUidMode(uid, persistentDeviceId, op);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public boolean setUidMode(int uid, int op, @AppOpsManager.Mode int mode) {
+ public boolean setUidMode(
+ int uid, String persistentDeviceId, int op, @AppOpsManager.Mode int mode) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingAppOpsCheckingServiceInterfaceImpl#setUidMode");
try {
- return mService.setUidMode(uid, op, mode);
+ return mService.setUidMode(uid, persistentDeviceId, op, mode);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -179,11 +180,11 @@
}
@Override
- public SparseBooleanArray getForegroundOps(int uid) {
+ public SparseBooleanArray getForegroundOps(int uid, String persistentDeviceId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingAppOpsCheckingServiceInterfaceImpl#getForegroundOps");
try {
- return mService.getForegroundOps(uid);
+ return mService.getForegroundOps(uid, persistentDeviceId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 14aab13..3446737 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -63,6 +63,7 @@
import static android.app.AppOpsManager.opRestrictsRead;
import static android.app.AppOpsManager.opToName;
import static android.app.AppOpsManager.opToPublicName;
+import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
@@ -1349,7 +1350,10 @@
SparseBooleanArray foregroundOps = new SparseBooleanArray();
- SparseBooleanArray uidForegroundOps = mAppOpsCheckingService.getForegroundOps(uid);
+ // TODO(b/299330771): Check uidForegroundOps for all devices.
+ SparseBooleanArray uidForegroundOps =
+ mAppOpsCheckingService.getForegroundOps(
+ uid, PERSISTENT_DEVICE_ID_DEFAULT);
for (int i = 0; i < uidForegroundOps.size(); i++) {
foregroundOps.put(uidForegroundOps.keyAt(i), true);
}
@@ -1369,10 +1373,16 @@
continue;
}
final int code = foregroundOps.keyAt(fgi);
-
- if (mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ // TODO(b/299330771): Notify op changes for all relevant devices.
+ if (mAppOpsCheckingService.getUidMode(
+ uidState.uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ code)
!= AppOpsManager.opToDefaultMode(code)
- && mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ && mAppOpsCheckingService.getUidMode(
+ uidState.uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ code)
== AppOpsManager.MODE_FOREGROUND) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChangedForAllPkgsInUid,
@@ -1489,7 +1499,11 @@
@Nullable
private ArrayList<AppOpsManager.OpEntry> collectUidOps(@NonNull UidState uidState,
@Nullable int[] ops) {
- final SparseIntArray opModes = mAppOpsCheckingService.getNonDefaultUidModes(uidState.uid);
+ // TODO(b/299330771): Make this methods device-aware, currently it represents only the
+ // primary device.
+ final SparseIntArray opModes =
+ mAppOpsCheckingService.getNonDefaultUidModes(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT);
if (opModes == null) {
return null;
}
@@ -1844,16 +1858,22 @@
uidState = new UidState(uid);
mUidStates.put(uid, uidState);
}
- if (mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ // TODO(b/266164193): Ensure this behavior is device-aware after uid op mode for runtime
+ // permissions is deprecated.
+ if (mAppOpsCheckingService.getUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, code)
!= AppOpsManager.opToDefaultMode(code)) {
- previousMode = mAppOpsCheckingService.getUidMode(uidState.uid, code);
+ previousMode =
+ mAppOpsCheckingService.getUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, code);
} else {
// doesn't look right but is legacy behavior.
previousMode = MODE_DEFAULT;
}
mIgnoredCallback = permissionPolicyCallback;
- if (!mAppOpsCheckingService.setUidMode(uidState.uid, code, mode)) {
+ if (!mAppOpsCheckingService.setUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, code, mode)) {
return;
}
if (mode != MODE_ERRORED && mode != previousMode) {
@@ -2275,8 +2295,10 @@
boolean changed = false;
for (int i = mUidStates.size() - 1; i >= 0; i--) {
UidState uidState = mUidStates.valueAt(i);
-
- SparseIntArray opModes = mAppOpsCheckingService.getNonDefaultUidModes(uidState.uid);
+ // TODO(b/299330771): Check non default modes for all devices.
+ SparseIntArray opModes =
+ mAppOpsCheckingService.getNonDefaultUidModes(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT);
if (opModes != null && (uidState.uid == reqUid || reqUid == -1)) {
final int uidOpCount = opModes.size();
for (int j = uidOpCount - 1; j >= 0; j--) {
@@ -2285,7 +2307,12 @@
int previousMode = opModes.valueAt(j);
int newMode = isUidOpGrantedByRole(uidState.uid, code) ? MODE_ALLOWED :
AppOpsManager.opToDefaultMode(code);
- mAppOpsCheckingService.setUidMode(uidState.uid, code, newMode);
+ // TODO(b/299330771): Set mode for all necessary devices.
+ mAppOpsCheckingService.setUidMode(
+ uidState.uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ code,
+ newMode);
for (String packageName : getPackagesForUid(uidState.uid)) {
callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
previousMode, mOpModeWatchers.get(code));
@@ -2601,10 +2628,14 @@
}
code = AppOpsManager.opToSwitch(code);
UidState uidState = getUidStateLocked(uid, false);
+ // TODO(b/299330771): Check mode for the relevant device.
if (uidState != null
- && mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ && mAppOpsCheckingService.getUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, code)
!= AppOpsManager.opToDefaultMode(code)) {
- final int rawMode = mAppOpsCheckingService.getUidMode(uidState.uid, code);
+ final int rawMode =
+ mAppOpsCheckingService.getUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, code);
return raw ? rawMode : uidState.evalMode(code, rawMode);
}
Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ false);
@@ -2851,13 +2882,19 @@
return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
packageName);
}
+ // TODO(b/299330771): Check mode for the relevant device.
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (mAppOpsCheckingService.getUidMode(uidState.uid, switchCode)
+ if (mAppOpsCheckingService.getUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, switchCode)
!= AppOpsManager.opToDefaultMode(switchCode)) {
final int uidMode =
uidState.evalMode(
- code, mAppOpsCheckingService.getUidMode(uidState.uid, switchCode));
+ code,
+ mAppOpsCheckingService.getUidMode(
+ uidState.uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ switchCode));
if (uidMode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
@@ -3396,13 +3433,19 @@
isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass,
false);
final int switchCode = AppOpsManager.opToSwitch(code);
+ // TODO(b/299330771): Check mode for the relevant device.
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (mAppOpsCheckingService.getUidMode(uidState.uid, switchCode)
+ if (mAppOpsCheckingService.getUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, switchCode)
!= AppOpsManager.opToDefaultMode(switchCode)) {
final int uidMode =
uidState.evalMode(
- code, mAppOpsCheckingService.getUidMode(uidState.uid, switchCode));
+ code,
+ mAppOpsCheckingService.getUidMode(
+ uidState.uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ switchCode));
if (!shouldStartForMode(uidMode, startIfModeDefault)) {
if (DEBUG) {
Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code "
@@ -3511,13 +3554,19 @@
isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass,
false);
final int switchCode = AppOpsManager.opToSwitch(code);
+ // TODO(b/299330771): Check mode for the relevant device.
// If there is a non-default mode per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (mAppOpsCheckingService.getUidMode(uidState.uid, switchCode)
+ if (mAppOpsCheckingService.getUidMode(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT, switchCode)
!= AppOpsManager.opToDefaultMode(switchCode)) {
final int uidMode =
uidState.evalMode(
- code, mAppOpsCheckingService.getUidMode(uidState.uid, switchCode));
+ code,
+ mAppOpsCheckingService.getUidMode(
+ uidState.uid,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ switchCode));
if (!shouldStartForMode(uidMode, startIfModeDefault)) {
if (DEBUG) {
Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code "
@@ -5664,8 +5713,10 @@
}
for (int i=0; i<mUidStates.size(); i++) {
UidState uidState = mUidStates.valueAt(i);
+ // TODO(b/299330771): Get modes for all devices.
final SparseIntArray opModes =
- mAppOpsCheckingService.getNonDefaultUidModes(uidState.uid);
+ mAppOpsCheckingService.getNonDefaultUidModes(
+ uidState.uid, PERSISTENT_DEVICE_ID_DEFAULT);
final ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
if (dumpWatchers || dumpHistory) {
diff --git a/services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java b/services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java
index 98e6476..c9fa9e6 100644
--- a/services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java
+++ b/services/core/java/com/android/server/appop/AppOpsServiceTestingShim.java
@@ -65,9 +65,9 @@
}
@Override
- public SparseIntArray getNonDefaultUidModes(int uid) {
- SparseIntArray oldVal = mOldImplementation.getNonDefaultUidModes(uid);
- SparseIntArray newVal = mNewImplementation.getNonDefaultUidModes(uid);
+ public SparseIntArray getNonDefaultUidModes(int uid, String persistentDeviceId) {
+ SparseIntArray oldVal = mOldImplementation.getNonDefaultUidModes(uid, persistentDeviceId);
+ SparseIntArray newVal = mNewImplementation.getNonDefaultUidModes(uid, persistentDeviceId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("getNonDefaultUidModes");
@@ -89,9 +89,9 @@
}
@Override
- public int getUidMode(int uid, int op) {
- int oldVal = mOldImplementation.getUidMode(uid, op);
- int newVal = mNewImplementation.getUidMode(uid, op);
+ public int getUidMode(int uid, String persistentDeviceId, int op) {
+ int oldVal = mOldImplementation.getUidMode(uid, persistentDeviceId, op);
+ int newVal = mNewImplementation.getUidMode(uid, persistentDeviceId, op);
if (oldVal != newVal) {
signalImplDifference("getUidMode");
@@ -101,9 +101,9 @@
}
@Override
- public boolean setUidMode(int uid, int op, int mode) {
- boolean oldVal = mOldImplementation.setUidMode(uid, op, mode);
- boolean newVal = mNewImplementation.setUidMode(uid, op, mode);
+ public boolean setUidMode(int uid, String persistentDeviceId, int op, int mode) {
+ boolean oldVal = mOldImplementation.setUidMode(uid, persistentDeviceId, op, mode);
+ boolean newVal = mNewImplementation.setUidMode(uid, persistentDeviceId, op, mode);
if (oldVal != newVal) {
signalImplDifference("setUidMode");
@@ -155,9 +155,9 @@
}
@Override
- public SparseBooleanArray getForegroundOps(int uid) {
- SparseBooleanArray oldVal = mOldImplementation.getForegroundOps(uid);
- SparseBooleanArray newVal = mNewImplementation.getForegroundOps(uid);
+ public SparseBooleanArray getForegroundOps(int uid, String persistentDeviceId) {
+ SparseBooleanArray oldVal = mOldImplementation.getForegroundOps(uid, persistentDeviceId);
+ SparseBooleanArray newVal = mNewImplementation.getForegroundOps(uid, persistentDeviceId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("getForegroundOps");
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9701fc8..3f27ca0 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -18,6 +18,8 @@
import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED;
import static android.app.BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT;
+import static android.media.audio.Flags.autoPublicVolumeApiHardening;
+import static android.media.audio.Flags.focusFreezeTestApi;
import static android.media.AudioDeviceInfo.TYPE_BLE_HEADSET;
import static android.media.AudioDeviceInfo.TYPE_BLE_SPEAKER;
import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
@@ -157,6 +159,7 @@
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionCallback;
import android.media.projection.IMediaProjectionManager;
+import android.media.session.MediaSessionManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -314,6 +317,9 @@
private final ContentResolver mContentResolver;
private final AppOpsManager mAppOps;
+ /** do not use directly, use getMediaSessionManager() which handles lazy initialization */
+ @Nullable private volatile MediaSessionManager mMediaSessionManager;
+
// the platform type affects volume and silent mode behavior
private final int mPlatformType;
@@ -946,6 +952,8 @@
private final LoudnessCodecHelper mLoudnessCodecHelper;
+ private final HardeningEnforcer mHardeningEnforcer;
+
private final Object mSupportedSystemUsagesLock = new Object();
@GuardedBy("mSupportedSystemUsagesLock")
private @AttributeSystemUsage int[] mSupportedSystemUsages =
@@ -1322,6 +1330,8 @@
mDisplayManager = context.getSystemService(DisplayManager.class);
mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler);
+
+ mHardeningEnforcer = new HardeningEnforcer(mContext, isPlatformAutomotive());
}
private void initVolumeStreamStates() {
@@ -1393,7 +1403,6 @@
// check on volume initialization
checkVolumeRangeInitialization("AudioService()");
-
}
private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionChangedListener =
@@ -1406,6 +1415,14 @@
}
};
+ private MediaSessionManager getMediaSessionManager() {
+ if (mMediaSessionManager == null) {
+ mMediaSessionManager = (MediaSessionManager) mContext
+ .getSystemService(Context.MEDIA_SESSION_SERVICE);
+ }
+ return mMediaSessionManager;
+ }
+
/**
* Initialize intent receives and settings observers for this service.
* Must be called after createStreamStates() as the handling of some events
@@ -3434,6 +3451,10 @@
* Part of service interface, check permissions here */
public void adjustStreamVolumeWithAttribution(int streamType, int direction, int flags,
String callingPackage, String attributionTag) {
+ if (mHardeningEnforcer.blockVolumeMethod(
+ HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME)) {
+ return;
+ }
if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
Log.w(TAG, "Trying to call adjustStreamVolume() for a11y without"
+ "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
@@ -4214,6 +4235,10 @@
* Part of service interface, check permissions here */
public void setStreamVolumeWithAttribution(int streamType, int index, int flags,
String callingPackage, String attributionTag) {
+ if (mHardeningEnforcer.blockVolumeMethod(
+ HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME)) {
+ return;
+ }
setStreamVolumeWithAttributionInt(streamType, index, flags, /*device*/ null,
callingPackage, attributionTag, true /*canChangeMuteAndUpdateController*/);
}
@@ -4457,6 +4482,18 @@
null /* playbackConfigs */, configs /* recordConfigs */);
}
+ private void dumpFlags(PrintWriter pw) {
+ pw.println("\nFun with Flags: ");
+ pw.println("\tandroid.media.audio.Flags.autoPublicVolumeApiHardening:"
+ + autoPublicVolumeApiHardening());
+ pw.println("\tandroid.media.audio.Flags.focusFreezeTestApi:"
+ + focusFreezeTestApi());
+ pw.println("\tcom.android.media.audio.Flags.bluetoothMacAddressAnonymization:"
+ + bluetoothMacAddressAnonymization());
+ pw.println("\tcom.android.media.audio.Flags.disablePrescaleAbsoluteVolume:"
+ + disablePrescaleAbsoluteVolume());
+ }
+
private void dumpAudioMode(PrintWriter pw) {
pw.println("\nAudio mode: ");
pw.println("- Requested mode = " + AudioSystem.modeToString(getMode()));
@@ -4563,7 +4600,7 @@
}
private void setStreamVolume(int streamType, int index, int flags,
- @NonNull AudioDeviceAttributes ada,
+ @Nullable AudioDeviceAttributes ada,
String callingPackage, String caller, String attributionTag, int uid,
boolean hasModifyAudioSettings,
boolean canChangeMuteAndUpdateController) {
@@ -4581,7 +4618,9 @@
int streamTypeAlias = mStreamVolumeAlias[streamType];
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
- final int device = ada.getInternalType();
+ final int device = (ada == null)
+ ? getDeviceForStream(streamType)
+ : ada.getInternalType();
int oldIndex;
// skip a2dp absolute volume control request when the device
@@ -5081,6 +5120,7 @@
/** @see AudioManager#setMasterMute(boolean, int) */
public void setMasterMute(boolean mute, int flags, String callingPackage, int userId,
String attributionTag) {
+
super.setMasterMute_enforcePermission();
setMasterMuteInternal(mute, flags, callingPackage,
@@ -5450,6 +5490,10 @@
}
public void setRingerModeExternal(int ringerMode, String caller) {
+ if (mHardeningEnforcer.blockVolumeMethod(
+ HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_RINGER_MODE)) {
+ return;
+ }
if (isAndroidNPlus(caller) && wouldToggleZenMode(ringerMode)
&& !mNm.isNotificationPolicyAccessGrantedForPackage(caller)) {
throw new SecurityException("Not allowed to change Do Not Disturb state");
@@ -6202,6 +6246,35 @@
AudioDeviceVolumeManager.ADJUST_MODE_NORMAL);
}
+ /**
+ * @see AudioManager#adjustVolume(int, int)
+ * This method is redirected from AudioManager to AudioService for API hardening rules
+ * enforcement then to MediaSession for implementation.
+ */
+ @Override
+ public void adjustVolume(int direction, int flags) {
+ if (mHardeningEnforcer.blockVolumeMethod(
+ HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_VOLUME)) {
+ return;
+ }
+ getMediaSessionManager().dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
+ direction, flags);
+ }
+
+ /**
+ * @see AudioManager#adjustSuggestedStreamVolume(int, int, int)
+ * This method is redirected from AudioManager to AudioService for API hardening rules
+ * enforcement then to MediaSession for implementation.
+ */
+ @Override
+ public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
+ if (mHardeningEnforcer.blockVolumeMethod(
+ HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME)) {
+ return;
+ }
+ getMediaSessionManager().dispatchAdjustVolume(suggestedStreamType, direction, flags);
+ }
+
/** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */
@Override
public void setStreamVolumeForUid(int streamType, int index, int flags,
@@ -11405,6 +11478,7 @@
} else {
pw.println("\nMessage handler is null");
}
+ dumpFlags(pw);
mMediaFocusControl.dump(pw);
dumpStreamStates(pw);
dumpVolumeGroups(pw);
diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java
new file mode 100644
index 0000000..4ceb83b2
--- /dev/null
+++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java
@@ -0,0 +1,109 @@
+/*
+ * 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 com.android.server.audio;
+
+import static android.media.audio.Flags.autoPublicVolumeApiHardening;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.media.AudioManager;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * Class to encapsulate all audio API hardening operations
+ */
+public class HardeningEnforcer {
+
+ private static final String TAG = "AS.HardeningEnforcer";
+
+ final Context mContext;
+ final boolean mIsAutomotive;
+
+ /**
+ * Matches calls from {@link AudioManager#setStreamVolume(int, int, int)}
+ */
+ public static final int METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME = 100;
+ /**
+ * Matches calls from {@link AudioManager#adjustVolume(int, int)}
+ */
+ public static final int METHOD_AUDIO_MANAGER_ADJUST_VOLUME = 101;
+ /**
+ * Matches calls from {@link AudioManager#adjustSuggestedStreamVolume(int, int, int)}
+ */
+ public static final int METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME = 102;
+ /**
+ * Matches calls from {@link AudioManager#adjustStreamVolume(int, int, int)}
+ */
+ public static final int METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME = 103;
+ /**
+ * Matches calls from {@link AudioManager#setRingerMode(int)}
+ */
+ public static final int METHOD_AUDIO_MANAGER_SET_RINGER_MODE = 200;
+
+ public HardeningEnforcer(Context ctxt, boolean isAutomotive) {
+ mContext = ctxt;
+ mIsAutomotive = isAutomotive;
+ }
+
+ /**
+ * Checks whether the call in the current thread should be allowed or blocked
+ * @param volumeMethod name of the method to check, for logging purposes
+ * @return false if the method call is allowed, true if it should be a no-op
+ */
+ protected boolean blockVolumeMethod(int volumeMethod) {
+ // for Auto, volume methods require MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ if (mIsAutomotive) {
+ if (!autoPublicVolumeApiHardening()) {
+ // automotive hardening flag disabled, no blocking on auto
+ return false;
+ }
+ if (mContext.checkCallingOrSelfPermission(
+ Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ == PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ if (Binder.getCallingUid() < UserHandle.AID_APP_START) {
+ return false;
+ }
+ // TODO metrics?
+ // TODO log for audio dumpsys?
+ Log.e(TAG, "Preventing volume method " + volumeMethod + " for "
+ + getPackNameForUid(Binder.getCallingUid()));
+ return true;
+ }
+ // not blocking
+ return false;
+ }
+
+ private String getPackNameForUid(int uid) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final String[] names = mContext.getPackageManager().getPackagesForUid(uid);
+ if (names == null
+ || names.length == 0
+ || TextUtils.isEmpty(names[0])) {
+ return "[" + uid + "]";
+ }
+ return names[0];
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
index 3c67e9d..c306e3c 100644
--- a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
+++ b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
@@ -21,6 +21,11 @@
import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEARING_AID;
import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_WATCH;
import static android.media.AudioPlaybackConfiguration.PLAYER_DEVICEID_INVALID;
+import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_4;
+import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_D;
+import static android.media.MediaFormat.KEY_AAC_DRC_EFFECT_TYPE;
+import static android.media.MediaFormat.KEY_AAC_DRC_HEAVY_COMPRESSION;
+import static android.media.MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -41,6 +46,7 @@
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -70,10 +76,14 @@
private static final String SYSTEM_PROPERTY_SPEAKER_SPL_RANGE_SIZE =
"audio.loudness.builtin-speaker-spl-range-size";
- private static final int SPL_RANGE_UNKNOWN = 0;
- private static final int SPL_RANGE_SMALL = 1;
- private static final int SPL_RANGE_MEDIUM = 2;
- private static final int SPL_RANGE_LARGE = 3;
+ @VisibleForTesting
+ static final int SPL_RANGE_UNKNOWN = 0;
+ @VisibleForTesting
+ static final int SPL_RANGE_SMALL = 1;
+ @VisibleForTesting
+ static final int SPL_RANGE_MEDIUM = 2;
+ @VisibleForTesting
+ static final int SPL_RANGE_LARGE = 3;
/** The possible transducer SPL ranges as defined in CTA2075 */
@IntDef({
@@ -125,7 +135,8 @@
private final AudioService mAudioService;
/** Contains the properties necessary to compute the codec loudness related parameters. */
- private static final class LoudnessCodecInputProperties {
+ @VisibleForTesting
+ static final class LoudnessCodecInputProperties {
private final int mMetadataType;
private final boolean mIsDownmixing;
@@ -200,10 +211,53 @@
}
PersistableBundle createLoudnessParameters() {
- // TODO: create bundle with new parameters
- return new PersistableBundle();
- }
+ PersistableBundle loudnessParams = new PersistableBundle();
+ switch (mDeviceSplRange) {
+ case SPL_RANGE_LARGE:
+ // corresponds to -31dB attenuation
+ loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 124);
+ if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+ loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, 0);
+ } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+ // general compression
+ loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 6);
+ }
+ break;
+ case SPL_RANGE_MEDIUM:
+ // corresponds to -24dB attenuation
+ loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 96);
+ if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+ loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, mIsDownmixing ? 1 : 0);
+ } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+ // general compression
+ loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 6);
+ }
+ break;
+ case SPL_RANGE_SMALL:
+ // corresponds to -16dB attenuation
+ loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 64);
+ if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+ loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, 1);
+ } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+ // limited playback range compression
+ loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 3);
+ }
+ break;
+ default:
+ // corresponds to -24dB attenuation
+ loudnessParams.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 96);
+ if (mMetadataType == CODEC_METADATA_TYPE_MPEG_4) {
+ loudnessParams.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION, mIsDownmixing ? 1 : 0);
+ } else if (mMetadataType == CODEC_METADATA_TYPE_MPEG_D) {
+ // general compression
+ loudnessParams.putInt(KEY_AAC_DRC_EFFECT_TYPE, 6);
+ }
+ break;
+ }
+
+ return loudnessParams;
+ }
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index be78ea2..cecde55 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -149,6 +149,14 @@
public abstract @NonNull ArraySet<Integer> getDisplayIdsForDevice(int deviceId);
/**
+ * Returns the ID of the device which owns the display with the given ID.
+ *
+ * <p>In case the virtual display ID is invalid or doesn't belong to a virtual device, then
+ * {@link android.content.Context#DEVICE_ID_DEFAULT} is returned.</p>
+ */
+ public abstract int getDeviceIdForDisplayId(int displayId);
+
+ /**
* Gets the persistent ID for the VirtualDevice with the given device ID.
*
* @param deviceId which device we're asking about
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
index af33de0..50ab3f8 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
@@ -63,13 +63,27 @@
*/
int SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED = 5;
+ /**
+ * Indicating that the supported device states have changed because an external display was
+ * added.
+ */
+ int SUPPORTED_DEVICE_STATES_CHANGED_EXTERNAL_DISPLAY_ADDED = 6;
+
+ /**
+ * Indicating that the supported device states have changed because an external display was
+ * removed.
+ */
+ int SUPPORTED_DEVICE_STATES_CHANGED_EXTERNAL_DISPLAY_REMOVED = 7;
+
@IntDef(prefix = { "SUPPORTED_DEVICE_STATES_CHANGED_" }, value = {
SUPPORTED_DEVICE_STATES_CHANGED_DEFAULT,
SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED,
SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL,
SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL,
SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED,
- SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED
+ SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED,
+ SUPPORTED_DEVICE_STATES_CHANGED_EXTERNAL_DISPLAY_ADDED,
+ SUPPORTED_DEVICE_STATES_CHANGED_EXTERNAL_DISPLAY_REMOVED
})
@Retention(RetentionPolicy.SOURCE)
@interface SupportedStatesUpdatedReason {}
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index debf828..d848f4b 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -121,6 +121,7 @@
// Display independent, mode dependent values
float[] brightnessLevelsNits;
+ float[] brightnessLevels = null;
float[] luxLevels;
if (isForIdleMode) {
brightnessLevelsNits = getFloatArray(resources.obtainTypedArray(
@@ -130,11 +131,21 @@
} else {
brightnessLevelsNits = displayDeviceConfig.getAutoBrightnessBrighteningLevelsNits();
luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux();
+
+ brightnessLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevels();
+ if (brightnessLevels == null || brightnessLevels.length == 0) {
+ // Load the old configuration in the range [0, 255]. The values need to be
+ // normalized to the range [0, 1].
+ int[] brightnessLevelsInt = resources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
+ brightnessLevels = new float[brightnessLevelsInt.length];
+ for (int i = 0; i < brightnessLevels.length; i++) {
+ brightnessLevels[i] = normalizeAbsoluteBrightness(brightnessLevelsInt[i]);
+ }
+ }
}
// Display independent, mode independent values
- int[] brightnessLevelsBacklight = resources.getIntArray(
- com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
float autoBrightnessAdjustmentMaxGamma = resources.getFraction(
com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
1, 1);
@@ -155,8 +166,8 @@
builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
return new PhysicalMappingStrategy(builder.build(), nitsRange, brightnessRange,
autoBrightnessAdjustmentMaxGamma, isForIdleMode, displayWhiteBalanceController);
- } else if (isValidMapping(luxLevels, brightnessLevelsBacklight) && !isForIdleMode) {
- return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight,
+ } else if (isValidMapping(luxLevels, brightnessLevels)) {
+ return new SimpleMappingStrategy(luxLevels, brightnessLevels,
autoBrightnessAdjustmentMaxGamma, shortTermModelTimeout);
} else {
return null;
@@ -620,7 +631,7 @@
private float mUserBrightness;
private long mShortTermModelTimeout;
- private SimpleMappingStrategy(float[] lux, int[] brightness, float maxGamma,
+ private SimpleMappingStrategy(float[] lux, float[] brightness, float maxGamma,
long timeout) {
Preconditions.checkArgument(lux.length != 0 && brightness.length != 0,
"Lux and brightness arrays must not be empty!");
@@ -635,7 +646,7 @@
mBrightness = new float[N];
for (int i = 0; i < N; i++) {
mLux[i] = lux[i];
- mBrightness[i] = normalizeAbsoluteBrightness(brightness[i]);
+ mBrightness[i] = brightness[i];
}
mMaxGamma = maxGamma;
diff --git a/services/core/java/com/android/server/display/DisplayAdapter.java b/services/core/java/com/android/server/display/DisplayAdapter.java
index 70d4ad2..c26118e 100644
--- a/services/core/java/com/android/server/display/DisplayAdapter.java
+++ b/services/core/java/com/android/server/display/DisplayAdapter.java
@@ -20,6 +20,8 @@
import android.os.Handler;
import android.view.Display;
+import com.android.server.display.feature.DisplayManagerFlags;
+
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicInteger;
@@ -39,6 +41,7 @@
private final Handler mHandler;
private final Listener mListener;
private final String mName;
+ private final DisplayManagerFlags mFeatureFlags;
public static final int DISPLAY_DEVICE_EVENT_ADDED = 1;
public static final int DISPLAY_DEVICE_EVENT_CHANGED = 2;
@@ -50,13 +53,14 @@
private static final AtomicInteger NEXT_DISPLAY_MODE_ID = new AtomicInteger(1); // 0 = no mode.
// Called with SyncRoot lock held.
- public DisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
- Context context, Handler handler, Listener listener, String name) {
+ DisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler,
+ Listener listener, String name, DisplayManagerFlags featureFlags) {
mSyncRoot = syncRoot;
mContext = context;
mHandler = handler;
mListener = listener;
mName = name;
+ mFeatureFlags = featureFlags;
}
/**
@@ -88,6 +92,10 @@
return mName;
}
+ public final DisplayManagerFlags getFeatureFlags() {
+ return mFeatureFlags;
+ }
+
/**
* Registers the display adapter with the display manager.
*
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 2fdf90d..3b05b47 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -400,6 +400,7 @@
}
private DisplayDeviceConfig loadDisplayDeviceConfig() {
- return DisplayDeviceConfig.create(mContext, false);
+ return DisplayDeviceConfig.create(mContext, /* useConfigXml= */ false,
+ mDisplayAdapter.getFeatureFlags());
}
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index b99de5c..d97127c 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -57,6 +57,7 @@
import com.android.server.display.config.HighBrightnessMode;
import com.android.server.display.config.IntegerArray;
import com.android.server.display.config.LuxThrottling;
+import com.android.server.display.config.LuxToBrightnessMapping;
import com.android.server.display.config.NitsMap;
import com.android.server.display.config.NonNegativeFloatToFloatPoint;
import com.android.server.display.config.Point;
@@ -77,6 +78,7 @@
import com.android.server.display.config.ThresholdPoint;
import com.android.server.display.config.UsiVersion;
import com.android.server.display.config.XmlParser;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.utils.DebugUtils;
import org.xmlpull.v1.XmlPullParserException;
@@ -310,16 +312,18 @@
* <darkeningLightDebounceIdleMillis>
* 1000
* </darkeningLightDebounceIdleMillis>
- * <displayBrightnessMapping>
- * <displayBrightnessPoint>
- * <lux>50</lux>
- * <nits>45.32</nits>
- * </displayBrightnessPoint>
- * <displayBrightnessPoint>
- * <lux>80</lux>
- * <nits>75.43</nits>
- * </displayBrightnessPoint>
- * </displayBrightnessMapping>
+ * <luxToBrightnessMapping>
+ * <map>
+ * <point>
+ * <first>0</first>
+ * <second>0.2</second>
+ * </point>
+ * <point>
+ * <first>80</first>
+ * <second>0.3</second>
+ * </point>
+ * </map>
+ * </luxToBrightnessMapping>
* </autoBrightness>
*
* <screenBrightnessRampFastDecrease>0.01</screenBrightnessRampFastDecrease>
@@ -528,6 +532,7 @@
* <majorVersion>2</majorVersion>
* <minorVersion>0</minorVersion>
* </usiVersion>
+ * <screenBrightnessCapForWearBedtimeMode>0.1</screenBrightnessCapForWearBedtimeMode>
* </displayConfiguration>
* }
* </pre>
@@ -629,7 +634,6 @@
// for the corresponding values above
private float[] mBrightness;
-
/**
* Array of desired screen brightness in nits corresponding to the lux values
* in the mBrightnessLevelsLux array. The display brightness is defined as the
@@ -639,20 +643,25 @@
private float[] mBrightnessLevelsNits;
/**
- * Array of light sensor lux values to define our levels for auto backlight
- * brightness support.
+ * Array of desired screen brightness corresponding to the lux values
+ * in the mBrightnessLevelsLux array. The brightness values must be non-negative and
+ * non-decreasing. They must be between {@link PowerManager.BRIGHTNESS_MIN} and
+ * {@link PowerManager.BRIGHTNESS_MAX}. This must be overridden in platform specific overlays
+ */
+ private float[] mBrightnessLevels;
+
+ /**
+ * Array of light sensor lux values to define our levels for auto-brightness support.
*
- * The N + 1 entries of this array define N control points defined in mBrightnessLevelsNits,
- * with first value always being 0 lux
+ * The first lux value is always 0.
*
- * The control points must be strictly increasing. Each control point
- * corresponds to an entry in the brightness backlight values arrays.
- * For example, if lux == level[1] (second element of the levels array)
- * then the brightness will be determined by value[0] (first element
- * of the brightness values array).
+ * The control points must be strictly increasing. Each control point corresponds to an entry
+ * in the brightness values arrays. For example, if lux == luxLevels[1] (second element
+ * of the levels array) then the brightness will be determined by brightnessLevels[1] (second
+ * element of the brightness values array).
*
- * Spline interpolation is used to determine the auto-brightness
- * backlight values for lux levels between these control points.
+ * Spline interpolation is used to determine the auto-brightness values for lux levels between
+ * these control points.
*/
private float[] mBrightnessLevelsLux;
@@ -843,9 +852,17 @@
@Nullable
private HdrBrightnessData mHdrBrightnessData;
+ /**
+ * Maximum screen brightness setting when screen brightness capped in Wear Bedtime mode.
+ */
+ private float mBrightnessCapForWearBedtimeMode;
+
+ private final DisplayManagerFlags mFlags;
+
@VisibleForTesting
- DisplayDeviceConfig(Context context) {
+ DisplayDeviceConfig(Context context, DisplayManagerFlags flags) {
mContext = context;
+ mFlags = flags;
}
/**
@@ -861,9 +878,9 @@
* @return A configuration instance for the specified display.
*/
public static DisplayDeviceConfig create(Context context, long physicalDisplayId,
- boolean isFirstDisplay) {
+ boolean isFirstDisplay, DisplayManagerFlags flags) {
final DisplayDeviceConfig config = createWithoutDefaultValues(context, physicalDisplayId,
- isFirstDisplay);
+ isFirstDisplay, flags);
config.copyUninitializedValuesFromSecondaryConfig(loadDefaultConfigurationXml(context));
return config;
@@ -878,28 +895,29 @@
* or the default values.
* @return A configuration instance.
*/
- public static DisplayDeviceConfig create(Context context, boolean useConfigXml) {
+ public static DisplayDeviceConfig create(Context context, boolean useConfigXml,
+ DisplayManagerFlags flags) {
final DisplayDeviceConfig config;
if (useConfigXml) {
- config = getConfigFromGlobalXml(context);
+ config = getConfigFromGlobalXml(context, flags);
} else {
- config = getConfigFromPmValues(context);
+ config = getConfigFromPmValues(context, flags);
}
return config;
}
private static DisplayDeviceConfig createWithoutDefaultValues(Context context,
- long physicalDisplayId, boolean isFirstDisplay) {
+ long physicalDisplayId, boolean isFirstDisplay, DisplayManagerFlags flags) {
DisplayDeviceConfig config;
config = loadConfigFromDirectory(context, Environment.getProductDirectory(),
- physicalDisplayId);
+ physicalDisplayId, flags);
if (config != null) {
return config;
}
config = loadConfigFromDirectory(context, Environment.getVendorDirectory(),
- physicalDisplayId);
+ physicalDisplayId, flags);
if (config != null) {
return config;
}
@@ -907,7 +925,7 @@
// If no config can be loaded from any ddc xml at all,
// prepare a whole config using the global config.xml.
// Guaranteed not null
- return create(context, isFirstDisplay);
+ return create(context, isFirstDisplay, flags);
}
private static DisplayConfiguration loadDefaultConfigurationXml(Context context) {
@@ -960,18 +978,19 @@
}
private static DisplayDeviceConfig loadConfigFromDirectory(Context context,
- File baseDirectory, long physicalDisplayId) {
+ File baseDirectory, long physicalDisplayId, DisplayManagerFlags flags) {
DisplayDeviceConfig config;
// Create config using filename from physical ID (including "stable" bit).
config = getConfigFromSuffix(context, baseDirectory, STABLE_ID_SUFFIX_FORMAT,
- physicalDisplayId);
+ physicalDisplayId, flags);
if (config != null) {
return config;
}
// Create config using filename from physical ID (excluding "stable" bit).
final long withoutStableFlag = physicalDisplayId & ~STABLE_FLAG;
- config = getConfigFromSuffix(context, baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag);
+ config = getConfigFromSuffix(context, baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag,
+ flags);
if (config != null) {
return config;
}
@@ -980,7 +999,7 @@
final DisplayAddress.Physical physicalAddress =
DisplayAddress.fromPhysicalDisplayId(physicalDisplayId);
int port = physicalAddress.getPort();
- config = getConfigFromSuffix(context, baseDirectory, PORT_SUFFIX_FORMAT, port);
+ config = getConfigFromSuffix(context, baseDirectory, PORT_SUFFIX_FORMAT, port, flags);
return config;
}
@@ -1599,6 +1618,13 @@
}
/**
+ * @return Auto brightness brightening levels
+ */
+ public float[] getAutoBrightnessBrighteningLevels() {
+ return mBrightnessLevels;
+ }
+
+ /**
* @return Default peak refresh rate of the associated display
*/
public int getDefaultPeakRefreshRate() {
@@ -1741,6 +1767,13 @@
return mHostUsiVersion;
}
+ /**
+ * @return Maximum screen brightness setting when screen brightness capped in Wear Bedtime mode.
+ */
+ public float getBrightnessCapForWearBedtimeMode() {
+ return mBrightnessCapForWearBedtimeMode;
+ }
+
@Override
public String toString() {
return "DisplayDeviceConfig{"
@@ -1844,6 +1877,7 @@
+ mAutoBrightnessDarkeningLightDebounceIdle
+ ", mBrightnessLevelsLux= " + Arrays.toString(mBrightnessLevelsLux)
+ ", mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits)
+ + ", mBrightnessLevels= " + Arrays.toString(mBrightnessLevels)
+ ", mDdcAutoBrightnessAvailable= " + mDdcAutoBrightnessAvailable
+ ", mAutoBrightnessAvailable= " + mAutoBrightnessAvailable
+ "\n"
@@ -1867,36 +1901,39 @@
+ ", mHighAmbientBrightnessThresholds= "
+ Arrays.toString(mHighAmbientBrightnessThresholds)
+ "\n"
- + "mScreenOffBrightnessSensorValueToLux=" + Arrays.toString(
+ + "mScreenOffBrightnessSensorValueToLux= " + Arrays.toString(
mScreenOffBrightnessSensorValueToLux)
+ "\n"
+ "mUsiVersion= " + mHostUsiVersion + "\n"
- + "mHdrBrightnessData" + mHdrBrightnessData
+ + "mHdrBrightnessData= " + mHdrBrightnessData + "\n"
+ + "mBrightnessCapForWearBedtimeMode= " + mBrightnessCapForWearBedtimeMode
+ "}";
}
private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory,
- String suffixFormat, long idNumber) {
+ String suffixFormat, long idNumber, DisplayManagerFlags flags) {
final String suffix = String.format(Locale.ROOT, suffixFormat, idNumber);
final String filename = String.format(Locale.ROOT, CONFIG_FILE_FORMAT, suffix);
final File filePath = Environment.buildPath(
baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename);
- final DisplayDeviceConfig config = new DisplayDeviceConfig(context);
+ final DisplayDeviceConfig config = new DisplayDeviceConfig(context, flags);
if (config.initFromFile(filePath)) {
return config;
}
return null;
}
- private static DisplayDeviceConfig getConfigFromGlobalXml(Context context) {
- DisplayDeviceConfig config = new DisplayDeviceConfig(context);
+ private static DisplayDeviceConfig getConfigFromGlobalXml(Context context,
+ DisplayManagerFlags flags) {
+ DisplayDeviceConfig config = new DisplayDeviceConfig(context, flags);
config.initFromGlobalXml();
return config;
}
- private static DisplayDeviceConfig getConfigFromPmValues(Context context) {
- DisplayDeviceConfig config = new DisplayDeviceConfig(context);
+ private static DisplayDeviceConfig getConfigFromPmValues(Context context,
+ DisplayManagerFlags flags) {
+ DisplayDeviceConfig config = new DisplayDeviceConfig(context, flags);
config.initFromDefaultValues();
return config;
}
@@ -1938,6 +1975,7 @@
loadScreenOffBrightnessSensorValueToLuxFromDdc(config);
loadUsiVersion(config);
mHdrBrightnessData = HdrBrightnessData.loadConfig(config);
+ loadBrightnessCapForWearBedtimeMode(config);
} else {
Slog.w(TAG, "DisplayDeviceConfig file is null");
}
@@ -1961,6 +1999,7 @@
loadAutoBrightnessConfigsFromConfigXml();
loadAutoBrightnessAvailableFromConfigXml();
loadRefreshRateSetting(null);
+ loadBrightnessCapForWearBedtimeModeFromConfigXml();
mLoadedFrom = "<config.xml>";
}
@@ -2599,8 +2638,23 @@
* loading the value from the display config, and if not present, falls back to config.xml.
*/
private void loadAutoBrightnessDisplayBrightnessMapping(AutoBrightness autoBrightnessConfig) {
- if (autoBrightnessConfig == null
- || autoBrightnessConfig.getDisplayBrightnessMapping() == null) {
+ if (mFlags.areAutoBrightnessModesEnabled() && autoBrightnessConfig != null
+ && autoBrightnessConfig.getLuxToBrightnessMapping() != null) {
+ LuxToBrightnessMapping mapping = autoBrightnessConfig.getLuxToBrightnessMapping();
+ final int size = mapping.getMap().getPoint().size();
+ mBrightnessLevels = new float[size];
+ mBrightnessLevelsLux = new float[size];
+ for (int i = 0; i < size; i++) {
+ float backlight = mapping.getMap().getPoint().get(i).getSecond().floatValue();
+ mBrightnessLevels[i] = mBacklightToBrightnessSpline.interpolate(backlight);
+ mBrightnessLevelsLux[i] = mapping.getMap().getPoint().get(i).getFirst()
+ .floatValue();
+ }
+ if (size > 0 && mBrightnessLevelsLux[0] != 0) {
+ throw new IllegalArgumentException(
+ "The first lux value in the display brightness mapping must be 0");
+ }
+ } else {
mBrightnessLevelsNits = getFloatArray(mContext.getResources()
.obtainTypedArray(com.android.internal.R.array
.config_autoBrightnessDisplayValuesNits), PowerManager
@@ -2608,18 +2662,6 @@
mBrightnessLevelsLux = getLuxLevels(mContext.getResources()
.getIntArray(com.android.internal.R.array
.config_autoBrightnessLevels));
- } else {
- final int size = autoBrightnessConfig.getDisplayBrightnessMapping()
- .getDisplayBrightnessPoint().size();
- mBrightnessLevelsNits = new float[size];
- // The first control point is implicit and always at 0 lux.
- mBrightnessLevelsLux = new float[size + 1];
- for (int i = 0; i < size; i++) {
- mBrightnessLevelsNits[i] = autoBrightnessConfig.getDisplayBrightnessMapping()
- .getDisplayBrightnessPoint().get(i).getNits().floatValue();
- mBrightnessLevelsLux[i + 1] = autoBrightnessConfig.getDisplayBrightnessMapping()
- .getDisplayBrightnessPoint().get(i).getLux().floatValue();
- }
}
}
@@ -3350,6 +3392,23 @@
: null;
}
+ private void loadBrightnessCapForWearBedtimeMode(DisplayConfiguration config) {
+ if (config != null) {
+ BigDecimal configBrightnessCap = config.getScreenBrightnessCapForWearBedtimeMode();
+ if (configBrightnessCap != null) {
+ mBrightnessCapForWearBedtimeMode = configBrightnessCap.floatValue();
+ } else {
+ loadBrightnessCapForWearBedtimeModeFromConfigXml();
+ }
+ }
+ }
+
+ private void loadBrightnessCapForWearBedtimeModeFromConfigXml() {
+ mBrightnessCapForWearBedtimeMode = BrightnessSynchronizer.brightnessIntToFloat(
+ mContext.getResources().getInteger(com.android.internal.R.integer
+ .config_screenBrightnessCapForWearBedtimeMode));
+ }
+
/**
* Container for high brightness mode configuration data.
*/
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 6a7c17d..2ab15e6 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1853,7 +1853,7 @@
// early apps like SetupWizard/Launcher. In particular, SUW is displayed using
// the virtual display inside VR before any VR-specific apps even run.
mVirtualDisplayAdapter = mInjector.getVirtualDisplayAdapter(mSyncRoot, mContext,
- mHandler, mDisplayDeviceRepo);
+ mHandler, mDisplayDeviceRepo, mFlags);
if (mVirtualDisplayAdapter != null) {
registerDisplayAdapterLocked(mVirtualDisplayAdapter);
}
@@ -1871,7 +1871,7 @@
private void registerOverlayDisplayAdapterLocked() {
registerDisplayAdapterLocked(new OverlayDisplayAdapter(
- mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mUiHandler));
+ mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mUiHandler, mFlags));
}
private void registerWifiDisplayAdapterLocked() {
@@ -1880,7 +1880,7 @@
|| SystemProperties.getInt(FORCE_WIFI_DISPLAY_ENABLE, -1) == 1) {
mWifiDisplayAdapter = new WifiDisplayAdapter(
mSyncRoot, mContext, mHandler, mDisplayDeviceRepo,
- mPersistentDataStore);
+ mPersistentDataStore, mFlags);
registerDisplayAdapterLocked(mWifiDisplayAdapter);
}
}
@@ -3288,8 +3288,10 @@
@VisibleForTesting
static class Injector {
VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler, DisplayAdapter.Listener displayAdapterListener) {
- return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener);
+ Handler handler, DisplayAdapter.Listener displayAdapterListener,
+ DisplayManagerFlags flags) {
+ return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+ flags);
}
LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index ff9a1ab..22898a6 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -84,8 +84,6 @@
private final boolean mIsBootDisplayModeSupported;
- private final DisplayManagerFlags mFlags;
-
private final DisplayNotificationManager mDisplayNotificationManager;
private Context mOverlayContext;
@@ -103,12 +101,11 @@
Listener listener, DisplayManagerFlags flags,
DisplayNotificationManager displayNotificationManager,
Injector injector) {
- super(syncRoot, context, handler, listener, TAG);
+ super(syncRoot, context, handler, listener, TAG, flags);
mDisplayNotificationManager = displayNotificationManager;
mInjector = injector;
mSurfaceControlProxy = mInjector.getSurfaceControlProxy();
mIsBootDisplayModeSupported = mSurfaceControlProxy.getBootDisplayModeSupport();
- mFlags = flags;
}
@Override
@@ -510,7 +507,7 @@
// Load display device config
final Context context = getOverlayContext();
mDisplayDeviceConfig = mInjector.createDisplayDeviceConfig(context, mPhysicalDisplayId,
- mIsFirstDisplay);
+ mIsFirstDisplay, getFeatureFlags());
// Load brightness HWC quirk
mBacklightAdapter.setForceSurfaceControl(mDisplayDeviceConfig.hasQuirk(
@@ -831,7 +828,8 @@
+ ", state=" + Display.stateToString(state) + ")");
}
- boolean isDisplayOffloadEnabled = mFlags.isDisplayOffloadEnabled();
+ boolean isDisplayOffloadEnabled =
+ getFeatureFlags().isDisplayOffloadEnabled();
// We must tell sidekick/displayoffload to stop controlling the display
// before we can change its power mode, so do that first.
@@ -1377,8 +1375,8 @@
}
public DisplayDeviceConfig createDisplayDeviceConfig(Context context,
- long physicalDisplayId, boolean isFirstDisplay) {
- return DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay);
+ long physicalDisplayId, boolean isFirstDisplay, DisplayManagerFlags flags) {
+ return DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay, flags);
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index ba321ae..db636d6 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -17,6 +17,8 @@
package com.android.server.display;
import static com.android.server.display.DisplayDeviceInfo.TOUCH_NONE;
+import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELDS;
+import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -33,6 +35,7 @@
import com.android.server.display.layout.Layout;
import com.android.server.display.mode.DisplayModeDirector;
+import com.android.server.wm.utils.DisplayInfoOverrides;
import com.android.server.wm.utils.InsetUtils;
import java.io.PrintWriter;
@@ -252,24 +255,8 @@
public DisplayInfo getDisplayInfoLocked() {
if (mInfo.get() == null) {
DisplayInfo info = new DisplayInfo();
- info.copyFrom(mBaseDisplayInfo);
- if (mOverrideDisplayInfo != null) {
- info.appWidth = mOverrideDisplayInfo.appWidth;
- info.appHeight = mOverrideDisplayInfo.appHeight;
- info.smallestNominalAppWidth = mOverrideDisplayInfo.smallestNominalAppWidth;
- info.smallestNominalAppHeight = mOverrideDisplayInfo.smallestNominalAppHeight;
- info.largestNominalAppWidth = mOverrideDisplayInfo.largestNominalAppWidth;
- info.largestNominalAppHeight = mOverrideDisplayInfo.largestNominalAppHeight;
- info.logicalWidth = mOverrideDisplayInfo.logicalWidth;
- info.logicalHeight = mOverrideDisplayInfo.logicalHeight;
- info.physicalXDpi = mOverrideDisplayInfo.physicalXDpi;
- info.physicalYDpi = mOverrideDisplayInfo.physicalYDpi;
- info.rotation = mOverrideDisplayInfo.rotation;
- info.displayCutout = mOverrideDisplayInfo.displayCutout;
- info.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi;
- info.roundedCorners = mOverrideDisplayInfo.roundedCorners;
- info.displayShape = mOverrideDisplayInfo.displayShape;
- }
+ copyDisplayInfoFields(info, mBaseDisplayInfo, mOverrideDisplayInfo,
+ WM_OVERRIDE_FIELDS);
mInfo.set(info);
}
return mInfo.get();
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index 2ce7690..22ff2d0 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -36,6 +36,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.mode.DisplayModeDirector;
import java.io.PrintWriter;
@@ -134,8 +135,9 @@
// Called with SyncRoot lock held.
public OverlayDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
- Context context, Handler handler, Listener listener, Handler uiHandler) {
- super(syncRoot, context, handler, listener, TAG);
+ Context context, Handler handler, Listener listener, Handler uiHandler,
+ DisplayManagerFlags featureFlags) {
+ super(syncRoot, context, handler, listener, TAG, featureFlags);
mUiHandler = uiHandler;
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index edbd424..ec5ad7d 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -61,6 +61,7 @@
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.feature.DisplayManagerFlags;
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicInteger;
@@ -88,7 +89,7 @@
// Called with SyncRoot lock held.
public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
- Context context, Handler handler, Listener listener) {
+ Context context, Handler handler, Listener listener, DisplayManagerFlags featureFlags) {
this(syncRoot, context, handler, listener, new SurfaceControlDisplayFactory() {
@Override
public IBinder createDisplay(String name, boolean secure, float requestedRefreshRate) {
@@ -99,14 +100,15 @@
public void destroyDisplay(IBinder displayToken) {
DisplayControl.destroyDisplay(displayToken);
}
- });
+ }, featureFlags);
}
@VisibleForTesting
VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener,
- SurfaceControlDisplayFactory surfaceControlDisplayFactory) {
- super(syncRoot, context, handler, listener, TAG);
+ SurfaceControlDisplayFactory surfaceControlDisplayFactory,
+ DisplayManagerFlags featureFlags) {
+ super(syncRoot, context, handler, listener, TAG, featureFlags);
mHandler = handler;
mSurfaceControlDisplayFactory = surfaceControlDisplayFactory;
}
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index 7660cf8..aa98cd8 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -41,6 +41,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.utils.DebugUtils;
import java.io.PrintWriter;
@@ -99,8 +100,8 @@
// Called with SyncRoot lock held.
public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener,
- PersistentDataStore persistentDataStore) {
- super(syncRoot, context, handler, listener, TAG);
+ PersistentDataStore persistentDataStore, DisplayManagerFlags featureFlags) {
+ super(syncRoot, context, handler, listener, TAG, featureFlags);
if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)) {
throw new RuntimeException("WiFi display was requested, "
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
index dfcda40..8a884b6 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
@@ -17,6 +17,7 @@
package com.android.server.display.brightness.clamper;
import android.annotation.NonNull;
+import android.os.Handler;
import android.os.PowerManager;
import com.android.server.display.DisplayBrightnessState;
@@ -31,6 +32,18 @@
protected float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
protected boolean mIsActive = false;
+ @NonNull
+ protected final Handler mHandler;
+
+ @NonNull
+ protected final BrightnessClamperController.ClamperChangeListener mChangeListener;
+
+ BrightnessClamper(Handler handler,
+ BrightnessClamperController.ClamperChangeListener changeListener) {
+ mHandler = handler;
+ mChangeListener = changeListener;
+ }
+
float getBrightnessCap() {
return mBrightnessCap;
}
@@ -60,6 +73,7 @@
protected enum Type {
THERMAL,
- POWER
+ POWER,
+ BEDTIME_MODE
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index b574919..765608e 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -90,7 +90,8 @@
}
};
- mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags);
+ mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags,
+ context);
mModifiers = injector.getModifiers(context);
mOnPropertiesChangedListener =
properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
@@ -234,7 +235,7 @@
List<BrightnessClamper<? super DisplayDeviceData>> getClampers(Handler handler,
ClamperChangeListener clamperChangeListener, DisplayDeviceData data,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags, Context context) {
List<BrightnessClamper<? super DisplayDeviceData>> clampers = new ArrayList<>();
clampers.add(
new BrightnessThermalClamper(handler, clamperChangeListener, data));
@@ -242,6 +243,10 @@
clampers.add(new BrightnessPowerClamper(handler, clamperChangeListener,
data));
}
+ if (flags.isBrightnessWearBedtimeModeClamperEnabled()) {
+ clampers.add(new BrightnessWearBedtimeModeClamper(handler, context,
+ clamperChangeListener, data));
+ }
return clampers;
}
@@ -257,7 +262,8 @@
* Config Data for clampers
*/
public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData,
- BrightnessPowerClamper.PowerData {
+ BrightnessPowerClamper.PowerData,
+ BrightnessWearBedtimeModeClamper.WearBedtimeModeData {
@NonNull
private final String mUniqueDisplayId;
@NonNull
@@ -315,5 +321,10 @@
public PowerThrottlingConfigData getPowerThrottlingConfigData() {
return mDisplayDeviceConfig.getPowerThrottlingConfigData();
}
+
+ @Override
+ public float getBrightnessWearBedtimeModeCap() {
+ return mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode();
+ }
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
index 339b589..790322d 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessPowerClamper.java
@@ -49,10 +49,6 @@
private final Injector mInjector;
@NonNull
private final DeviceConfigParameterProvider mConfigParameterProvider;
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final ClamperChangeListener mChangeListener;
@Nullable
private PmicMonitor mPmicMonitor;
// data from DeviceConfig, for all displays, for all dataSets
@@ -99,10 +95,9 @@
@VisibleForTesting
BrightnessPowerClamper(Injector injector, Handler handler, ClamperChangeListener listener,
PowerData powerData) {
+ super(handler, listener);
mInjector = injector;
mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
- mHandler = handler;
- mChangeListener = listener;
mHandler.post(() -> {
setDisplayData(powerData);
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
index 8ae962b..944a8a6 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
@@ -54,10 +54,6 @@
private final IThermalService mThermalService;
@NonNull
private final DeviceConfigParameterProvider mConfigParameterProvider;
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final ClamperChangeListener mChangelistener;
// data from DeviceConfig, for all displays, for all dataSets
// mapOf(uniqueDisplayId to mapOf(dataSetId to ThermalBrightnessThrottlingData))
@NonNull
@@ -108,10 +104,9 @@
@VisibleForTesting
BrightnessThermalClamper(Injector injector, Handler handler,
ClamperChangeListener listener, ThermalData thermalData) {
+ super(handler, listener);
mThermalService = injector.getThermalService();
mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
- mHandler = handler;
- mChangelistener = listener;
mHandler.post(() -> {
setDisplayData(thermalData);
loadOverrideData();
@@ -220,7 +215,7 @@
if (brightnessCap != mBrightnessCap || mIsActive != isActive) {
mBrightnessCap = brightnessCap;
mIsActive = isActive;
- mChangelistener.onChanged();
+ mChangeListener.onChanged();
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
new file mode 100644
index 0000000..7e853bf
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.clamper;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+public class BrightnessWearBedtimeModeClamper extends
+ BrightnessClamper<BrightnessWearBedtimeModeClamper.WearBedtimeModeData> {
+
+ public static final int BEDTIME_MODE_OFF = 0;
+ public static final int BEDTIME_MODE_ON = 1;
+
+ private final Context mContext;
+
+ private final ContentObserver mSettingsObserver;
+
+ BrightnessWearBedtimeModeClamper(Handler handler, Context context,
+ BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) {
+ this(new Injector(), handler, context, listener, data);
+ }
+
+ @VisibleForTesting
+ BrightnessWearBedtimeModeClamper(Injector injector, Handler handler, Context context,
+ BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) {
+ super(handler, listener);
+ mContext = context;
+ mBrightnessCap = data.getBrightnessWearBedtimeModeCap();
+ mSettingsObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ final int bedtimeModeSetting = Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.Wearable.BEDTIME_MODE,
+ BEDTIME_MODE_OFF);
+ mIsActive = bedtimeModeSetting == BEDTIME_MODE_ON;
+ mChangeListener.onChanged();
+ }
+ };
+ injector.registerBedtimeModeObserver(context.getContentResolver(), mSettingsObserver);
+ }
+
+ @NonNull
+ @Override
+ Type getType() {
+ return Type.BEDTIME_MODE;
+ }
+
+ @Override
+ void onDeviceConfigChanged() {}
+
+ @Override
+ void onDisplayChanged(WearBedtimeModeData displayData) {
+ mHandler.post(() -> {
+ mBrightnessCap = displayData.getBrightnessWearBedtimeModeCap();
+ mChangeListener.onChanged();
+ });
+ }
+
+ @Override
+ void stop() {
+ mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
+ }
+
+ interface WearBedtimeModeData {
+ float getBrightnessWearBedtimeModeCap();
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ void registerBedtimeModeObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ cr.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE),
+ /* notifyForDescendants= */ false, observer, UserHandle.USER_ALL);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index aa80612..c71f0cf2 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -86,6 +86,18 @@
Flags.FLAG_BRIGHTNESS_INT_RANGE_USER_PERCEPTION,
Flags::brightnessIntRangeUserPerception);
+ private final FlagState mVsyncProximityVote = new FlagState(
+ Flags.FLAG_ENABLE_EXTERNAL_VSYNC_PROXIMITY_VOTE,
+ Flags::enableExternalVsyncProximityVote);
+
+ private final FlagState mBrightnessWearBedtimeModeClamperFlagState = new FlagState(
+ Flags.FLAG_BRIGHTNESS_WEAR_BEDTIME_MODE_CLAMPER,
+ Flags::brightnessWearBedtimeModeClamper);
+
+ private final FlagState mAutoBrightnessModesFlagState = new FlagState(
+ Flags.FLAG_AUTO_BRIGHTNESS_MODES,
+ Flags::autoBrightnessModes);
+
/** Returns whether connected display management is enabled or not. */
public boolean isConnectedDisplayManagementEnabled() {
return mConnectedDisplayManagementFlagState.isEnabled();
@@ -170,6 +182,21 @@
return mBrightnessIntRangeUserPerceptionFlagState.isEnabled();
}
+ public boolean isExternalVsyncProximityVoteEnabled() {
+ return mVsyncProximityVote.isEnabled();
+ }
+
+ public boolean isBrightnessWearBedtimeModeClamperEnabled() {
+ return mBrightnessWearBedtimeModeClamperFlagState.isEnabled();
+ }
+
+ /**
+ * @return Whether generic auto-brightness modes are enabled
+ */
+ public boolean areAutoBrightnessModesEnabled() {
+ return mAutoBrightnessModesFlagState.isEnabled();
+ }
+
/**
* dumps all flagstates
* @param pw printWriter
@@ -188,6 +215,9 @@
pw.println(" " + mPowerThrottlingClamperFlagState);
pw.println(" " + mSmallAreaDetectionFlagState);
pw.println(" " + mBrightnessIntRangeUserPerceptionFlagState);
+ pw.println(" " + mVsyncProximityVote);
+ pw.println(" " + mBrightnessWearBedtimeModeClamperFlagState);
+ pw.println(" " + mAutoBrightnessModesFlagState);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index e28b415..9dfa1ee 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -112,3 +112,27 @@
bug: "183655602"
is_fixed_read_only: true
}
+
+flag {
+ name: "enable_external_vsync_proximity_vote"
+ namespace: "display_manager"
+ description: "Feature flag for external vsync proximity vote"
+ bug: "284866750"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "brightness_wear_bedtime_mode_clamper"
+ namespace: "display_manager"
+ description: "Feature flag for the Wear Bedtime mode brightness clamper"
+ bug: "293613040"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "auto_brightness_modes"
+ namespace: "display_manager"
+ description: "Feature flag for generic auto-brightness modes"
+ bug: "293613040"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/mode/BaseModeRefreshRateVote.java b/services/core/java/com/android/server/display/mode/BaseModeRefreshRateVote.java
new file mode 100644
index 0000000..c04df64
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/BaseModeRefreshRateVote.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode;
+
+import java.util.Objects;
+
+class BaseModeRefreshRateVote implements Vote {
+
+ /**
+ * The preferred refresh rate selected by the app. It is used to validate that the summary
+ * refresh rate ranges include this value, and are not restricted by a lower priority vote.
+ */
+ final float mAppRequestBaseModeRefreshRate;
+
+ BaseModeRefreshRateVote(float baseModeRefreshRate) {
+ mAppRequestBaseModeRefreshRate = baseModeRefreshRate;
+ }
+
+ @Override
+ public void updateSummary(DisplayModeDirector.VoteSummary summary) {
+ if (summary.appRequestBaseModeRefreshRate == 0f
+ && mAppRequestBaseModeRefreshRate > 0f) {
+ summary.appRequestBaseModeRefreshRate = mAppRequestBaseModeRefreshRate;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof BaseModeRefreshRateVote that)) return false;
+ return Float.compare(that.mAppRequestBaseModeRefreshRate,
+ mAppRequestBaseModeRefreshRate) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAppRequestBaseModeRefreshRate);
+ }
+
+ @Override
+ public String toString() {
+ return "BaseModeRefreshRateVote{ mAppRequestBaseModeRefreshRate="
+ + mAppRequestBaseModeRefreshRate + " }";
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/CombinedVote.java b/services/core/java/com/android/server/display/mode/CombinedVote.java
new file mode 100644
index 0000000..f24fe3a
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/CombinedVote.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+class CombinedVote implements Vote {
+ final List<Vote> mVotes;
+
+ CombinedVote(List<Vote> votes) {
+ mVotes = Collections.unmodifiableList(votes);
+ }
+
+ @Override
+ public void updateSummary(DisplayModeDirector.VoteSummary summary) {
+ mVotes.forEach(vote -> vote.updateSummary(summary));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof CombinedVote that)) return false;
+ return Objects.equals(mVotes, that.mVotes);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mVotes);
+ }
+
+ @Override
+ public String toString() {
+ return "CombinedVote{ mVotes=" + mVotes + " }";
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/DisableRefreshRateSwitchingVote.java b/services/core/java/com/android/server/display/mode/DisableRefreshRateSwitchingVote.java
new file mode 100644
index 0000000..2fc5590
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/DisableRefreshRateSwitchingVote.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode;
+
+import java.util.Objects;
+
+class DisableRefreshRateSwitchingVote implements Vote {
+
+ /**
+ * Whether refresh rate switching should be disabled (i.e. the refresh rate range is
+ * a single value).
+ */
+ final boolean mDisableRefreshRateSwitching;
+
+ DisableRefreshRateSwitchingVote(boolean disableRefreshRateSwitching) {
+ mDisableRefreshRateSwitching = disableRefreshRateSwitching;
+ }
+
+ @Override
+ public void updateSummary(DisplayModeDirector.VoteSummary summary) {
+ summary.disableRefreshRateSwitching =
+ summary.disableRefreshRateSwitching || mDisableRefreshRateSwitching;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DisableRefreshRateSwitchingVote that)) return false;
+ return mDisableRefreshRateSwitching == that.mDisableRefreshRateSwitching;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDisableRefreshRateSwitching);
+ }
+
+ @Override
+ public String toString() {
+ return "DisableRefreshRateSwitchingVote{ mDisableRefreshRateSwitching="
+ + mDisableRefreshRateSwitching + " }";
+ }
+}
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 9587f55..8fa838d 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -21,8 +21,8 @@
import static android.os.PowerManager.BRIGHTNESS_INVALID_FLOAT;
import static android.view.Display.Mode.INVALID_MODE_ID;
-import static com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRate;
import static com.android.server.display.DisplayDeviceConfig.DEFAULT_LOW_REFRESH_RATE;
+import static com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay;
import android.annotation.IntegerRes;
import android.annotation.NonNull;
@@ -262,7 +262,7 @@
mVotesStorage.setLoggingEnabled(loggingEnabled);
}
- private static final class VoteSummary {
+ static final class VoteSummary {
public float minPhysicalRefreshRate;
public float maxPhysicalRefreshRate;
public float minRenderFrameRate;
@@ -274,7 +274,12 @@
public boolean disableRefreshRateSwitching;
public float appRequestBaseModeRefreshRate;
- VoteSummary() {
+ public List<SupportedModesVote.SupportedMode> supportedModes;
+
+ final boolean mIsDisplayResolutionRangeVotingEnabled;
+
+ VoteSummary(boolean isDisplayResolutionRangeVotingEnabled) {
+ mIsDisplayResolutionRangeVotingEnabled = isDisplayResolutionRangeVotingEnabled;
reset();
}
@@ -322,46 +327,7 @@
continue;
}
- // For physical refresh rates, just use the tightest bounds of all the votes.
- // The refresh rate cannot be lower than the minimal render frame rate.
- final float minPhysicalRefreshRate = Math.max(vote.refreshRateRanges.physical.min,
- vote.refreshRateRanges.render.min);
- summary.minPhysicalRefreshRate = Math.max(summary.minPhysicalRefreshRate,
- minPhysicalRefreshRate);
- summary.maxPhysicalRefreshRate = Math.min(summary.maxPhysicalRefreshRate,
- vote.refreshRateRanges.physical.max);
-
- // Same goes to render frame rate, but frame rate cannot exceed the max physical
- // refresh rate
- final float maxRenderFrameRate = Math.min(vote.refreshRateRanges.render.max,
- vote.refreshRateRanges.physical.max);
- summary.minRenderFrameRate = Math.max(summary.minRenderFrameRate,
- vote.refreshRateRanges.render.min);
- summary.maxRenderFrameRate = Math.min(summary.maxRenderFrameRate, maxRenderFrameRate);
-
- // For display size, disable refresh rate switching and base mode refresh rate use only
- // the first vote we come across (i.e. the highest priority vote that includes the
- // attribute).
- if (vote.height > 0 && vote.width > 0) {
- if (summary.width == Vote.INVALID_SIZE && summary.height == Vote.INVALID_SIZE) {
- summary.width = vote.width;
- summary.height = vote.height;
- summary.minWidth = vote.minWidth;
- summary.minHeight = vote.minHeight;
- } else if (mIsDisplayResolutionRangeVotingEnabled) {
- summary.width = Math.min(summary.width, vote.width);
- summary.height = Math.min(summary.height, vote.height);
- summary.minWidth = Math.max(summary.minWidth, vote.minWidth);
- summary.minHeight = Math.max(summary.minHeight, vote.minHeight);
- }
- }
- if (!summary.disableRefreshRateSwitching && vote.disableRefreshRateSwitching) {
- summary.disableRefreshRateSwitching = true;
- }
- if (summary.appRequestBaseModeRefreshRate == 0f
- && vote.appRequestBaseModeRefreshRate > 0f) {
- summary.appRequestBaseModeRefreshRate = vote.appRequestBaseModeRefreshRate;
- }
+ vote.updateSummary(summary);
if (mLoggingEnabled) {
Slog.w(TAG, "Vote summary for priority " + Vote.priorityToString(priority)
@@ -443,7 +409,7 @@
ArrayList<Display.Mode> availableModes = new ArrayList<>();
availableModes.add(defaultMode);
- VoteSummary primarySummary = new VoteSummary();
+ VoteSummary primarySummary = new VoteSummary(mIsDisplayResolutionRangeVotingEnabled);
int lowestConsideredPriority = Vote.MIN_PRIORITY;
int highestConsideredPriority = Vote.MAX_PRIORITY;
@@ -526,7 +492,7 @@
+ "]");
}
- VoteSummary appRequestSummary = new VoteSummary();
+ VoteSummary appRequestSummary = new VoteSummary(mIsDisplayResolutionRangeVotingEnabled);
summarizeVotes(
votes,
Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF,
@@ -959,16 +925,11 @@
}
@VisibleForTesting
- DisplayObserver getDisplayObserver() {
- return mDisplayObserver;
- }
-
- @VisibleForTesting
DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
synchronized (mLock) {
- mSettingsObserver.updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate,
- defaultRefreshRate, Display.DEFAULT_DISPLAY);
+ mSettingsObserver.updateRefreshRateSettingLocked(
+ minRefreshRate, peakRefreshRate, defaultRefreshRate);
return getDesiredDisplayModeSpecs(Display.DEFAULT_DISPLAY);
}
}
@@ -1302,23 +1263,9 @@
mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
}
- /**
- * Update refresh rate settings for all displays
- */
private void updateRefreshRateSettingLocked() {
- Display[] displays = mInjector.getDisplays();
- for (int i = 0; i < displays.length; i++) {
- updateRefreshRateSettingLocked(displays[i].getDisplayId());
- }
- }
-
- /**
- * Update refresh rate settings for a specific display
- * @param displayId The display ID
- */
- private void updateRefreshRateSettingLocked(int displayId) {
final ContentResolver cr = mContext.getContentResolver();
- float highestRefreshRate = findHighestRefreshRate(mContext, displayId);
+ float highestRefreshRate = findHighestRefreshRateForDefaultDisplay(mContext);
float minRefreshRate = Settings.System.getFloatForUser(cr,
Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
@@ -1346,12 +1293,11 @@
}
}
- updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate,
- displayId);
+ updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
}
- private void updateRefreshRateSettingLocked(float minRefreshRate, float peakRefreshRate,
- float defaultRefreshRate, int displayId) {
+ private void updateRefreshRateSettingLocked(
+ float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
// TODO(b/156304339): The logic in here, aside from updating the refresh rate votes, is
// used to predict if we're going to be doing frequent refresh rate switching, and if
// so, enable the brightness observer. The logic here is more complicated and fragile
@@ -1359,9 +1305,9 @@
Vote peakVote = peakRefreshRate == 0f
? null
: Vote.forRenderFrameRates(0f, Math.max(minRefreshRate, peakRefreshRate));
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
+ mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
peakVote);
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
+ mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(minRefreshRate, Float.POSITIVE_INFINITY));
Vote defaultVote =
defaultRefreshRate == 0f
@@ -1518,8 +1464,7 @@
}
}
- @VisibleForTesting
- public final class DisplayObserver implements DisplayManager.DisplayListener {
+ private final class DisplayObserver implements DisplayManager.DisplayListener {
// Note that we can never call into DisplayManager or any of the non-POD classes it
// returns, while holding mLock since it may call into DMS, which might be simultaneously
// calling into us already holding its own lock.
@@ -1611,7 +1556,6 @@
updateDisplayModes(displayId, displayInfo);
updateLayoutLimitedFrameRate(displayId, displayInfo);
updateUserSettingDisplayPreferredSize(displayInfo);
- mSettingsObserver.updateRefreshRateSettingLocked(displayId);
}
@Nullable
diff --git a/services/core/java/com/android/server/display/mode/RefreshRateVote.java b/services/core/java/com/android/server/display/mode/RefreshRateVote.java
new file mode 100644
index 0000000..173b3c5
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/RefreshRateVote.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode;
+
+import java.util.Objects;
+
+
+/**
+ * Information about the refresh rate frame rate ranges DM would like to set the display to.
+ */
+abstract class RefreshRateVote implements Vote {
+ final float mMinRefreshRate;
+
+ final float mMaxRefreshRate;
+
+ RefreshRateVote(float minRefreshRate, float maxRefreshRate) {
+ mMinRefreshRate = minRefreshRate;
+ mMaxRefreshRate = maxRefreshRate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof RefreshRateVote that)) return false;
+ return Float.compare(that.mMinRefreshRate, mMinRefreshRate) == 0
+ && Float.compare(that.mMaxRefreshRate, mMaxRefreshRate) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mMinRefreshRate, mMaxRefreshRate);
+ }
+
+ @Override
+ public String toString() {
+ return "RefreshRateVote{ mMinRefreshRate=" + mMinRefreshRate
+ + ", mMaxRefreshRate=" + mMaxRefreshRate + " }";
+ }
+
+ static class RenderVote extends RefreshRateVote {
+ RenderVote(float minRefreshRate, float maxRefreshRate) {
+ super(minRefreshRate, maxRefreshRate);
+ }
+
+ /**
+ * Summary: minRender minPhysical maxRender
+ * v v v
+ * -------------------|---------------------"-----------------------------|---------
+ * ^ ^ ^* ^ ^
+ * Vote: min(ignored) min(applied) min(applied+physical) max(applied) max(ignored)
+ */
+ @Override
+ public void updateSummary(DisplayModeDirector.VoteSummary summary) {
+ summary.minRenderFrameRate = Math.max(summary.minRenderFrameRate, mMinRefreshRate);
+ summary.maxRenderFrameRate = Math.min(summary.maxRenderFrameRate, mMaxRefreshRate);
+ // Physical refresh rate cannot be lower than the minimal render frame rate.
+ summary.minPhysicalRefreshRate = Math.max(summary.minPhysicalRefreshRate,
+ mMinRefreshRate);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof RefreshRateVote.RenderVote)) return false;
+ return super.equals(o);
+ }
+
+ @Override
+ public String toString() {
+ return "RenderVote{ " + super.toString() + " }";
+ }
+ }
+
+ static class PhysicalVote extends RefreshRateVote {
+ PhysicalVote(float minRefreshRate, float maxRefreshRate) {
+ super(minRefreshRate, maxRefreshRate);
+ }
+
+ /**
+ * Summary: minPhysical maxRender maxPhysical
+ * v v v
+ * -------------------"-----------------------------|----------------------"----------
+ * ^ ^ ^* ^ ^
+ * Vote: min(ignored) min(applied) max(applied+render) max(applied) max(ignored)
+ */
+ @Override
+ public void updateSummary(DisplayModeDirector.VoteSummary summary) {
+ summary.minPhysicalRefreshRate = Math.max(summary.minPhysicalRefreshRate,
+ mMinRefreshRate);
+ summary.maxPhysicalRefreshRate = Math.min(summary.maxPhysicalRefreshRate,
+ mMaxRefreshRate);
+ // Render frame rate cannot exceed the max physical refresh rate
+ summary.maxRenderFrameRate = Math.min(summary.maxRenderFrameRate, mMaxRefreshRate);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof RefreshRateVote.PhysicalVote)) return false;
+ return super.equals(o);
+ }
+
+ @Override
+ public String toString() {
+ return "PhysicalVote{ " + super.toString() + " }";
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/SizeVote.java b/services/core/java/com/android/server/display/mode/SizeVote.java
new file mode 100644
index 0000000..a9b18a5
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/SizeVote.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode;
+
+import java.util.Objects;
+
+class SizeVote implements Vote {
+
+ /**
+ * The requested width of the display in pixels;
+ */
+ final int mWidth;
+
+ /**
+ * The requested height of the display in pixels;
+ */
+ final int mHeight;
+
+ /**
+ * Min requested width of the display in pixels;
+ */
+ final int mMinWidth;
+
+ /**
+ * Min requested height of the display in pixels;
+ */
+ final int mMinHeight;
+
+ SizeVote(int width, int height, int minWidth, int minHeight) {
+ mWidth = width;
+ mHeight = height;
+ mMinWidth = minWidth;
+ mMinHeight = minHeight;
+ }
+
+ @Override
+ public void updateSummary(DisplayModeDirector.VoteSummary summary) {
+ if (mHeight > 0 && mWidth > 0) {
+ // For display size, disable refresh rate switching and base mode refresh rate use
+ // only the first vote we come across (i.e. the highest priority vote that includes
+ // the attribute).
+ if (summary.width == Vote.INVALID_SIZE && summary.height == Vote.INVALID_SIZE) {
+ summary.width = mWidth;
+ summary.height = mHeight;
+ summary.minWidth = mMinWidth;
+ summary.minHeight = mMinHeight;
+ } else if (summary.mIsDisplayResolutionRangeVotingEnabled) {
+ summary.width = Math.min(summary.width, mWidth);
+ summary.height = Math.min(summary.height, mHeight);
+ summary.minWidth = Math.max(summary.minWidth, mMinWidth);
+ summary.minHeight = Math.max(summary.minHeight, mMinHeight);
+ }
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SizeVote sizeVote)) return false;
+ return mWidth == sizeVote.mWidth && mHeight == sizeVote.mHeight
+ && mMinWidth == sizeVote.mMinWidth && mMinHeight == sizeVote.mMinHeight;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mWidth, mHeight, mMinWidth, mMinHeight);
+ }
+
+ @Override
+ public String toString() {
+ return "SizeVote{ mWidth=" + mWidth + ", mHeight=" + mHeight
+ + ", mMinWidth=" + mMinWidth + ", mMinHeight=" + mMinHeight + " }";
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/SupportedModesVote.java b/services/core/java/com/android/server/display/mode/SupportedModesVote.java
new file mode 100644
index 0000000..b31461f
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/SupportedModesVote.java
@@ -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.server.display.mode;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+class SupportedModesVote implements Vote {
+
+ final List<SupportedMode> mSupportedModes;
+
+ SupportedModesVote(List<SupportedMode> supportedModes) {
+ mSupportedModes = Collections.unmodifiableList(supportedModes);
+ }
+
+ /**
+ * Summary should have subset of supported modes.
+ * If Vote1.supportedModes=(A,B), Vote2.supportedModes=(B,C) then summary.supportedModes=(B)
+ * If summary.supportedModes==null then there is no restriction on supportedModes
+ */
+ @Override
+ public void updateSummary(DisplayModeDirector.VoteSummary summary) {
+ if (summary.supportedModes == null) {
+ summary.supportedModes = new ArrayList<>(mSupportedModes);
+ } else {
+ summary.supportedModes.retainAll(mSupportedModes);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SupportedModesVote that)) return false;
+ return mSupportedModes.equals(that.mSupportedModes);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSupportedModes);
+ }
+
+ @Override
+ public String toString() {
+ return "SupportedModesVote{ mSupportedModes=" + mSupportedModes + " }";
+ }
+
+ static class SupportedMode {
+ final float mPeakRefreshRate;
+ final float mVsyncRate;
+
+
+ SupportedMode(float peakRefreshRate, float vsyncRate) {
+ mPeakRefreshRate = peakRefreshRate;
+ mVsyncRate = vsyncRate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SupportedMode that)) return false;
+ return Float.compare(that.mPeakRefreshRate, mPeakRefreshRate) == 0
+ && Float.compare(that.mVsyncRate, mVsyncRate) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPeakRefreshRate, mVsyncRate);
+ }
+
+ @Override
+ public String toString() {
+ return "SupportedMode{ mPeakRefreshRate=" + mPeakRefreshRate
+ + ", mVsyncRate=" + mVsyncRate + " }";
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index b6a6069..c1cdd69 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -16,15 +16,13 @@
package com.android.server.display.mode;
-import android.view.SurfaceControl;
+import java.util.List;
-import java.util.Objects;
-
-final class Vote {
+interface Vote {
// DEFAULT_RENDER_FRAME_RATE votes for render frame rate [0, DEFAULT]. As the lowest
// priority vote, it's overridden by all other considerations. It acts to set a default
// frame rate for a device.
- static final int PRIORITY_DEFAULT_RENDER_FRAME_RATE = 0;
+ int PRIORITY_DEFAULT_RENDER_FRAME_RATE = 0;
// PRIORITY_FLICKER_REFRESH_RATE votes for a single refresh rate like [60,60], [90,90] or
// null. It is used to set a preferred refresh rate value in case the higher priority votes
@@ -32,21 +30,21 @@
static final int PRIORITY_FLICKER_REFRESH_RATE = 1;
// High-brightness-mode may need a specific range of refresh-rates to function properly.
- static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 2;
+ int PRIORITY_HIGH_BRIGHTNESS_MODE = 2;
// SETTING_MIN_RENDER_FRAME_RATE is used to propose a lower bound of the render frame rate.
// It votes [minRefreshRate, Float.POSITIVE_INFINITY]
- static final int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3;
+ int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3;
// User setting preferred display resolution.
- static final int PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE = 4;
+ int PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE = 4;
// APP_REQUEST_RENDER_FRAME_RATE_RANGE is used to for internal apps to limit the render
// frame rate in certain cases, mostly to preserve power.
// @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate
// @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate
// It votes to [preferredMinRefreshRate, preferredMaxRefreshRate].
- static final int PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 5;
+ int PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 5;
// We split the app request into different priorities in case we can satisfy one desire
// without the other.
@@ -72,181 +70,100 @@
// The preferred refresh rate is set on the main surface of the app outside of
// DisplayModeDirector.
// @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded
- static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 6;
+ int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 6;
- static final int PRIORITY_APP_REQUEST_SIZE = 7;
+ int PRIORITY_APP_REQUEST_SIZE = 7;
// SETTING_PEAK_RENDER_FRAME_RATE has a high priority and will restrict the bounds of the
// rest of low priority voters. It votes [0, max(PEAK, MIN)]
- static final int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 8;
+ int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 8;
// Restrict all displays to 60Hz when external display is connected. It votes [59Hz, 61Hz].
- static final int PRIORITY_SYNCHRONIZED_REFRESH_RATE = 9;
+ int PRIORITY_SYNCHRONIZED_REFRESH_RATE = 9;
// Restrict displays max available resolution and refresh rates. It votes [0, LIMIT]
- static final int PRIORITY_LIMIT_MODE = 10;
+ int PRIORITY_LIMIT_MODE = 10;
// To avoid delay in switching between 60HZ -> 90HZ when activating LHBM, set refresh
// rate to max value (same as for PRIORITY_UDFPS) on lock screen
- static final int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 11;
+ int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 11;
// For concurrent displays we want to limit refresh rate on all displays
- static final int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 12;
+ int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 12;
// LOW_POWER_MODE force the render frame rate to [0, 60HZ] if
// Settings.Global.LOW_POWER_MODE is on.
- static final int PRIORITY_LOW_POWER_MODE = 13;
+ int PRIORITY_LOW_POWER_MODE = 13;
// PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
// higher priority voters' result is a range, it will fix the rate to a single choice.
// It's used to avoid refresh rate switches in certain conditions which may result in the
// user seeing the display flickering when the switches occur.
- static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 14;
+ int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 14;
// Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
- static final int PRIORITY_SKIN_TEMPERATURE = 15;
+ int PRIORITY_SKIN_TEMPERATURE = 15;
// The proximity sensor needs the refresh rate to be locked in order to function, so this is
// set to a high priority.
- static final int PRIORITY_PROXIMITY = 16;
+ int PRIORITY_PROXIMITY = 16;
// The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
// to function, so this needs to be the highest priority of all votes.
- static final int PRIORITY_UDFPS = 17;
+ int PRIORITY_UDFPS = 17;
// Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
// APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
- static final int MIN_PRIORITY = PRIORITY_DEFAULT_RENDER_FRAME_RATE;
- static final int MAX_PRIORITY = PRIORITY_UDFPS;
+ int MIN_PRIORITY = PRIORITY_DEFAULT_RENDER_FRAME_RATE;
+ int MAX_PRIORITY = PRIORITY_UDFPS;
// The cutoff for the app request refresh rate range. Votes with priorities lower than this
// value will not be considered when constructing the app request refresh rate range.
- static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
+ int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE;
/**
* A value signifying an invalid width or height in a vote.
*/
- static final int INVALID_SIZE = -1;
+ int INVALID_SIZE = -1;
- /**
- * The requested width of the display in pixels, or INVALID_SIZE;
- */
- public final int width;
- /**
- * The requested height of the display in pixels, or INVALID_SIZE;
- */
- public final int height;
- /**
- * Min requested width of the display in pixels, or 0;
- */
- public final int minWidth;
- /**
- * Min requested height of the display in pixels, or 0;
- */
- public final int minHeight;
- /**
- * Information about the refresh rate frame rate ranges DM would like to set the display to.
- */
- public final SurfaceControl.RefreshRateRanges refreshRateRanges;
-
- /**
- * Whether refresh rate switching should be disabled (i.e. the refresh rate range is
- * a single value).
- */
- public final boolean disableRefreshRateSwitching;
-
- /**
- * The preferred refresh rate selected by the app. It is used to validate that the summary
- * refresh rate ranges include this value, and are not restricted by a lower priority vote.
- */
- public final float appRequestBaseModeRefreshRate;
+ void updateSummary(DisplayModeDirector.VoteSummary summary);
static Vote forPhysicalRefreshRates(float minRefreshRate, float maxRefreshRate) {
- return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
- /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
- /* minPhysicalRefreshRate= */ minRefreshRate,
- /* maxPhysicalRefreshRate= */ maxRefreshRate,
- /* minRenderFrameRate= */ 0,
- /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
- /* disableRefreshRateSwitching= */ minRefreshRate == maxRefreshRate,
- /* baseModeRefreshRate= */ 0f);
+ return new CombinedVote(
+ List.of(
+ new RefreshRateVote.PhysicalVote(minRefreshRate, maxRefreshRate),
+ new DisableRefreshRateSwitchingVote(minRefreshRate == maxRefreshRate)
+ )
+ );
}
static Vote forRenderFrameRates(float minFrameRate, float maxFrameRate) {
- return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
- /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
- /* minPhysicalRefreshRate= */ 0,
- /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
- minFrameRate,
- maxFrameRate,
- /* disableRefreshRateSwitching= */ false,
- /* baseModeRefreshRate= */ 0f);
+ return new RefreshRateVote.RenderVote(minFrameRate, maxFrameRate);
}
static Vote forSize(int width, int height) {
- return new Vote(/* minWidth= */ width, /* minHeight= */ height,
- width, height,
- /* minPhysicalRefreshRate= */ 0,
- /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
- /* minRenderFrameRate= */ 0,
- /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
- /* disableRefreshRateSwitching= */ false,
- /* baseModeRefreshRate= */ 0f);
+ return new SizeVote(width, height, width, height);
}
static Vote forSizeAndPhysicalRefreshRatesRange(int minWidth, int minHeight,
int width, int height, float minRefreshRate, float maxRefreshRate) {
- return new Vote(minWidth, minHeight,
- width, height,
- minRefreshRate,
- maxRefreshRate,
- /* minRenderFrameRate= */ 0,
- /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
- /* disableRefreshRateSwitching= */ minRefreshRate == maxRefreshRate,
- /* baseModeRefreshRate= */ 0f);
+ return new CombinedVote(
+ List.of(
+ new SizeVote(width, height, minWidth, minHeight),
+ new RefreshRateVote.PhysicalVote(minRefreshRate, maxRefreshRate),
+ new DisableRefreshRateSwitchingVote(minRefreshRate == maxRefreshRate)
+ )
+ );
}
static Vote forDisableRefreshRateSwitching() {
- return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
- /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
- /* minPhysicalRefreshRate= */ 0,
- /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
- /* minRenderFrameRate= */ 0,
- /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
- /* disableRefreshRateSwitching= */ true,
- /* baseModeRefreshRate= */ 0f);
+ return new DisableRefreshRateSwitchingVote(true);
}
static Vote forBaseModeRefreshRate(float baseModeRefreshRate) {
- return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
- /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
- /* minPhysicalRefreshRate= */ 0,
- /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
- /* minRenderFrameRate= */ 0,
- /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
- /* disableRefreshRateSwitching= */ false,
- /* baseModeRefreshRate= */ baseModeRefreshRate);
- }
-
- private Vote(int minWidth, int minHeight,
- int width, int height,
- float minPhysicalRefreshRate,
- float maxPhysicalRefreshRate,
- float minRenderFrameRate,
- float maxRenderFrameRate,
- boolean disableRefreshRateSwitching,
- float baseModeRefreshRate) {
- this.minWidth = minWidth;
- this.minHeight = minHeight;
- this.width = width;
- this.height = height;
- this.refreshRateRanges = new SurfaceControl.RefreshRateRanges(
- new SurfaceControl.RefreshRateRange(minPhysicalRefreshRate, maxPhysicalRefreshRate),
- new SurfaceControl.RefreshRateRange(minRenderFrameRate, maxRenderFrameRate));
- this.disableRefreshRateSwitching = disableRefreshRateSwitching;
- this.appRequestBaseModeRefreshRate = baseModeRefreshRate;
+ return new BaseModeRefreshRateVote(baseModeRefreshRate);
}
static String priorityToString(int priority) {
@@ -291,33 +208,4 @@
return Integer.toString(priority);
}
}
-
- @Override
- public String toString() {
- return "Vote: {"
- + "minWidth: " + minWidth + ", minHeight: " + minHeight
- + ", width: " + width + ", height: " + height
- + ", refreshRateRanges: " + refreshRateRanges
- + ", disableRefreshRateSwitching: " + disableRefreshRateSwitching
- + ", appRequestBaseModeRefreshRate: " + appRequestBaseModeRefreshRate + "}";
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(minWidth, minHeight, width, height, refreshRateRanges,
- disableRefreshRateSwitching, appRequestBaseModeRefreshRate);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof Vote)) return false;
- final var vote = (Vote) o;
- return minWidth == vote.minWidth && minHeight == vote.minHeight
- && width == vote.width && height == vote.height
- && disableRefreshRateSwitching == vote.disableRefreshRateSwitching
- && Float.compare(vote.appRequestBaseModeRefreshRate,
- appRequestBaseModeRefreshRate) == 0
- && refreshRateRanges.equals(vote.refreshRateRanges);
- }
}
diff --git a/services/core/java/com/android/server/display/mode/VotesStorage.java b/services/core/java/com/android/server/display/mode/VotesStorage.java
index 49c587a..95fb8fc 100644
--- a/services/core/java/com/android/server/display/mode/VotesStorage.java
+++ b/services/core/java/com/android/server/display/mode/VotesStorage.java
@@ -157,13 +157,19 @@
}
}
- private int getMaxPhysicalRefreshRate(@Nullable Vote vote) {
+ private static int getMaxPhysicalRefreshRate(@Nullable Vote vote) {
if (vote == null) {
return -1;
- } else if (vote.refreshRateRanges.physical.max == Float.POSITIVE_INFINITY) {
- return 1000; // for visualisation, otherwise e.g. -1 -> 60 will be unnoticeable
+ } else if (vote instanceof RefreshRateVote.PhysicalVote physicalVote) {
+ return (int) physicalVote.mMaxRefreshRate;
+ } else if (vote instanceof CombinedVote combinedVote) {
+ return combinedVote.mVotes.stream()
+ .filter(v -> v instanceof RefreshRateVote.PhysicalVote)
+ .map(pv -> (int) (((RefreshRateVote.PhysicalVote) pv).mMaxRefreshRate))
+ .min(Integer::compare)
+ .orElse(1000); // for visualisation
}
- return (int) vote.refreshRateRanges.physical.max;
+ return 1000; // for visualisation, otherwise e.g. -1 -> 60 will be unnoticeable
}
interface Listener {
diff --git a/services/core/java/com/android/server/flags/OWNERS b/services/core/java/com/android/server/flags/OWNERS
new file mode 100644
index 0000000..535a750
--- /dev/null
+++ b/services/core/java/com/android/server/flags/OWNERS
@@ -0,0 +1 @@
+per-file pinner.aconfig = edgararriaga@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/flags/pinner.aconfig b/services/core/java/com/android/server/flags/pinner.aconfig
new file mode 100644
index 0000000..606a6be
--- /dev/null
+++ b/services/core/java/com/android/server/flags/pinner.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.flags"
+
+flag {
+ name: "pin_webview"
+ namespace: "system_performance"
+ description: "This flag controls if webview should be pinned in memory."
+ bug: "307594624"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 6f9b7d6..27b01a5 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -67,6 +67,7 @@
import android.location.LocationManager;
import android.location.LocationRequest;
import android.location.LocationResult;
+import android.location.flags.Flags;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
@@ -1048,10 +1049,22 @@
stopBatching();
if (mStarted && mGnssNative.getCapabilities().hasScheduling()) {
- // change period and/or lowPowerMode
- if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
- mFixInterval, mProviderRequest.isLowPower())) {
- Log.e(TAG, "set_position_mode failed in updateRequirements");
+ if (Flags.gnssCallStopBeforeSetPositionMode()) {
+ GnssPositionMode positionMode = new GnssPositionMode(mPositionMode,
+ GNSS_POSITION_RECURRENCE_PERIODIC, mFixInterval,
+ /* preferredAccuracy= */ 0,
+ /* preferredTime= */ 0,
+ mProviderRequest.isLowPower());
+ if (!positionMode.equals(mLastPositionMode)) {
+ stopNavigating();
+ startNavigating();
+ }
+ } else {
+ // change period and/or lowPowerMode
+ if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
+ mFixInterval, mProviderRequest.isLowPower())) {
+ Log.e(TAG, "set_position_mode failed in updateRequirements");
+ }
}
} else if (!mStarted) {
// start GPS
@@ -1234,11 +1247,32 @@
}
int interval = mGnssNative.getCapabilities().hasScheduling() ? mFixInterval : 1000;
- if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
- interval, mProviderRequest.isLowPower())) {
- setStarted(false);
- Log.e(TAG, "set_position_mode failed in startNavigating()");
- return;
+
+ if (Flags.gnssCallStopBeforeSetPositionMode()) {
+ boolean success = mGnssNative.setPositionMode(mPositionMode,
+ GNSS_POSITION_RECURRENCE_PERIODIC, interval,
+ /* preferredAccuracy= */ 0,
+ /* preferredTime= */ 0,
+ mProviderRequest.isLowPower());
+ if (success) {
+ mLastPositionMode = new GnssPositionMode(mPositionMode,
+ GNSS_POSITION_RECURRENCE_PERIODIC, interval,
+ /* preferredAccuracy= */ 0,
+ /* preferredTime= */ 0,
+ mProviderRequest.isLowPower());
+ } else {
+ mLastPositionMode = null;
+ setStarted(false);
+ Log.e(TAG, "set_position_mode failed in startNavigating()");
+ return;
+ }
+ } else {
+ if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
+ interval, mProviderRequest.isLowPower())) {
+ setStarted(false);
+ Log.e(TAG, "set_position_mode failed in startNavigating()");
+ return;
+ }
}
if (!mGnssNative.start()) {
setStarted(false);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 6a43697..4821fbe 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -2486,6 +2486,13 @@
private void onRequestFailedOnHandler(@NonNull MediaRoute2Provider provider,
long uniqueRequestId, int reason) {
if (handleSessionCreationRequestFailed(provider, uniqueRequestId, reason)) {
+ Slog.w(
+ TAG,
+ TextUtils.formatSimple(
+ "onRequestFailedOnHandler | Finished handling session creation"
+ + " request failed for provider: %s, uniqueRequestId: %d,"
+ + " reason: %d",
+ provider.getUniqueId(), uniqueRequestId, reason));
return;
}
@@ -2515,6 +2522,12 @@
if (matchingRequest == null) {
// The failure is not about creating a session.
+ Slog.w(
+ TAG,
+ TextUtils.formatSimple(
+ "handleSessionCreationRequestFailed | No matching request found for"
+ + " provider: %s, uniqueRequestId: %d, reason: %d",
+ provider.getUniqueId(), uniqueRequestId, reason));
return false;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1b640fc..aa0b9b8 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1765,8 +1765,7 @@
if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
// update system notification channels
SystemNotificationChannels.createAll(context);
- mZenModeHelper.updateDefaultZenRules(Binder.getCallingUid(),
- isCallerIsSystemOrSystemUi());
+ mZenModeHelper.updateDefaultZenRules(Binder.getCallingUid());
mPreferencesHelper.onLocaleChanged(context, ActivityManager.getCurrentUser());
}
}
@@ -3039,7 +3038,7 @@
mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), true);
mPreferencesHelper.updateNotificationChannel(pkg, uid, channel, true,
- Binder.getCallingUid(), isCallerIsSystemOrSystemUi());
+ Binder.getCallingUid(), isCallerSystemOrSystemUi());
if (mPreferencesHelper.onlyHasDefaultChannel(pkg, uid)) {
mPermissionHelper.setNotificationPermission(pkg, UserHandle.getUserId(uid),
channel.getImportance() != IMPORTANCE_NONE, true);
@@ -3087,7 +3086,7 @@
final NotificationChannelGroup preUpdate =
mPreferencesHelper.getNotificationChannelGroup(group.getId(), pkg, uid);
mPreferencesHelper.createNotificationChannelGroup(pkg, uid, group,
- fromApp, Binder.getCallingUid(), isCallerIsSystemOrSystemUi());
+ fromApp, Binder.getCallingUid(), isCallerSystemOrSystemUi());
if (!fromApp) {
maybeNotifyChannelGroupOwner(pkg, uid, preUpdate, group);
}
@@ -3517,7 +3516,7 @@
}
checkCallerIsSameApp(pkg);
- final boolean isSystemToast = isCallerIsSystemOrSystemUi()
+ final boolean isSystemToast = isCallerSystemOrSystemUi()
|| PackageManagerService.PLATFORM_PACKAGE_NAME.equals(pkg);
boolean isAppRenderedToast = (callback != null);
if (!checkCanEnqueueToast(pkg, callingUid, displayId, isAppRenderedToast,
@@ -4084,7 +4083,7 @@
channel, true /* fromTargetApp */,
mConditionProviders.isPackageOrComponentAllowed(
pkg, UserHandle.getUserId(uid)), Binder.getCallingUid(),
- isCallerIsSystemOrSystemUi());
+ isCallerSystemOrSystemUi());
if (needsPolicyFileChange) {
mListeners.notifyNotificationChannelChanged(pkg,
UserHandle.getUserHandleForUid(uid),
@@ -4165,7 +4164,7 @@
String targetPkg, String channelId, boolean returnParentIfNoConversationChannel,
String conversationId) {
if (canNotifyAsPackage(callingPkg, targetPkg, userId)
- || isCallerIsSystemOrSysemUiOrShell()) {
+ || isCallerSystemOrSystemUiOrShell()) {
int targetUid = -1;
try {
targetUid = mPackageManagerClient.getPackageUidAsUser(targetPkg, userId);
@@ -4220,7 +4219,7 @@
public void deleteNotificationChannel(String pkg, String channelId) {
checkCallerIsSystemOrSameApp(pkg);
final int callingUid = Binder.getCallingUid();
- final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+ final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
final int callingUser = UserHandle.getUserId(callingUid);
if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
throw new IllegalArgumentException("Cannot delete default channel");
@@ -4264,7 +4263,7 @@
checkCallerIsSystemOrSameApp(pkg);
final int callingUid = Binder.getCallingUid();
- final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+ final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
NotificationChannelGroup groupToDelete =
mPreferencesHelper.getNotificationChannelGroupWithChannels(
pkg, callingUid, groupId, false);
@@ -5207,7 +5206,7 @@
public void requestInterruptionFilterFromListener(INotificationListener token,
int interruptionFilter) throws RemoteException {
final int callingUid = Binder.getCallingUid();
- final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+ final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mNotificationLock) {
@@ -5254,7 +5253,7 @@
public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
enforceSystemOrSystemUI("INotificationManager.setZenMode");
final int callingUid = Binder.getCallingUid();
- final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+ final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
final long identity = Binder.clearCallingIdentity();
try {
mZenModeHelper.setManualZenMode(mode, conditionId, null, reason, callingUid,
@@ -5316,7 +5315,9 @@
return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule,
"addAutomaticZenRule", Binder.getCallingUid(),
- isCallerIsSystemOrSystemUi());
+ // TODO: b/308670715: Distinguish FROM_APP from FROM_USER
+ isCallerSystemOrSystemUi() ? ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI
+ : ZenModeHelper.FROM_APP);
}
@Override
@@ -5334,7 +5335,9 @@
return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
"updateAutomaticZenRule", Binder.getCallingUid(),
- isCallerIsSystemOrSystemUi());
+ // TODO: b/308670715: Distinguish FROM_APP from FROM_USER
+ isCallerSystemOrSystemUi() ? ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI
+ : ZenModeHelper.FROM_APP);
}
@Override
@@ -5344,7 +5347,7 @@
enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule",
- Binder.getCallingUid(), isCallerIsSystemOrSystemUi());
+ Binder.getCallingUid(), isCallerSystemOrSystemUi());
}
@Override
@@ -5354,7 +5357,7 @@
return mZenModeHelper.removeAutomaticZenRules(packageName,
packageName + "|removeAutomaticZenRules", Binder.getCallingUid(),
- isCallerIsSystemOrSystemUi());
+ isCallerSystemOrSystemUi());
}
@Override
@@ -5373,7 +5376,7 @@
enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState");
mZenModeHelper.setAutomaticZenRuleState(id, condition, Binder.getCallingUid(),
- isCallerIsSystemOrSystemUi());
+ isCallerSystemOrSystemUi());
}
@Override
@@ -5382,7 +5385,7 @@
final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
final int callingUid = Binder.getCallingUid();
- final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+ final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
if (android.app.Flags.modesApi() && !canManageGlobalZenPolicy(pkg, callingUid)) {
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, callingUid, zen);
@@ -5477,7 +5480,7 @@
() -> CompatChanges.isChangeEnabled(MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES,
callingUid));
return !isCompatChangeEnabled
- || isCallerIsSystemOrSystemUi()
+ || isCallerSystemOrSystemUi()
|| hasCompanionDevice(callingPkg, UserHandle.getUserId(callingUid),
AssociationRequest.DEVICE_PROFILE_WATCH);
}
@@ -5708,7 +5711,7 @@
public void setNotificationPolicy(String pkg, Policy policy) {
enforcePolicyAccess(pkg, "setNotificationPolicy");
int callingUid = Binder.getCallingUid();
- boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+ boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
boolean shouldApplyAsImplicitRule = android.app.Flags.modesApi()
&& !canManageGlobalZenPolicy(pkg, callingUid);
@@ -7110,7 +7113,7 @@
}
mPreferencesHelper.updateNotificationChannel(
pkg, notificationUid, channel, false, callingUid,
- isCallerIsSystemOrSystemUi());
+ isCallerSystemOrSystemUi());
r.updateNotificationChannel(channel);
} else if (!channel.isUserVisibleTaskShown() && !TextUtils.isEmpty(channelId)
&& !NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
@@ -10430,7 +10433,7 @@
}
@VisibleForTesting
- protected boolean isCallerIsSystemOrSystemUi() {
+ protected boolean isCallerSystemOrSystemUi() {
if (isCallerSystemOrPhone()) {
return true;
}
@@ -10438,12 +10441,12 @@
== PERMISSION_GRANTED;
}
- private boolean isCallerIsSystemOrSysemUiOrShell() {
+ private boolean isCallerSystemOrSystemUiOrShell() {
int callingUid = Binder.getCallingUid();
if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
return true;
}
- return isCallerIsSystemOrSystemUi();
+ return isCallerSystemOrSystemUi();
}
private void checkCallerIsSystemOrShell() {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 2ef0ca6..89d8200 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -27,6 +27,7 @@
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
@@ -73,6 +74,7 @@
import android.provider.Settings.Global;
import android.service.notification.Condition;
import android.service.notification.ConditionProviderService;
+import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ZenRule;
import android.service.notification.ZenModeProto;
@@ -105,6 +107,8 @@
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -129,6 +133,21 @@
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
static final long SEND_ACTIVATION_AZR_STATUSES = 308673617L;
+ /** A rule addition or update that is initiated by the System or SystemUI. */
+ static final int FROM_SYSTEM_OR_SYSTEMUI = 1;
+ /** A rule addition or update that is initiated by the user (through system settings). */
+ static final int FROM_USER = 2;
+ /** A rule addition or update that is initiated by an app (via NotificationManager APIs). */
+ static final int FROM_APP = 3;
+
+ @IntDef(prefix = { "FROM_" }, value = {
+ FROM_SYSTEM_OR_SYSTEMUI,
+ FROM_USER,
+ FROM_APP
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ChangeOrigin {}
+
// pkg|userId => uid
@VisibleForTesting protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
@@ -378,7 +397,7 @@
}
public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule,
- String reason, int callingUid, boolean fromSystemOrSystemUi) {
+ String reason, int callingUid, @ChangeOrigin int origin) {
if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) {
PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
if (component == null) {
@@ -412,10 +431,10 @@
}
newConfig = mConfig.copy();
ZenRule rule = new ZenRule();
- populateZenRule(pkg, automaticZenRule, rule, true);
+ populateZenRule(pkg, automaticZenRule, rule, true, origin);
newConfig.automaticRules.put(rule.id, rule);
if (setConfigLocked(newConfig, reason, rule.component, true, callingUid,
- fromSystemOrSystemUi)) {
+ origin == FROM_SYSTEM_OR_SYSTEMUI)) {
return rule.id;
} else {
throw new AndroidRuntimeException("Could not create rule");
@@ -424,7 +443,7 @@
}
public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule,
- String reason, int callingUid, boolean fromSystemOrSystemUi) {
+ String reason, int callingUid, @ChangeOrigin int origin) {
ZenModeConfig newConfig;
synchronized (mConfigLock) {
if (mConfig == null) return false;
@@ -452,9 +471,9 @@
}
}
- populateZenRule(rule.pkg, automaticZenRule, rule, false);
+ populateZenRule(rule.pkg, automaticZenRule, rule, false, origin);
return setConfigLocked(newConfig, reason, rule.component, true, callingUid,
- fromSystemOrSystemUi);
+ origin == FROM_SYSTEM_OR_SYSTEMUI);
}
}
@@ -790,7 +809,7 @@
}
}
- protected void updateDefaultZenRules(int callingUid, boolean fromSystemOrSystemUi) {
+ protected void updateDefaultZenRules(int callingUid) {
updateDefaultAutomaticRuleNames();
synchronized (mConfigLock) {
for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
@@ -807,7 +826,7 @@
// update default rule (if locale changed, name of rule will change)
currRule.name = defaultRule.name;
updateAutomaticZenRule(defaultRule.id, zenRuleToAutomaticZenRule(currRule),
- "locale changed", callingUid, fromSystemOrSystemUi);
+ "locale changed", callingUid, FROM_SYSTEM_OR_SYSTEMUI);
}
}
}
@@ -850,7 +869,11 @@
}
private static void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
- boolean isNew) {
+ boolean isNew, @ChangeOrigin int origin) {
+ // TODO: b/308671593,b/311406021 - Handle origins more precisely:
+ // - FROM_USER can override anything and updates bitmask of user-modified fields;
+ // - FROM_SYSTEM_OR_SYSTEMUI can override anything and preserves bitmask;
+ // - FROM_APP can only update if not user-modified.
if (rule.enabled != automaticZenRule.isEnabled()) {
rule.snoozing = false;
}
@@ -861,7 +884,10 @@
rule.modified = automaticZenRule.isModified();
rule.zenPolicy = automaticZenRule.getZenPolicy();
if (Flags.modesApi()) {
- rule.zenDeviceEffects = automaticZenRule.getDeviceEffects();
+ rule.zenDeviceEffects = fixZenDeviceEffects(
+ rule.zenDeviceEffects,
+ automaticZenRule.getDeviceEffects(),
+ origin);
}
rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
@@ -882,6 +908,50 @@
}
}
+ /** "
+ * Fix" {@link ZenDeviceEffects} that are being stored as part of a new or updated ZenRule.
+ *
+ * <ul>
+ * <li> Apps cannot turn on hidden effects (those tagged as {@code @hide}) since they are
+ * intended for platform-specific rules (e.g. wearables). If it's a new rule, we blank them
+ * out; if it's an update, we preserve the previous values.
+ * </ul>
+ */
+ @Nullable
+ private static ZenDeviceEffects fixZenDeviceEffects(@Nullable ZenDeviceEffects oldEffects,
+ @Nullable ZenDeviceEffects newEffects, @ChangeOrigin int origin) {
+ // TODO: b/308671593,b/311406021 - Handle origins more precisely:
+ // - FROM_USER can override anything and updates bitmask of user-modified fields;
+ // - FROM_SYSTEM_OR_SYSTEMUI can override anything and preserves bitmask;
+ // - FROM_APP can only update if not user-modified.
+ if (origin == FROM_SYSTEM_OR_SYSTEMUI || origin == FROM_USER) {
+ return newEffects;
+ }
+
+ if (newEffects == null) {
+ return null;
+ }
+ if (oldEffects != null) {
+ return new ZenDeviceEffects.Builder(newEffects)
+ .setShouldDisableAutoBrightness(oldEffects.shouldDisableAutoBrightness())
+ .setShouldDisableTapToWake(oldEffects.shouldDisableTapToWake())
+ .setShouldDisableTiltToWake(oldEffects.shouldDisableTiltToWake())
+ .setShouldDisableTouch(oldEffects.shouldDisableTouch())
+ .setShouldMinimizeRadioUsage(oldEffects.shouldMinimizeRadioUsage())
+ .setShouldMaximizeDoze(oldEffects.shouldMaximizeDoze())
+ .build();
+ } else {
+ return new ZenDeviceEffects.Builder(newEffects)
+ .setShouldDisableAutoBrightness(false)
+ .setShouldDisableTapToWake(false)
+ .setShouldDisableTiltToWake(false)
+ .setShouldDisableTouch(false)
+ .setShouldMinimizeRadioUsage(false)
+ .setShouldMaximizeDoze(false)
+ .build();
+ }
+ }
+
private static AutomaticZenRule zenRuleToAutomaticZenRule(ZenRule rule) {
AutomaticZenRule azr;
if (Flags.modesApi()) {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 3b3d79e..07e0ddf 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -357,6 +357,12 @@
final DeletePackageAction action;
synchronized (mPm.mLock) {
final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ if (ps == null) {
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, "Attempted to remove non-existent package " + packageName);
+ }
+ return false;
+ }
final PackageSetting disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(ps);
if (PackageManagerServiceUtils.isSystemApp(ps)
&& mPm.checkPermission(CONTROL_KEYGUARD, packageName, UserHandle.USER_SYSTEM)
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 3caff11..448f215 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -154,6 +154,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.F2fsUtils;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.pm.pkg.component.ParsedInstrumentation;
import com.android.internal.pm.pkg.component.ParsedIntentInfo;
@@ -175,7 +176,6 @@
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.Permission;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index fc83120..5494bd9 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -50,9 +50,9 @@
import android.util.ExceptionUtils;
import android.util.Slog;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.util.ArrayUtils;
import com.android.server.art.model.DexoptResult;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
@@ -539,6 +539,12 @@
}
@Nullable
+ public PackageSetting getScanRequestDisabledPackageSetting() {
+ assertScanResultExists();
+ return mScanResult.mRequest.mDisabledPkgSetting;
+ }
+
+ @Nullable
public String getRealPackageName() {
assertScanResultExists();
return mScanResult.mRequest.mRealPkgName;
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java
index 6faf68d..c66a9e9 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelper.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java
@@ -22,7 +22,7 @@
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e557f09..233bf4f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -181,6 +181,8 @@
import com.android.internal.content.F2fsUtils;
import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.om.OverlayConfig;
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.component.ParsedInstrumentation;
import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.internal.telephony.CarrierAppUtils;
@@ -220,9 +222,7 @@
import com.android.server.pm.local.PackageManagerLocalImpl;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.PackageParser2;
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.pm.permission.LegacyPermissionManagerService;
import com.android.server.pm.permission.LegacyPermissionSettings;
@@ -5216,6 +5216,18 @@
}
@Override
+ public String getSuspendingPackage(String packageName, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final Computer snapshot = snapshot();
+ // This will do visibility checks as well.
+ if (!snapshot.isPackageSuspendedForUser(packageName, userId)) {
+ return null;
+ }
+ return mSuspendPackageHelper.getSuspendingPackage(snapshot, packageName, userId,
+ callingUid);
+ }
+
+ @Override
public @NonNull ParceledListSlice<FeatureInfo> getSystemAvailableFeatures() {
// allow instant applications
ArrayList<FeatureInfo> res;
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
index 434a62d..15d2fdc 100644
--- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -41,10 +41,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.InstallLocationUtils;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.PackageParser2;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.rollback.RollbackManagerInternal;
import java.io.File;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index b50d0a0..293b873 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -44,9 +44,9 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.permission.LegacyPermissionDataProvider;
import com.android.server.pm.permission.LegacyPermissionState;
@@ -1732,7 +1732,7 @@
time = 1700251133016L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
- inputSignatures = "private int mBooleans\nprivate int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate int mTargetSdkVersion\nprivate @android.annotation.Nullable byte[] mRestrictUpdateHash\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic com.android.server.pm.PackageSetting setSharedUserAppId(int)\npublic com.android.server.pm.PackageSetting setTargetSdkVersion(int)\npublic com.android.server.pm.PackageSetting setRestrictUpdateHash(byte[])\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprivate void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic boolean isRequestLegacyExternalStorage()\npublic boolean isUserDataFragile()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n int[] queryInstalledUsers(int[],boolean)\n int[] queryUsersInstalledOrHasData(int[])\n long getCeDataInode(int)\n long getDeDataInode(int)\n void setCeDataInode(long,int)\n void setDeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\npublic com.android.server.pm.PackageSetting setScannedAsStoppedSystemApp(boolean)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isIncremental()\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\npublic @java.lang.Override boolean isPersistent()\npublic @java.lang.Override boolean isScannedAsStoppedSystemApp()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final int INSTALL_PERMISSION_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int SCANNED_AS_STOPPED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+ inputSignatures = "private int mBooleans\nprivate int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.internal.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate int mTargetSdkVersion\nprivate @android.annotation.Nullable byte[] mRestrictUpdateHash\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic com.android.server.pm.PackageSetting setSharedUserAppId(int)\npublic com.android.server.pm.PackageSetting setTargetSdkVersion(int)\npublic com.android.server.pm.PackageSetting setRestrictUpdateHash(byte[])\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprivate void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic boolean isRequestLegacyExternalStorage()\npublic boolean isUserDataFragile()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n int[] queryInstalledUsers(int[],boolean)\n int[] queryUsersInstalledOrHasData(int[])\n long getCeDataInode(int)\n long getDeDataInode(int)\n void setCeDataInode(long,int)\n void setDeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\npublic com.android.server.pm.PackageSetting setScannedAsStoppedSystemApp(boolean)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isIncremental()\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\npublic @java.lang.Override boolean isPersistent()\npublic @java.lang.Override boolean isScannedAsStoppedSystemApp()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final int INSTALL_PERMISSION_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int SCANNED_AS_STOPPED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/ParallelPackageParser.java b/services/core/java/com/android/server/pm/ParallelPackageParser.java
index 5625884..1089ac9 100644
--- a/services/core/java/com/android/server/pm/ParallelPackageParser.java
+++ b/services/core/java/com/android/server/pm/ParallelPackageParser.java
@@ -22,9 +22,9 @@
import android.os.Trace;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.util.ConcurrentUtils;
import com.android.server.pm.parsing.PackageParser2;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.io.File;
import java.util.concurrent.ArrayBlockingQueue;
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index 5312ae6..bb0017c 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -30,7 +30,7 @@
import android.util.ArrayMap;
import android.util.Log;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.utils.WatchedLongSparseArray;
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 22ee963..53b84e6 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -73,6 +73,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.internal.pm.pkg.component.ParsedProcess;
@@ -83,7 +84,6 @@
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
diff --git a/services/core/java/com/android/server/pm/ScanRequest.java b/services/core/java/com/android/server/pm/ScanRequest.java
index e66a72f..37cf30b 100644
--- a/services/core/java/com/android/server/pm/ScanRequest.java
+++ b/services/core/java/com/android/server/pm/ScanRequest.java
@@ -21,7 +21,7 @@
import android.os.UserHandle;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index 585e2e4..9384c13 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -46,11 +46,11 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.utils.Snappable;
@@ -856,9 +856,9 @@
// We may not yet have disabled the updated package yet, so be sure to grab the
// current setting if that's the case.
final PackageSetting updatedSystemPs = isUpdatedSystemApp
- ? installRequest.getDisabledPackageSetting() == null
+ ? installRequest.getScanRequestDisabledPackageSetting() == null
? installRequest.getScanRequestOldPackageSetting()
- : installRequest.getDisabledPackageSetting()
+ : installRequest.getScanRequestDisabledPackageSetting()
: null;
if (isUpdatedSystemApp && (updatedSystemPs.getPkg() == null
|| updatedSystemPs.getPkg().getLibraryNames() == null)) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8556317..f90bf4b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1957,6 +1957,19 @@
return userTypeDetails.getStatusBarIcon();
}
+ @Override
+ public @StringRes int getProfileLabelResId(@UserIdInt int userId) {
+ checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId,
+ "getProfileLabelResId");
+ final UserInfo userInfo = getUserInfoNoChecks(userId);
+ final UserTypeDetails userTypeDetails = getUserTypeDetails(userInfo);
+ if (userInfo == null || userTypeDetails == null) {
+ return Resources.ID_NULL;
+ }
+ final int userIndex = userInfo.profileBadge;
+ return userTypeDetails.getLabel(userIndex);
+ }
+
public boolean isProfile(@UserIdInt int userId) {
checkQueryOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isProfile");
return isProfileUnchecked(userId);
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 7bdcd68..56c400a0 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -54,8 +54,15 @@
/** Whether users of this type can be created. */
private final boolean mEnabled;
- // TODO(b/142482943): Currently unused and not set. Hook this up.
- private final int mLabel;
+ /**
+ * Resource IDs ({@link StringRes}) of the user's labels. This might be used to label a
+ * user/profile in tabbed views, etc.
+ * The values are resource IDs referring to the strings not the strings themselves.
+ *
+ * <p>This is an array because, in general, there may be multiple users of the same user type.
+ * In this case, the user is indexed according to its {@link UserInfo#profileBadge}.
+ */
+ private final @Nullable int[] mLabels;
/**
* Maximum number of this user type allowed on the device.
@@ -160,8 +167,8 @@
private final @NonNull UserProperties mDefaultUserProperties;
private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed,
- @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label,
- int maxAllowedPerParent,
+ @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags,
+ @Nullable int[] labels, int maxAllowedPerParent,
int iconBadge, int badgePlain, int badgeNoBackground,
int statusBarIcon,
@Nullable int[] badgeLabels, @Nullable int[] badgeColors,
@@ -181,12 +188,11 @@
this.mDefaultSystemSettings = defaultSystemSettings;
this.mDefaultSecureSettings = defaultSecureSettings;
this.mDefaultCrossProfileIntentFilters = defaultCrossProfileIntentFilters;
-
this.mIconBadge = iconBadge;
this.mBadgePlain = badgePlain;
this.mBadgeNoBackground = badgeNoBackground;
this.mStatusBarIcon = statusBarIcon;
- this.mLabel = label;
+ this.mLabels = labels;
this.mBadgeLabels = badgeLabels;
this.mBadgeColors = badgeColors;
this.mDarkThemeBadgeColors = darkThemeBadgeColors;
@@ -234,9 +240,16 @@
return mDefaultUserInfoPropertyFlags | mBaseType;
}
- // TODO(b/142482943) Hook this up; it is currently unused.
- public int getLabel() {
- return mLabel;
+ /**
+ * Returns the resource ID corresponding to the badgeIndexth label name where the badgeIndex is
+ * expected to be the {@link UserInfo#profileBadge} of the user. If badgeIndex exceeds the
+ * number of labels, returns the label for the highest index.
+ */
+ public @StringRes int getLabel(int badgeIndex) {
+ if (mLabels == null || mLabels.length == 0 || badgeIndex < 0) {
+ return Resources.ID_NULL;
+ }
+ return mLabels[Math.min(badgeIndex, mLabels.length - 1)];
}
/** Returns whether users of this user type should be badged. */
@@ -358,7 +371,6 @@
pw.print(prefix); pw.print("mMaxAllowedPerParent: "); pw.println(mMaxAllowedPerParent);
pw.print(prefix); pw.print("mDefaultUserInfoFlags: ");
pw.println(UserInfo.flagsToString(mDefaultUserInfoPropertyFlags));
- pw.print(prefix); pw.print("mLabel: "); pw.println(mLabel);
mDefaultUserProperties.println(pw, prefix);
final String restrictionsPrefix = prefix + " ";
@@ -392,6 +404,8 @@
pw.println(mBadgeColors != null ? mBadgeColors.length : "0(null)");
pw.print(prefix); pw.print("mDarkThemeBadgeColors.length: ");
pw.println(mDarkThemeBadgeColors != null ? mDarkThemeBadgeColors.length : "0(null)");
+ pw.print(prefix); pw.print("mLabels.length: ");
+ pw.println(mLabels != null ? mLabels.length : "0(null)");
}
/** Builder for a {@link UserTypeDetails}; see that class for documentation. */
@@ -408,7 +422,7 @@
private @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters =
null;
private int mEnabled = 1;
- private int mLabel = Resources.ID_NULL;
+ private @Nullable int[] mLabels = null;
private @Nullable int[] mBadgeLabels = null;
private @Nullable int[] mBadgeColors = null;
private @Nullable int[] mDarkThemeBadgeColors = null;
@@ -488,8 +502,8 @@
return this;
}
- public Builder setLabel(int label) {
- mLabel = label;
+ public Builder setLabels(@StringRes int ... labels) {
+ mLabels = labels;
return this;
}
@@ -562,7 +576,7 @@
mMaxAllowed,
mBaseType,
mDefaultUserInfoPropertyFlags,
- mLabel,
+ mLabels,
mMaxAllowedPerParent,
mIconBadge,
mBadgePlain,
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 7da76c1..4ef8cb7 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -128,7 +128,7 @@
.setName(USER_TYPE_PROFILE_CLONE)
.setBaseType(FLAG_PROFILE)
.setMaxAllowedPerParent(1)
- .setLabel(0)
+ .setLabels(R.string.profile_label_clone)
.setIconBadge(com.android.internal.R.drawable.ic_clone_icon_badge)
.setBadgePlain(com.android.internal.R.drawable.ic_clone_badge)
// Clone doesn't use BadgeNoBackground, so just set to BadgePlain as a placeholder.
@@ -154,6 +154,10 @@
UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
.setCrossProfileIntentResolutionStrategy(UserProperties
.CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_NO_FILTERING)
+ .setShowInQuietMode(
+ UserProperties.SHOW_IN_QUIET_MODE_DEFAULT)
+ .setShowInSharingSurfaces(
+ UserProperties.SHOW_IN_SHARING_SURFACES_WITH_PARENT)
.setMediaSharedWithParent(true)
.setCredentialShareableWithParent(true)
.setDeleteAppWithParent(true));
@@ -169,7 +173,10 @@
.setBaseType(FLAG_PROFILE)
.setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE)
.setMaxAllowedPerParent(1)
- .setLabel(0)
+ .setLabels(
+ R.string.profile_label_work,
+ R.string.profile_label_work_2,
+ R.string.profile_label_work_3)
.setIconBadge(com.android.internal.R.drawable.ic_corp_icon_badge_case)
.setBadgePlain(com.android.internal.R.drawable.ic_corp_badge_case)
.setBadgeNoBackground(com.android.internal.R.drawable.ic_corp_badge_no_background)
@@ -193,6 +200,10 @@
.setStartWithParent(true)
.setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
.setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
+ .setShowInQuietMode(
+ UserProperties.SHOW_IN_QUIET_MODE_PAUSED)
+ .setShowInSharingSurfaces(
+ UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
.setAuthAlwaysRequiredToDisableQuietMode(false)
.setCredentialShareableWithParent(true));
}
@@ -209,7 +220,10 @@
.setName(USER_TYPE_PROFILE_TEST)
.setBaseType(FLAG_PROFILE)
.setMaxAllowedPerParent(2)
- .setLabel(0)
+ .setLabels(
+ R.string.profile_label_test,
+ R.string.profile_label_test,
+ R.string.profile_label_test)
.setIconBadge(com.android.internal.R.drawable.ic_test_icon_badge_experiment)
.setBadgePlain(com.android.internal.R.drawable.ic_test_badge_experiment)
.setBadgeNoBackground(com.android.internal.R.drawable.ic_test_badge_no_background)
@@ -240,7 +254,7 @@
.setBaseType(FLAG_PROFILE)
.setMaxAllowed(1)
.setEnabled(UserManager.isCommunalProfileEnabled() ? 1 : 0)
- .setLabel(0)
+ .setLabels(R.string.profile_label_communal)
.setIconBadge(com.android.internal.R.drawable.ic_test_icon_badge_experiment)
.setBadgePlain(com.android.internal.R.drawable.ic_test_badge_experiment)
.setBadgeNoBackground(com.android.internal.R.drawable.ic_test_badge_no_background)
@@ -276,7 +290,7 @@
.setName(USER_TYPE_PROFILE_PRIVATE)
.setBaseType(FLAG_PROFILE)
.setMaxAllowedPerParent(1)
- .setLabel(0)
+ .setLabels(R.string.profile_label_private)
.setIconBadge(com.android.internal.R.drawable.ic_private_profile_icon_badge)
.setBadgePlain(com.android.internal.R.drawable.ic_private_profile_badge)
// Private Profile doesn't use BadgeNoBackground, so just set to BadgePlain
@@ -298,7 +312,10 @@
.setMediaSharedWithParent(false)
.setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
.setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
- .setHideInSettingsInQuietMode(true)
+ .setShowInQuietMode(
+ UserProperties.SHOW_IN_QUIET_MODE_HIDDEN)
+ .setShowInSharingSurfaces(
+ UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
.setCrossProfileIntentFilterAccessControl(
UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
.setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT));
diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
index 2ab7db4..459e2cf 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
@@ -28,9 +28,9 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.ApexManager;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import libcore.io.IoUtils;
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index 3b10c7f..1c751e0 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -35,12 +35,12 @@
import android.util.Slog;
import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.PackageManagerException;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/parsing/library/AndroidHidlUpdater.java b/services/core/java/com/android/server/pm/parsing/library/AndroidHidlUpdater.java
index 8b5719a..f4e187f 100644
--- a/services/core/java/com/android/server/pm/parsing/library/AndroidHidlUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/AndroidHidlUpdater.java
@@ -21,7 +21,7 @@
import android.os.Build;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
/**
* Updates a package to ensure that if it targets <= P that the android.hidl.base-V1.0-java
diff --git a/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java b/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java
index adaa04c..34880a8d 100644
--- a/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java
@@ -16,7 +16,7 @@
package com.android.server.pm.parsing.library;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
/**
* Updates a package to remove dependency on android.net.ipsec.ike library.
diff --git a/services/core/java/com/android/server/pm/parsing/library/AndroidTestBaseUpdater.java b/services/core/java/com/android/server/pm/parsing/library/AndroidTestBaseUpdater.java
index 3b29d1f..97e3020 100644
--- a/services/core/java/com/android/server/pm/parsing/library/AndroidTestBaseUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/AndroidTestBaseUpdater.java
@@ -29,8 +29,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
/**
diff --git a/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
index 041b77b..0c38c60 100644
--- a/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
@@ -19,9 +19,9 @@
import android.util.ArrayMap;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.modules.utils.build.UnboundedSdkLevel;
import com.android.server.SystemConfig;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
/**
* Updates packages to add or remove dependencies on shared libraries as per attributes
diff --git a/services/core/java/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdater.java b/services/core/java/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdater.java
index b47a768..1c6c1fb 100644
--- a/services/core/java/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdater.java
@@ -16,7 +16,7 @@
package com.android.server.pm.parsing.library;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
/**
* Updates a package to remove dependency on com.google.android.maps library.
diff --git a/services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java b/services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
index ac65c8c..b558981 100644
--- a/services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
@@ -20,7 +20,7 @@
import android.os.Build;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
/**
diff --git a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
index 3da7141..fe9cd0e 100644
--- a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
+++ b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
@@ -24,9 +24,9 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.server.SystemConfig;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
import java.util.ArrayList;
import java.util.List;
diff --git a/services/core/java/com/android/server/pm/parsing/library/PackageSharedLibraryUpdater.java b/services/core/java/com/android/server/pm/parsing/library/PackageSharedLibraryUpdater.java
index a9c22d9..a5af005 100644
--- a/services/core/java/com/android/server/pm/parsing/library/PackageSharedLibraryUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/PackageSharedLibraryUpdater.java
@@ -19,8 +19,8 @@
import android.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.util.ArrayUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.util.ArrayList;
import java.util.List;
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index 0eb2bbd..61be6e1 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -29,17 +29,18 @@
import android.os.incremental.IncrementalManager;
import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.pm.parsing.pkg.AndroidPackageHidden;
import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.pm.pkg.component.ParsedInstrumentation;
import com.android.internal.pm.pkg.component.ParsedProvider;
import com.android.internal.pm.pkg.component.ParsedService;
+import com.android.internal.pm.pkg.parsing.ParsingPackageHidden;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
import com.android.server.pm.PackageManagerException;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.parsing.ParsingPackageHidden;
import java.io.IOException;
import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index c8ac698..85d95ea 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -50,6 +50,10 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.parsing.pkg.AndroidPackageHidden;
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.AndroidPackageSplitImpl;
import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.pm.pkg.component.ParsedApexSystemService;
import com.android.internal.pm.pkg.component.ParsedAttribution;
@@ -63,6 +67,8 @@
import com.android.internal.pm.pkg.component.ParsedProvider;
import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.pm.pkg.component.ParsedUsesPermission;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackageHidden;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
@@ -71,7 +77,6 @@
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.AndroidPackageSplit;
-import com.android.server.pm.pkg.AndroidPackageSplitImpl;
import com.android.server.pm.pkg.SELinuxUtil;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
import com.android.server.pm.pkg.component.ParsedActivityImpl;
@@ -84,8 +89,6 @@
import com.android.server.pm.pkg.component.ParsedProviderImpl;
import com.android.server.pm.pkg.component.ParsedServiceImpl;
import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackageHidden;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 6f6bb45..f7f76aa 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -807,7 +807,7 @@
getDefaultSystemHandlerActivityPackage(pm,
SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId),
userId, MICROPHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
- NOTIFICATION_PERMISSIONS);
+ NOTIFICATION_PERMISSIONS, PHONE_PERMISSIONS);
}
// Voice recognition
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
index fc74a195..c737283 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
@@ -22,9 +22,9 @@
import android.content.pm.SigningDetails;
import android.util.SparseArray;
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal;
import com.android.server.pm.InstallSource;
import com.android.server.pm.PackageKeySetData;
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal;
import com.android.server.pm.permission.LegacyPermissionState;
import java.util.UUID;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java b/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
index 041edaa..019ca13 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java
@@ -32,9 +32,9 @@
import com.android.internal.pm.pkg.component.ParsedComponent;
import com.android.internal.pm.pkg.component.ParsedIntentInfo;
import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
index 1497684..dd54cfc 100644
--- a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
+++ b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
@@ -26,8 +26,8 @@
import android.util.ArraySet;
import com.android.internal.R;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.server.SystemConfig;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
index 5709cbb..64985bd 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
@@ -49,8 +49,8 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.internal.util.ArrayUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java
index c6b9b1a..9322cf0 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java
@@ -31,7 +31,7 @@
import android.util.TypedValue;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
index 9792a91..a711694 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java
@@ -27,7 +27,7 @@
import com.android.internal.R;
import com.android.internal.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
index 5e67bbf..e5e214d 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java
@@ -32,7 +32,7 @@
import com.android.internal.R;
import com.android.internal.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.pkg.parsing.ParsingUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
index 6c22f82..8268f0f 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java
@@ -33,7 +33,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.pm.pkg.component.ParsedIntentInfo;
import com.android.internal.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingUtils;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
index 0f2b49b..4b45d37 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java
@@ -33,7 +33,7 @@
import com.android.internal.R;
import com.android.internal.pm.pkg.component.ParsedPermission;
import com.android.internal.pm.pkg.component.ParsedPermissionGroup;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingUtils;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
index 766fb90..a849549 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java
@@ -28,9 +28,9 @@
import com.android.internal.R;
import com.android.internal.pm.pkg.component.ParsedProcess;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.XmlUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingUtils;
import org.xmlpull.v1.XmlPullParser;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
index b66db4f..0b28a12 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java
@@ -35,7 +35,7 @@
import com.android.internal.R;
import com.android.internal.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingUtils;
import org.xmlpull.v1.XmlPullParser;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
index 1b42184..171ef59 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
@@ -33,7 +33,7 @@
import com.android.internal.R;
import com.android.internal.pm.pkg.component.ParsedService;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.pkg.parsing.ParsingUtils;
import org.xmlpull.v1.XmlPullParser;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index e4594c5..722350a 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -89,6 +89,7 @@
import com.android.internal.R;
import com.android.internal.os.ClassLoaderFactory;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.pm.pkg.component.ParsedApexSystemService;
import com.android.internal.pm.pkg.component.ParsedAttribution;
@@ -102,11 +103,11 @@
import com.android.internal.pm.pkg.component.ParsedProvider;
import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.pm.pkg.component.ParsedUsesPermission;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.SharedUidMigration;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.CompatibilityPermissionInfo;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
import com.android.server.pm.pkg.component.ComponentParseUtils;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
index 2cfffb3..1d15955 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
@@ -31,6 +31,7 @@
import android.util.Slog;
import com.android.internal.pm.pkg.component.ParsedIntentInfo;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.internal.util.Parcelling;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 986735f..73c4224 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3537,7 +3537,8 @@
mDisplayManager.setBrightness(screenDisplayId, adjustedLinearBrightness);
Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG);
- intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION
+ | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true);
startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
logKeyboardSystemsEvent(event, KeyboardLogEvent.getBrightnessEvent(keyCode));
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index e3f3638..5d90851 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -32,7 +32,6 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
@@ -41,6 +40,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ConcurrentUtils;
+import com.android.server.utils.UserSettingDeviceConfigMediator;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -809,12 +809,13 @@
private static Policy fromSettings(String settings, String deviceSpecificSettings,
DeviceConfig.Properties properties, String configSuffix, Policy defaultPolicy) {
- final KeyValueListParser parser = new KeyValueListParser(',');
+ final UserSettingDeviceConfigMediator userSettingDeviceConfigMediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
configSuffix = TextUtils.emptyIfNull(configSuffix);
// Device-specific parameters.
try {
- parser.setString(deviceSpecificSettings == null ? "" : deviceSpecificSettings);
+ userSettingDeviceConfigMediator.setSettingsString(deviceSpecificSettings);
} catch (IllegalArgumentException e) {
Slog.wtf(TAG, "Bad device specific battery saver constants: "
+ deviceSpecificSettings);
@@ -822,68 +823,58 @@
// Non-device-specific parameters.
try {
- parser.setString(settings == null ? "" : settings);
+ userSettingDeviceConfigMediator.setSettingsString(settings);
+ userSettingDeviceConfigMediator.setDeviceConfigProperties(properties);
} catch (IllegalArgumentException e) {
Slog.wtf(TAG, "Bad battery saver constants: " + settings);
}
// The Settings value overrides everything, since that will be set by the user.
// The DeviceConfig value takes second place, with the default as the last choice.
- final float adjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR,
- properties.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR + configSuffix,
- defaultPolicy.adjustBrightnessFactor));
- final boolean advertiseIsEnabled = parser.getBoolean(KEY_ADVERTISE_IS_ENABLED,
- properties.getBoolean(KEY_ADVERTISE_IS_ENABLED + configSuffix,
- defaultPolicy.advertiseIsEnabled));
- final boolean deferFullBackup = parser.getBoolean(KEY_DEFER_FULL_BACKUP,
- properties.getBoolean(KEY_DEFER_FULL_BACKUP + configSuffix,
- defaultPolicy.deferFullBackup));
- final boolean deferKeyValueBackup = parser.getBoolean(KEY_DEFER_KEYVALUE_BACKUP,
- properties.getBoolean(KEY_DEFER_KEYVALUE_BACKUP + configSuffix,
- defaultPolicy.deferKeyValueBackup));
- final boolean disableAnimation = parser.getBoolean(KEY_DISABLE_ANIMATION,
- properties.getBoolean(KEY_DISABLE_ANIMATION + configSuffix,
- defaultPolicy.disableAnimation));
- final boolean disableAod = parser.getBoolean(KEY_DISABLE_AOD,
- properties.getBoolean(KEY_DISABLE_AOD + configSuffix,
- defaultPolicy.disableAod));
- final boolean disableLaunchBoost = parser.getBoolean(KEY_DISABLE_LAUNCH_BOOST,
- properties.getBoolean(KEY_DISABLE_LAUNCH_BOOST + configSuffix,
- defaultPolicy.disableLaunchBoost));
- final boolean disableOptionalSensors = parser.getBoolean(KEY_DISABLE_OPTIONAL_SENSORS,
- properties.getBoolean(KEY_DISABLE_OPTIONAL_SENSORS + configSuffix,
- defaultPolicy.disableOptionalSensors));
- final boolean disableVibrationConfig = parser.getBoolean(KEY_DISABLE_VIBRATION,
- properties.getBoolean(KEY_DISABLE_VIBRATION + configSuffix,
- defaultPolicy.disableVibration));
- final boolean enableBrightnessAdjustment = parser.getBoolean(
- KEY_ENABLE_BRIGHTNESS_ADJUSTMENT,
- properties.getBoolean(KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + configSuffix,
- defaultPolicy.enableAdjustBrightness));
- final boolean enableDataSaver = parser.getBoolean(KEY_ENABLE_DATASAVER,
- properties.getBoolean(KEY_ENABLE_DATASAVER + configSuffix,
- defaultPolicy.enableDataSaver));
- final boolean enableFirewall = parser.getBoolean(KEY_ENABLE_FIREWALL,
- properties.getBoolean(KEY_ENABLE_FIREWALL + configSuffix,
- defaultPolicy.enableFirewall));
- final boolean enableNightMode = parser.getBoolean(KEY_ENABLE_NIGHT_MODE,
- properties.getBoolean(KEY_ENABLE_NIGHT_MODE + configSuffix,
- defaultPolicy.enableNightMode));
- final boolean enableQuickDoze = parser.getBoolean(KEY_ENABLE_QUICK_DOZE,
- properties.getBoolean(KEY_ENABLE_QUICK_DOZE + configSuffix,
- defaultPolicy.enableQuickDoze));
- final boolean forceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY,
- properties.getBoolean(KEY_FORCE_ALL_APPS_STANDBY + configSuffix,
- defaultPolicy.forceAllAppsStandby));
- final boolean forceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK,
- properties.getBoolean(KEY_FORCE_BACKGROUND_CHECK + configSuffix,
- defaultPolicy.forceBackgroundCheck));
- final int locationMode = parser.getInt(KEY_LOCATION_MODE,
- properties.getInt(KEY_LOCATION_MODE + configSuffix,
- defaultPolicy.locationMode));
- final int soundTriggerMode = parser.getInt(KEY_SOUNDTRIGGER_MODE,
- properties.getInt(KEY_SOUNDTRIGGER_MODE + configSuffix,
- defaultPolicy.soundTriggerMode));
+ final float adjustBrightnessFactor = userSettingDeviceConfigMediator.getFloat(
+ KEY_ADJUST_BRIGHTNESS_FACTOR + configSuffix,
+ defaultPolicy.adjustBrightnessFactor);
+ final boolean advertiseIsEnabled = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ADVERTISE_IS_ENABLED + configSuffix,
+ defaultPolicy.advertiseIsEnabled);
+ final boolean deferFullBackup = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DEFER_FULL_BACKUP + configSuffix, defaultPolicy.deferFullBackup);
+ final boolean deferKeyValueBackup = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DEFER_KEYVALUE_BACKUP + configSuffix,
+ defaultPolicy.deferKeyValueBackup);
+ final boolean disableAnimation = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DISABLE_ANIMATION + configSuffix, defaultPolicy.disableAnimation);
+ final boolean disableAod = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DISABLE_AOD + configSuffix, defaultPolicy.disableAod);
+ final boolean disableLaunchBoost = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DISABLE_LAUNCH_BOOST + configSuffix,
+ defaultPolicy.disableLaunchBoost);
+ final boolean disableOptionalSensors = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DISABLE_OPTIONAL_SENSORS + configSuffix,
+ defaultPolicy.disableOptionalSensors);
+ final boolean disableVibrationConfig = userSettingDeviceConfigMediator.getBoolean(
+ KEY_DISABLE_VIBRATION + configSuffix, defaultPolicy.disableVibration);
+ final boolean enableBrightnessAdjustment = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + configSuffix,
+ defaultPolicy.enableAdjustBrightness);
+ final boolean enableDataSaver = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ENABLE_DATASAVER + configSuffix, defaultPolicy.enableDataSaver);
+ final boolean enableFirewall = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ENABLE_FIREWALL + configSuffix, defaultPolicy.enableFirewall);
+ final boolean enableNightMode = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ENABLE_NIGHT_MODE + configSuffix, defaultPolicy.enableNightMode);
+ final boolean enableQuickDoze = userSettingDeviceConfigMediator.getBoolean(
+ KEY_ENABLE_QUICK_DOZE + configSuffix, defaultPolicy.enableQuickDoze);
+ final boolean forceAllAppsStandby = userSettingDeviceConfigMediator.getBoolean(
+ KEY_FORCE_ALL_APPS_STANDBY + configSuffix,
+ defaultPolicy.forceAllAppsStandby);
+ final boolean forceBackgroundCheck = userSettingDeviceConfigMediator.getBoolean(
+ KEY_FORCE_BACKGROUND_CHECK + configSuffix,
+ defaultPolicy.forceBackgroundCheck);
+ final int locationMode = userSettingDeviceConfigMediator.getInt(
+ KEY_LOCATION_MODE + configSuffix, defaultPolicy.locationMode);
+ final int soundTriggerMode = userSettingDeviceConfigMediator.getInt(
+ KEY_SOUNDTRIGGER_MODE + configSuffix, defaultPolicy.soundTriggerMode);
return new Policy(
adjustBrightnessFactor,
advertiseIsEnabled,
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
index aadd03b..894226c 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
@@ -33,6 +33,7 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -265,4 +266,11 @@
ipw.decreaseIndent();
}
}
+
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ dump(new IndentingPrintWriter(sw));
+ return sw.toString();
+ }
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
index f9d57e4..a8eda3c 100644
--- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
@@ -44,11 +44,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.power.EnergyConsumerStats;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
-import libcore.util.EmptyArray;
-
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@@ -128,9 +125,6 @@
private boolean mUseLatestStates = true;
@GuardedBy("this")
- private final IntArray mUidsToRemove = new IntArray();
-
- @GuardedBy("this")
private Future<?> mWakelockChangesUpdate;
@GuardedBy("this")
@@ -260,7 +254,6 @@
@Override
public synchronized Future<?> scheduleCpuSyncDueToRemovedUid(int uid) {
- mUidsToRemove.add(uid);
return scheduleSyncLocked("remove-uid", UPDATE_CPU);
}
@@ -459,7 +452,6 @@
// Capture a snapshot of the state we are meant to process.
final int updateFlags;
final String reason;
- final int[] uidsToRemove;
final boolean onBattery;
final boolean onBatteryScreenOff;
final int screenState;
@@ -468,7 +460,6 @@
synchronized (BatteryExternalStatsWorker.this) {
updateFlags = mUpdateFlags;
reason = mCurrentReason;
- uidsToRemove = mUidsToRemove.size() > 0 ? mUidsToRemove.toArray() : EmptyArray.INT;
onBattery = mOnBattery;
onBatteryScreenOff = mOnBatteryScreenOff;
screenState = mScreenState;
@@ -476,7 +467,6 @@
useLatestStates = mUseLatestStates;
mUpdateFlags = 0;
mCurrentReason = null;
- mUidsToRemove.clear();
mCurrentFuture = null;
mUseLatestStates = true;
if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) {
@@ -512,12 +502,6 @@
// Clean up any UIDs if necessary.
synchronized (mStats) {
- for (int uid : uidsToRemove) {
- FrameworkStatsLog.write(FrameworkStatsLog.ISOLATED_UID_CHANGED, -1, uid,
- FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__REMOVED);
- mStats.maybeRemoveIsolatedUidLocked(uid, SystemClock.elapsedRealtime(),
- SystemClock.uptimeMillis());
- }
mStats.clearPendingRemovedUidsLocked();
}
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsDumpHelperImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsDumpHelperImpl.java
new file mode 100644
index 0000000..ad146af
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsDumpHelperImpl.java
@@ -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.server.power.stats;
+
+import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+
+public class BatteryStatsDumpHelperImpl implements BatteryStats.BatteryStatsDumpHelper {
+ private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
+
+ public BatteryStatsDumpHelperImpl(BatteryUsageStatsProvider batteryUsageStatsProvider) {
+ mBatteryUsageStatsProvider = batteryUsageStatsProvider;
+ }
+
+ @Override
+ public BatteryUsageStats getBatteryUsageStats(BatteryStats batteryStats, boolean detailed) {
+ BatteryUsageStatsQuery.Builder builder = new BatteryUsageStatsQuery.Builder()
+ .setMaxStatsAgeMs(0);
+ if (detailed) {
+ builder.includePowerModels().includeProcessStateData().includeVirtualUids();
+ }
+ return mBatteryUsageStatsProvider.getBatteryUsageStats((BatteryStatsImpl) batteryStats,
+ builder.build());
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index eb40104..0491c14 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -185,7 +185,7 @@
// TODO: remove "tcp" from network methods, since we measure total stats.
// Current on-disk Parcel version. Must be updated when the format of the parcelable changes
- public static final int VERSION = 213;
+ public static final int VERSION = 214;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -220,6 +220,8 @@
public static final int RESET_REASON_FULL_CHARGE = 3;
public static final int RESET_REASON_ENERGY_CONSUMER_BUCKETS_CHANGE = 4;
public static final int RESET_REASON_PLUGGED_IN_FOR_LONG_DURATION = 5;
+ @NonNull
+ private final MonotonicClock mMonotonicClock;
protected Clock mClock;
@@ -393,19 +395,9 @@
}
}
- /**
- * Listener for the battery stats reset.
- */
- public interface BatteryResetListener {
-
- /**
- * Callback invoked immediately prior to resetting battery stats.
- * @param resetReason One of the RESET_REASON_* constants.
- */
- void prepareForBatteryStatsReset(int resetReason);
- }
-
- private BatteryResetListener mBatteryResetListener;
+ private boolean mSaveBatteryUsageStatsOnReset;
+ private BatteryUsageStatsProvider mBatteryUsageStatsProvider;
+ private PowerStatsStore mPowerStatsStore;
public interface BatteryCallback {
public void batteryNeedsCpuUpdate();
@@ -787,13 +779,10 @@
private BatteryCallback mCallback;
/**
- * Mapping isolated uids to the actual owning app uid.
+ * Mapping child uids to their parent uid.
*/
- private final SparseIntArray mIsolatedUids = new SparseIntArray();
- /**
- * Internal reference count of isolated uids.
- */
- private final SparseIntArray mIsolatedUidRefCounts = new SparseIntArray();
+ @VisibleForTesting
+ protected final PowerStatsUidResolver mPowerStatsUidResolver;
/**
* The statistics we have collected organized by uids.
@@ -874,6 +863,8 @@
long mUptimeStartUs;
long mRealtimeUs;
long mRealtimeStartUs;
+ long mMonotonicStartTime;
+ long mMonotonicEndTime = MonotonicClock.UNDEFINED;
int mWakeLockNesting;
boolean mWakeLockImportant;
@@ -1724,25 +1715,26 @@
}
@VisibleForTesting
- public BatteryStatsImpl(Clock clock, File historyDirectory) {
+ public BatteryStatsImpl(Clock clock, File historyDirectory, @NonNull Handler handler,
+ @NonNull PowerStatsUidResolver powerStatsUidResolver) {
init(clock);
mBatteryStatsConfig = new BatteryStatsConfig.Builder().build();
- mHandler = null;
+ mHandler = handler;
+ mPowerStatsUidResolver = powerStatsUidResolver;
mConstants = new Constants(mHandler);
mStartClockTimeMs = clock.currentTimeMillis();
mDailyFile = null;
+ mMonotonicClock = new MonotonicClock(0, mClock);
if (historyDirectory == null) {
mCheckinFile = null;
mStatsFile = null;
mHistory = new BatteryStatsHistory(mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock,
- new MonotonicClock(0, mClock));
+ mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, mMonotonicClock);
} else {
mCheckinFile = new AtomicFile(new File(historyDirectory, "batterystats-checkin.bin"));
mStatsFile = new AtomicFile(new File(historyDirectory, "batterystats.bin"));
mHistory = new BatteryStatsHistory(historyDirectory, mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock,
- new MonotonicClock(0, mClock));
+ mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, mMonotonicClock);
}
mPlatformIdleStateCallback = null;
mEnergyConsumerRetriever = null;
@@ -4278,92 +4270,51 @@
}
}
- @GuardedBy("this")
- public void addIsolatedUidLocked(int isolatedUid, int appUid) {
- addIsolatedUidLocked(isolatedUid, appUid,
- mClock.elapsedRealtime(), mClock.uptimeMillis());
+ private void onIsolatedUidAdded(int isolatedUid, int parentUid) {
+ long realtime = mClock.elapsedRealtime();
+ long uptime = mClock.uptimeMillis();
+ synchronized (this) {
+ getUidStatsLocked(parentUid, realtime, uptime).addIsolatedUid(isolatedUid);
+ }
}
- @GuardedBy("this")
- @SuppressWarnings("GuardedBy") // errorprone false positive on u.addIsolatedUid
- public void addIsolatedUidLocked(int isolatedUid, int appUid,
- long elapsedRealtimeMs, long uptimeMs) {
- mIsolatedUids.put(isolatedUid, appUid);
- mIsolatedUidRefCounts.put(isolatedUid, 1);
- final Uid u = getUidStatsLocked(appUid, elapsedRealtimeMs, uptimeMs);
- u.addIsolatedUid(isolatedUid);
+ private void onBeforeIsolatedUidRemoved(int isolatedUid, int parentUid) {
+ long realtime = mClock.elapsedRealtime();
+ mPowerStatsUidResolver.retainIsolatedUid(isolatedUid);
+ synchronized (this) {
+ mPendingRemovedUids.add(new UidToRemove(isolatedUid, realtime));
+ }
+ if (mExternalSync != null) {
+ mExternalSync.scheduleCpuSyncDueToRemovedUid(isolatedUid);
+ }
}
- /**
- * Schedules a read of the latest cpu times before removing the isolated UID.
- * @see #removeIsolatedUidLocked(int, int, int)
- */
- public void scheduleRemoveIsolatedUidLocked(int isolatedUid, int appUid) {
- int curUid = mIsolatedUids.get(isolatedUid, -1);
- if (curUid == appUid) {
- if (mExternalSync != null) {
- mExternalSync.scheduleCpuSyncDueToRemovedUid(isolatedUid);
- }
+ private void onAfterIsolatedUidRemoved(int isolatedUid, int parentUid) {
+ long realtime = mClock.elapsedRealtime();
+ long uptime = mClock.uptimeMillis();
+ synchronized (this) {
+ getUidStatsLocked(parentUid, realtime, uptime).removeIsolatedUid(isolatedUid);
}
}
/**
* Isolated uid should only be removed after all wakelocks associated with the uid are stopped
* and the cpu time-in-state has been read one last time for the uid.
- *
- * @see #scheduleRemoveIsolatedUidLocked(int, int)
- *
- * @return true if the isolated uid is actually removed.
*/
@GuardedBy("this")
- public boolean maybeRemoveIsolatedUidLocked(int isolatedUid, long elapsedRealtimeMs,
- long uptimeMs) {
- final int refCount = mIsolatedUidRefCounts.get(isolatedUid, 0) - 1;
- if (refCount > 0) {
- // Isolated uid is still being tracked
- mIsolatedUidRefCounts.put(isolatedUid, refCount);
- return false;
- }
-
- final int idx = mIsolatedUids.indexOfKey(isolatedUid);
- if (idx >= 0) {
- final int ownerUid = mIsolatedUids.valueAt(idx);
- final Uid u = getUidStatsLocked(ownerUid, elapsedRealtimeMs, uptimeMs);
- u.removeIsolatedUid(isolatedUid);
- mIsolatedUids.removeAt(idx);
- mIsolatedUidRefCounts.delete(isolatedUid);
- } else {
- Slog.w(TAG, "Attempted to remove untracked isolated uid (" + isolatedUid + ")");
- }
- mPendingRemovedUids.add(new UidToRemove(isolatedUid, elapsedRealtimeMs));
-
- return true;
- }
-
- /**
- * Increment the ref count for an isolated uid.
- * call #maybeRemoveIsolatedUidLocked to decrement.
- */
- public void incrementIsolatedUidRefCount(int uid) {
- final int refCount = mIsolatedUidRefCounts.get(uid, 0);
- if (refCount <= 0) {
- // Uid is not mapped or referenced
- Slog.w(TAG,
- "Attempted to increment ref counted of untracked isolated uid (" + uid + ")");
- return;
- }
- mIsolatedUidRefCounts.put(uid, refCount + 1);
+ public void releaseIsolatedUidLocked(int isolatedUid, long elapsedRealtimeMs, long uptimeMs) {
+ mPowerStatsUidResolver.releaseIsolatedUid(isolatedUid);
}
private int mapUid(int uid) {
if (Process.isSdkSandboxUid(uid)) {
return Process.getAppUidForSdkSandboxUid(uid);
}
- return mapIsolatedUid(uid);
+ return mPowerStatsUidResolver.mapUid(uid);
}
private int mapIsolatedUid(int uid) {
- return mIsolatedUids.get(/*key=*/uid, /*valueIfKeyNotFound=*/uid);
+ return mPowerStatsUidResolver.mapUid(uid);
}
@GuardedBy("this")
@@ -4745,7 +4696,7 @@
if (mappedUid != uid) {
// Prevent the isolated uid mapping from being removed while the wakelock is
// being held.
- incrementIsolatedUidRefCount(uid);
+ mPowerStatsUidResolver.retainIsolatedUid(uid);
}
if (mOnBatteryScreenOffTimeBase.isRunning()) {
// We only update the cpu time when a wake lock is acquired if the screen is off.
@@ -4825,7 +4776,7 @@
if (mappedUid != uid) {
// Decrement the ref count for the isolated uid and delete the mapping if uneeded.
- maybeRemoveIsolatedUidLocked(uid, elapsedRealtimeMs, uptimeMs);
+ releaseIsolatedUidLocked(uid, elapsedRealtimeMs, uptimeMs);
}
}
}
@@ -4996,7 +4947,7 @@
if (mappedUid != uid) {
// Prevent the isolated uid mapping from being removed while the wakelock is
// being held.
- incrementIsolatedUidRefCount(uid);
+ mPowerStatsUidResolver.retainIsolatedUid(uid);
}
}
@@ -5048,7 +4999,7 @@
historyName, mappedUid);
if (mappedUid != uid) {
// Decrement the ref count for the isolated uid and delete the mapping if uneeded.
- maybeRemoveIsolatedUidLocked(uid, elapsedRealtimeMs, uptimeMs);
+ releaseIsolatedUidLocked(uid, elapsedRealtimeMs, uptimeMs);
}
}
@@ -7642,35 +7593,53 @@
/**
* Returns the names of custom power components.
*/
- @GuardedBy("this")
@Override
public @NonNull String[] getCustomEnergyConsumerNames() {
- if (mEnergyConsumerStatsConfig == null) {
- return new String[0];
- }
- final String[] names = mEnergyConsumerStatsConfig.getCustomBucketNames();
- for (int i = 0; i < names.length; i++) {
- if (TextUtils.isEmpty(names[i])) {
- names[i] = "CUSTOM_" + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i;
+ synchronized (this) {
+ if (mEnergyConsumerStatsConfig == null) {
+ return new String[0];
}
+ final String[] names = mEnergyConsumerStatsConfig.getCustomBucketNames();
+ for (int i = 0; i < names.length; i++) {
+ if (TextUtils.isEmpty(names[i])) {
+ names[i] = "CUSTOM_" + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i;
+ }
+ }
+ return names;
}
- return names;
}
- @GuardedBy("this")
- @Override public long getStartClockTime() {
- final long currentTimeMs = mClock.currentTimeMillis();
- if ((currentTimeMs > MILLISECONDS_IN_YEAR
- && mStartClockTimeMs < (currentTimeMs - MILLISECONDS_IN_YEAR))
+ @Override
+ public long getStartClockTime() {
+ synchronized (this) {
+ final long currentTimeMs = mClock.currentTimeMillis();
+ if ((currentTimeMs > MILLISECONDS_IN_YEAR
+ && mStartClockTimeMs < (currentTimeMs - MILLISECONDS_IN_YEAR))
|| (mStartClockTimeMs > currentTimeMs)) {
- // If the start clock time has changed by more than a year, then presumably
- // the previous time was completely bogus. So we are going to figure out a
- // new time based on how much time has elapsed since we started counting.
- mHistory.recordCurrentTimeChange(mClock.elapsedRealtime(), mClock.uptimeMillis(),
- currentTimeMs);
- adjustStartClockTime(currentTimeMs);
+ // If the start clock time has changed by more than a year, then presumably
+ // the previous time was completely bogus. So we are going to figure out a
+ // new time based on how much time has elapsed since we started counting.
+ mHistory.recordCurrentTimeChange(mClock.elapsedRealtime(), mClock.uptimeMillis(),
+ currentTimeMs);
+ adjustStartClockTime(currentTimeMs);
+ }
+ return mStartClockTimeMs;
}
- return mStartClockTimeMs;
+ }
+
+ /**
+ * Returns the monotonic time when the BatteryStats session started.
+ */
+ public long getMonotonicStartTime() {
+ return mMonotonicStartTime;
+ }
+
+ /**
+ * Returns the monotonic time when the BatteryStats session ended, or
+ * {@link MonotonicClock#UNDEFINED} if the session is still ongoing.
+ */
+ public long getMonotonicEndTime() {
+ return mMonotonicEndTime;
}
@Override public String getStartPlatformVersion() {
@@ -8197,7 +8166,9 @@
return mProportionalSystemServiceUsage;
}
- @GuardedBy("mBsi")
+ /**
+ * Adds isolated UID to the list of children.
+ */
public void addIsolatedUid(int isolatedUid) {
if (mChildUids == null) {
mChildUids = new SparseArray<>();
@@ -8207,6 +8178,9 @@
mChildUids.put(isolatedUid, new ChildUid());
}
+ /**
+ * Removes isolated UID from the list of children.
+ */
public void removeIsolatedUid(int isolatedUid) {
final int idx = mChildUids == null ? -1 : mChildUids.indexOfKey(isolatedUid);
if (idx < 0) {
@@ -10910,15 +10884,18 @@
@NonNull Handler handler, @Nullable PlatformIdleStateCallback cb,
@Nullable EnergyStatsRetriever energyStatsCb,
@NonNull UserInfoProvider userInfoProvider, @NonNull PowerProfile powerProfile,
- @NonNull CpuScalingPolicies cpuScalingPolicies) {
+ @NonNull CpuScalingPolicies cpuScalingPolicies,
+ @NonNull PowerStatsUidResolver powerStatsUidResolver) {
init(clock);
mBatteryStatsConfig = config;
+ mMonotonicClock = monotonicClock;
mHandler = new MyHandler(handler.getLooper());
mConstants = new Constants(mHandler);
mPowerProfile = powerProfile;
mCpuScalingPolicies = cpuScalingPolicies;
+ mPowerStatsUidResolver = powerStatsUidResolver;
initPowerProfile();
@@ -10927,17 +10904,17 @@
mCheckinFile = null;
mDailyFile = null;
mHistory = new BatteryStatsHistory(mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, monotonicClock);
+ mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, mMonotonicClock);
} else {
mStatsFile = new AtomicFile(new File(systemDir, "batterystats.bin"));
mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));
mHistory = new BatteryStatsHistory(systemDir, mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, monotonicClock);
+ mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, mMonotonicClock);
}
mCpuPowerStatsCollector = new CpuPowerStatsCollector(mCpuScalingPolicies, mPowerProfile,
- () -> mBatteryVoltageMv, mHandler,
+ mPowerStatsUidResolver, () -> mBatteryVoltageMv, mHandler,
mBatteryStatsConfig.getPowerStatsThrottlePeriodCpu());
mCpuPowerStatsCollector.addConsumer(this::recordPowerStats);
@@ -10954,6 +10931,23 @@
mEnergyConsumerRetriever = energyStatsCb;
mUserInfoProvider = userInfoProvider;
+ mPowerStatsUidResolver.addListener(new PowerStatsUidResolver.Listener() {
+ @Override
+ public void onIsolatedUidAdded(int isolatedUid, int parentUid) {
+ BatteryStatsImpl.this.onIsolatedUidAdded(isolatedUid, parentUid);
+ }
+
+ @Override
+ public void onBeforeIsolatedUidRemoved(int isolatedUid, int parentUid) {
+ BatteryStatsImpl.this.onBeforeIsolatedUidRemoved(isolatedUid, parentUid);
+ }
+
+ @Override
+ public void onAfterIsolatedUidRemoved(int isolatedUid, int parentUid) {
+ BatteryStatsImpl.this.onAfterIsolatedUidRemoved(isolatedUid, parentUid);
+ }
+ });
+
// Notify statsd that the system is initially not in doze.
mDeviceIdleMode = DEVICE_IDLE_MODE_OFF;
FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode);
@@ -11497,6 +11491,7 @@
mUptimeUs = 0;
mRealtimeStartUs = realtimeUs;
mUptimeStartUs = uptimeUs;
+ mMonotonicStartTime = mMonotonicClock.monotonicTime();
}
void initDischarge(long elapsedRealtimeUs) {
@@ -11517,8 +11512,17 @@
mDischargeCounter.reset(false, elapsedRealtimeUs);
}
- public void setBatteryResetListener(BatteryResetListener batteryResetListener) {
- mBatteryResetListener = batteryResetListener;
+ /**
+ * Associates the BatteryStatsImpl object with a BatteryUsageStatsProvider and PowerStatsStore
+ * to allow for a snapshot of battery usage stats to be taken and stored just before battery
+ * reset.
+ */
+ public void saveBatteryUsageStatsOnReset(
+ @NonNull BatteryUsageStatsProvider batteryUsageStatsProvider,
+ @NonNull PowerStatsStore powerStatsStore) {
+ mSaveBatteryUsageStatsOnReset = true;
+ mBatteryUsageStatsProvider = batteryUsageStatsProvider;
+ mPowerStatsStore = powerStatsStore;
}
@GuardedBy("this")
@@ -11557,9 +11561,7 @@
@GuardedBy("this")
private void resetAllStatsLocked(long uptimeMillis, long elapsedRealtimeMillis,
int resetReason) {
- if (mBatteryResetListener != null) {
- mBatteryResetListener.prepareForBatteryStatsReset(resetReason);
- }
+ saveBatteryUsageStatsOnReset(resetReason);
final long uptimeUs = uptimeMillis * 1000;
final long elapsedRealtimeUs = elapsedRealtimeMillis * 1000;
@@ -11707,6 +11709,31 @@
mHandler.sendEmptyMessage(MSG_REPORT_RESET_STATS);
}
+ private void saveBatteryUsageStatsOnReset(int resetReason) {
+ if (!mSaveBatteryUsageStatsOnReset
+ || resetReason == BatteryStatsImpl.RESET_REASON_CORRUPT_FILE) {
+ return;
+ }
+
+ final BatteryUsageStats batteryUsageStats;
+ synchronized (this) {
+ batteryUsageStats = mBatteryUsageStatsProvider.getBatteryUsageStats(this,
+ new BatteryUsageStatsQuery.Builder()
+ .setMaxStatsAgeMs(0)
+ .includePowerModels()
+ .includeProcessStateData()
+ .build());
+ }
+
+ // TODO(b/188068523): BatteryUsageStats should use monotonic time for start and end
+ // Once that change is made, we will be able to use the BatteryUsageStats' monotonic
+ // start time
+ long monotonicStartTime =
+ mMonotonicClock.monotonicTime() - batteryUsageStats.getStatsDuration();
+ mHandler.post(() ->
+ mPowerStatsStore.storeBatteryUsageStats(monotonicStartTime, batteryUsageStats));
+ }
+
@GuardedBy("this")
private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) {
for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
@@ -15137,6 +15164,7 @@
if (mKernelSingleUidTimeReader != null) {
mKernelSingleUidTimeReader.removeUidsInRange(startUid, endUid);
}
+ mPowerStatsUidResolver.releaseUidsInRange(startUid, endUid);
// Treat as one. We don't know how many uids there are in between.
mNumUidsRemoved++;
} else {
@@ -15192,10 +15220,11 @@
mShuttingDown = true;
}
- @GuardedBy("this")
@Override
public boolean isProcessStateDataAvailable() {
- return trackPerProcStateCpuTimes();
+ synchronized (this) {
+ return trackPerProcStateCpuTimes();
+ }
}
@GuardedBy("this")
@@ -15862,6 +15891,8 @@
mUptimeUs = in.readLong();
mRealtimeUs = in.readLong();
mStartClockTimeMs = in.readLong();
+ mMonotonicStartTime = in.readLong();
+ mMonotonicEndTime = in.readLong();
mStartPlatformVersion = in.readString();
mEndPlatformVersion = in.readString();
mOnBatteryTimeBase.readSummaryFromParcel(in);
@@ -16382,6 +16413,8 @@
out.writeLong(computeUptime(nowUptime, STATS_SINCE_CHARGED));
out.writeLong(computeRealtime(nowRealtime, STATS_SINCE_CHARGED));
out.writeLong(mStartClockTimeMs);
+ out.writeLong(mMonotonicStartTime);
+ out.writeLong(mMonotonicClock.monotonicTime());
out.writeString(mStartPlatformVersion);
out.writeString(mEndPlatformVersion);
mOnBatteryTimeBase.writeSummaryToParcel(out, nowUptime, nowRealtime);
@@ -16912,7 +16945,8 @@
}
@GuardedBy("this")
- public void dump(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
+ public void dump(Context context, PrintWriter pw, int flags, int reqUid, long histStart,
+ BatteryStatsDumpHelper dumpHelper) {
if (DEBUG) {
pw.println("mOnBatteryTimeBase:");
mOnBatteryTimeBase.dump(pw, " ");
@@ -16984,7 +17018,7 @@
pr.println("*** Camera timer:");
mCameraOnTimer.logState(pr, " ");
}
- super.dump(context, pw, flags, reqUid, histStart);
+ super.dump(context, pw, flags, reqUid, histStart, dumpHelper);
synchronized (this) {
pw.print("Per process state tracking available: ");
@@ -16998,15 +17032,7 @@
pw.print("UIDs removed since the later of device start or stats reset: ");
pw.println(mNumUidsRemoved);
- pw.println("Currently mapped isolated uids:");
- final int numIsolatedUids = mIsolatedUids.size();
- for (int i = 0; i < numIsolatedUids; i++) {
- final int isolatedUid = mIsolatedUids.keyAt(i);
- final int ownerUid = mIsolatedUids.valueAt(i);
- final int refCount = mIsolatedUidRefCounts.get(isolatedUid);
- pw.println(
- " " + isolatedUid + "->" + ownerUid + " (ref count = " + refCount + ")");
- }
+ mPowerStatsUidResolver.dump(pw);
pw.println();
dumpConstantsLocked(pw);
@@ -17020,15 +17046,4 @@
dumpEnergyConsumerStatsLocked(pw);
}
}
-
- @Override
- protected BatteryUsageStats getBatteryUsageStats(Context context, boolean detailed) {
- final BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, this);
- BatteryUsageStatsQuery.Builder builder = new BatteryUsageStatsQuery.Builder()
- .setMaxStatsAgeMs(0);
- if (detailed) {
- builder.includePowerModels().includeProcessStateData().includeVirtualUids();
- }
- return provider.getBatteryUsageStats(builder.build());
- }
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index 83d7d72..303c245 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -23,14 +23,12 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
-import android.os.SystemClock;
import android.os.UidBatteryConsumer;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.Clock;
import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
@@ -45,27 +43,25 @@
public class BatteryUsageStatsProvider {
private static final String TAG = "BatteryUsageStatsProv";
private final Context mContext;
- private final BatteryStats mStats;
+ private boolean mPowerStatsExporterEnabled;
+ private final PowerStatsExporter mPowerStatsExporter;
private final PowerStatsStore mPowerStatsStore;
private final PowerProfile mPowerProfile;
private final CpuScalingPolicies mCpuScalingPolicies;
+ private final Clock mClock;
private final Object mLock = new Object();
private List<PowerCalculator> mPowerCalculators;
- public BatteryUsageStatsProvider(Context context, BatteryStats stats) {
- this(context, stats, null);
- }
-
- @VisibleForTesting
- public BatteryUsageStatsProvider(Context context, BatteryStats stats,
- PowerStatsStore powerStatsStore) {
+ public BatteryUsageStatsProvider(Context context,
+ PowerStatsExporter powerStatsExporter,
+ PowerProfile powerProfile, CpuScalingPolicies cpuScalingPolicies,
+ PowerStatsStore powerStatsStore, Clock clock) {
mContext = context;
- mStats = stats;
+ mPowerStatsExporter = powerStatsExporter;
mPowerStatsStore = powerStatsStore;
- mPowerProfile = stats instanceof BatteryStatsImpl
- ? ((BatteryStatsImpl) stats).getPowerProfile()
- : new PowerProfile(context);
- mCpuScalingPolicies = stats.getCpuScalingPolicies();
+ mPowerProfile = powerProfile;
+ mCpuScalingPolicies = cpuScalingPolicies;
+ mClock = clock;
}
private List<PowerCalculator> getPowerCalculators() {
@@ -75,7 +71,10 @@
// Power calculators are applied in the order of registration
mPowerCalculators.add(new BatteryChargeCalculator());
- mPowerCalculators.add(new CpuPowerCalculator(mCpuScalingPolicies, mPowerProfile));
+ if (mPowerStatsExporterEnabled) {
+ mPowerCalculators.add(
+ new CpuPowerCalculator(mCpuScalingPolicies, mPowerProfile));
+ }
mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
if (!BatteryStats.checkWifiOnly(mContext)) {
@@ -111,27 +110,28 @@
* Returns true if the last update was too long ago for the tolerances specified
* by the supplied queries.
*/
- public boolean shouldUpdateStats(List<BatteryUsageStatsQuery> queries,
- long lastUpdateTimeStampMs) {
+ public static boolean shouldUpdateStats(List<BatteryUsageStatsQuery> queries,
+ long elapsedRealtime, long lastUpdateTimeStampMs) {
long allowableStatsAge = Long.MAX_VALUE;
for (int i = queries.size() - 1; i >= 0; i--) {
BatteryUsageStatsQuery query = queries.get(i);
allowableStatsAge = Math.min(allowableStatsAge, query.getMaxStatsAge());
}
- return elapsedRealtime() - lastUpdateTimeStampMs > allowableStatsAge;
+ return elapsedRealtime - lastUpdateTimeStampMs > allowableStatsAge;
}
/**
* Returns snapshots of battery attribution data, one per supplied query.
*/
- public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
+ public List<BatteryUsageStats> getBatteryUsageStats(BatteryStatsImpl stats,
+ List<BatteryUsageStatsQuery> queries) {
ArrayList<BatteryUsageStats> results = new ArrayList<>(queries.size());
- synchronized (mStats) {
- mStats.prepareForDumpLocked();
- final long currentTimeMillis = currentTimeMillis();
+ synchronized (stats) {
+ stats.prepareForDumpLocked();
+ final long currentTimeMillis = mClock.currentTimeMillis();
for (int i = 0; i < queries.size(); i++) {
- results.add(getBatteryUsageStats(queries.get(i), currentTimeMillis));
+ results.add(getBatteryUsageStats(stats, queries.get(i), currentTimeMillis));
}
}
return results;
@@ -140,60 +140,59 @@
/**
* Returns a snapshot of battery attribution data.
*/
- @VisibleForTesting
- public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
- synchronized (mStats) {
- return getBatteryUsageStats(query, currentTimeMillis());
- }
+ public BatteryUsageStats getBatteryUsageStats(BatteryStatsImpl stats,
+ BatteryUsageStatsQuery query) {
+ return getBatteryUsageStats(stats, query, mClock.currentTimeMillis());
}
- @GuardedBy("mStats")
- private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query,
- long currentTimeMs) {
+ private BatteryUsageStats getBatteryUsageStats(BatteryStatsImpl stats,
+ BatteryUsageStatsQuery query, long currentTimeMs) {
if (query.getToTimestamp() == 0) {
- return getCurrentBatteryUsageStats(query, currentTimeMs);
+ return getCurrentBatteryUsageStats(stats, query, currentTimeMs);
} else {
- return getAggregatedBatteryUsageStats(query);
+ return getAggregatedBatteryUsageStats(stats, query);
}
}
- @GuardedBy("mStats")
- private BatteryUsageStats getCurrentBatteryUsageStats(BatteryUsageStatsQuery query,
- long currentTimeMs) {
- final long realtimeUs = elapsedRealtime() * 1000;
- final long uptimeUs = uptimeMillis() * 1000;
+ private BatteryUsageStats getCurrentBatteryUsageStats(BatteryStatsImpl stats,
+ BatteryUsageStatsQuery query, long currentTimeMs) {
+ final long realtimeUs = mClock.elapsedRealtime() * 1000;
+ final long uptimeUs = mClock.uptimeMillis() * 1000;
final boolean includePowerModels = (query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
final boolean includeProcessStateData = ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0)
- && mStats.isProcessStateDataAvailable();
+ && stats.isProcessStateDataAvailable();
final boolean includeVirtualUids = ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_VIRTUAL_UIDS) != 0);
final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold();
final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder(
- mStats.getCustomEnergyConsumerNames(), includePowerModels,
+ stats.getCustomEnergyConsumerNames(), includePowerModels,
includeProcessStateData, minConsumedPowerThreshold);
// TODO(b/188068523): use a monotonic clock to ensure resilience of order and duration
- // of stats sessions to wall-clock adjustments
- batteryUsageStatsBuilder.setStatsStartTimestamp(mStats.getStartClockTime());
+ // of batteryUsageStats sessions to wall-clock adjustments
+ batteryUsageStatsBuilder.setStatsStartTimestamp(stats.getStartClockTime());
batteryUsageStatsBuilder.setStatsEndTimestamp(currentTimeMs);
- SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats();
- for (int i = uidStats.size() - 1; i >= 0; i--) {
- final BatteryStats.Uid uid = uidStats.valueAt(i);
- if (!includeVirtualUids && uid.getUid() == Process.SDK_SANDBOX_VIRTUAL_UID) {
- continue;
- }
+ synchronized (stats) {
+ SparseArray<? extends BatteryStats.Uid> uidStats = stats.getUidStats();
+ for (int i = uidStats.size() - 1; i >= 0; i--) {
+ final BatteryStats.Uid uid = uidStats.valueAt(i);
+ if (!includeVirtualUids && uid.getUid() == Process.SDK_SANDBOX_VIRTUAL_UID) {
+ continue;
+ }
- batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid)
- .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_BACKGROUND,
- getProcessBackgroundTimeMs(uid, realtimeUs))
- .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND,
- getProcessForegroundTimeMs(uid, realtimeUs))
- .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE,
- getProcessForegroundServiceTimeMs(uid, realtimeUs));
+ batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid)
+ .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_BACKGROUND,
+ getProcessBackgroundTimeMs(uid, realtimeUs))
+ .setTimeInProcessStateMs(UidBatteryConsumer.PROCESS_STATE_FOREGROUND,
+ getProcessForegroundTimeMs(uid, realtimeUs))
+ .setTimeInProcessStateMs(
+ UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE,
+ getProcessForegroundServiceTimeMs(uid, realtimeUs));
+ }
}
final int[] powerComponents = query.getPowerComponents();
@@ -202,8 +201,8 @@
PowerCalculator powerCalculator = powerCalculators.get(i);
if (powerComponents != null) {
boolean include = false;
- for (int j = 0; j < powerComponents.length; j++) {
- if (powerCalculator.isPowerComponentSupported(powerComponents[j])) {
+ for (int powerComponent : powerComponents) {
+ if (powerCalculator.isPowerComponentSupported(powerComponent)) {
include = true;
break;
}
@@ -212,26 +211,24 @@
continue;
}
}
- powerCalculator.calculate(batteryUsageStatsBuilder, mStats, realtimeUs, uptimeUs,
- query);
+ powerCalculator.calculate(batteryUsageStatsBuilder, stats, realtimeUs, uptimeUs, query);
+ }
+
+ if (mPowerStatsExporterEnabled) {
+ mPowerStatsExporter.exportAggregatedPowerStats(batteryUsageStatsBuilder,
+ stats.getMonotonicStartTime(), stats.getMonotonicEndTime());
}
if ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY) != 0) {
- if (!(mStats instanceof BatteryStatsImpl)) {
- throw new UnsupportedOperationException(
- "History cannot be included for " + getClass().getName());
- }
-
- BatteryStatsImpl batteryStatsImpl = (BatteryStatsImpl) mStats;
- batteryUsageStatsBuilder.setBatteryHistory(batteryStatsImpl.copyHistory());
+ batteryUsageStatsBuilder.setBatteryHistory(stats.copyHistory());
}
- BatteryUsageStats stats = batteryUsageStatsBuilder.build();
+ BatteryUsageStats batteryUsageStats = batteryUsageStatsBuilder.build();
if (includeProcessStateData) {
- verify(stats);
+ verify(batteryUsageStats);
}
- return stats;
+ return batteryUsageStats;
}
// STOPSHIP(b/229906525): remove verification before shipping
@@ -308,15 +305,16 @@
/ 1000;
}
- private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryUsageStatsQuery query) {
+ private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryStatsImpl stats,
+ BatteryUsageStatsQuery query) {
final boolean includePowerModels = (query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
final boolean includeProcessStateData = ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0)
- && mStats.isProcessStateDataAvailable();
+ && stats.isProcessStateDataAvailable();
final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold();
- final String[] customEnergyConsumerNames = mStats.getCustomEnergyConsumerNames();
+ final String[] customEnergyConsumerNames = stats.getCustomEnergyConsumerNames();
final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
customEnergyConsumerNames, includePowerModels, includeProcessStateData,
minConsumedPowerThreshold);
@@ -386,27 +384,8 @@
return builder.build();
}
- private long elapsedRealtime() {
- if (mStats instanceof BatteryStatsImpl) {
- return ((BatteryStatsImpl) mStats).mClock.elapsedRealtime();
- } else {
- return SystemClock.elapsedRealtime();
- }
- }
+ public void setPowerStatsExporterEnabled(boolean enabled) {
- private long uptimeMillis() {
- if (mStats instanceof BatteryStatsImpl) {
- return ((BatteryStatsImpl) mStats).mClock.uptimeMillis();
- } else {
- return SystemClock.uptimeMillis();
- }
- }
-
- private long currentTimeMillis() {
- if (mStats instanceof BatteryStatsImpl) {
- return ((BatteryStatsImpl) mStats).mClock.currentTimeMillis();
- } else {
- return System.currentTimeMillis();
- }
+ mPowerStatsExporterEnabled = enabled;
}
}
diff --git a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java
index f40eef2..ed9414f 100644
--- a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java
@@ -64,7 +64,7 @@
private PowerStats.Descriptor mLastUsedDescriptor;
// Cached results of parsing of current PowerStats.Descriptor. Only refreshed when
// mLastUsedDescriptor changes
- private CpuPowerStatsCollector.StatsArrayLayout mStatsLayout;
+ private CpuPowerStatsCollector.CpuStatsArrayLayout mStatsLayout;
// Sequence of steps for power estimation and intermediate results.
private PowerEstimationPlan mPlan;
@@ -106,7 +106,7 @@
}
mLastUsedDescriptor = descriptor;
- mStatsLayout = new CpuPowerStatsCollector.StatsArrayLayout();
+ mStatsLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout();
mStatsLayout.fromExtras(descriptor.extras);
mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
@@ -149,7 +149,7 @@
if (mPlan == null) {
mPlan = new PowerEstimationPlan(stats.getConfig());
- if (mStatsLayout.getCpuClusterEnergyConsumerCount() != 0) {
+ if (mStatsLayout.getEnergyConsumerCount() != 0) {
initEnergyConsumerToPowerBracketMaps();
}
}
@@ -212,7 +212,7 @@
* CL_2: [bracket3, bracket4]
*/
private void initEnergyConsumerToPowerBracketMaps() {
- int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+ int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
mEnergyConsumerToCombinedEnergyConsumerMap = new int[energyConsumerCount];
@@ -294,7 +294,7 @@
continue;
}
- intermediates.uptime += mStatsLayout.getUptime(mTmpDeviceStatsArray);
+ intermediates.uptime += mStatsLayout.getUsageDuration(mTmpDeviceStatsArray);
for (int cluster = 0; cluster < mCpuClusterCount; cluster++) {
intermediates.timeByCluster[cluster] +=
@@ -351,7 +351,7 @@
int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount();
int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
int[] scalingStepToBracketMap = mStatsLayout.getScalingStepToPowerBracketMap();
- int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+ int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
List<DeviceStateEstimation> deviceStateEstimations = mPlan.deviceStateEstimations;
for (int dse = deviceStateEstimations.size() - 1; dse >= 0; dse--) {
DeviceStateEstimation deviceStateEstimation = deviceStateEstimations.get(dse);
@@ -392,7 +392,7 @@
private void adjustEstimatesUsingEnergyConsumers(
Intermediates intermediates, DeviceStatsIntermediates deviceStatsIntermediates) {
- int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+ int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
if (energyConsumerCount == 0) {
return;
}
@@ -509,8 +509,8 @@
}
sb.append(mStatsLayout.getTimeByCluster(stats, cluster));
}
- sb.append("] uptime: ").append(mStatsLayout.getUptime(stats));
- int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+ sb.append("] uptime: ").append(mStatsLayout.getUsageDuration(stats));
+ int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
if (energyConsumerCount > 0) {
sb.append(" energy: [");
for (int i = 0; i < energyConsumerCount; i++) {
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
index b8e581f..c05407c 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -22,6 +22,7 @@
import android.os.BatteryConsumer;
import android.os.Handler;
import android.os.PersistableBundle;
+import android.os.Process;
import android.power.PowerStatsInternal;
import android.util.Slog;
import android.util.SparseArray;
@@ -67,6 +68,7 @@
private final CpuScalingPolicies mCpuScalingPolicies;
private final PowerProfile mPowerProfile;
private final KernelCpuStatsReader mKernelCpuStatsReader;
+ private final PowerStatsUidResolver mUidResolver;
private final Supplier<PowerStatsInternal> mPowerStatsSupplier;
private final IntSupplier mVoltageSupplier;
private final int mDefaultCpuPowerBrackets;
@@ -81,7 +83,7 @@
private PowerStats.Descriptor mPowerStatsDescriptor;
// Reusable instance
private PowerStats mCpuPowerStats;
- private StatsArrayLayout mLayout;
+ private CpuStatsArrayLayout mLayout;
private long mLastUpdateTimestampNanos;
private long mLastUpdateUptimeMillis;
private int mLastVoltageMv;
@@ -91,55 +93,30 @@
* Captures the positions and lengths of sections of the stats array, such as time-in-state,
* power usage estimates etc.
*/
- public static class StatsArrayLayout {
+ public static class CpuStatsArrayLayout extends StatsArrayLayout {
private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION = "dt";
private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT = "dtc";
private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION = "dc";
private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT = "dcc";
- private static final String EXTRA_DEVICE_POWER_POSITION = "dp";
- private static final String EXTRA_DEVICE_UPTIME_POSITION = "du";
- private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de";
- private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
private static final String EXTRA_UID_BRACKETS_POSITION = "ub";
private static final String EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET = "us";
- private static final String EXTRA_UID_POWER_POSITION = "up";
-
- private static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
-
- private int mDeviceStatsArrayLength;
- private int mUidStatsArrayLength;
private int mDeviceCpuTimeByScalingStepPosition;
private int mDeviceCpuTimeByScalingStepCount;
private int mDeviceCpuTimeByClusterPosition;
private int mDeviceCpuTimeByClusterCount;
- private int mDeviceCpuUptimePosition;
- private int mDeviceEnergyConsumerPosition;
- private int mDeviceEnergyConsumerCount;
- private int mDevicePowerEstimatePosition;
private int mUidPowerBracketsPosition;
private int mUidPowerBracketCount;
- private int[][] mEnergyConsumerToPowerBucketMaps;
- private int mUidPowerEstimatePosition;
private int[] mScalingStepToPowerBracketMap;
- public int getDeviceStatsArrayLength() {
- return mDeviceStatsArrayLength;
- }
-
- public int getUidStatsArrayLength() {
- return mUidStatsArrayLength;
- }
-
/**
* Declare that the stats array has a section capturing CPU time per scaling step
*/
public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) {
- mDeviceCpuTimeByScalingStepPosition = mDeviceStatsArrayLength;
+ mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount);
mDeviceCpuTimeByScalingStepCount = scalingStepCount;
- mDeviceStatsArrayLength += scalingStepCount;
}
public int getCpuScalingStepCount() {
@@ -166,9 +143,8 @@
* Declare that the stats array has a section capturing CPU time in each cluster
*/
public void addDeviceSectionCpuTimeByCluster(int clusterCount) {
+ mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount);
mDeviceCpuTimeByClusterCount = clusterCount;
- mDeviceCpuTimeByClusterPosition = mDeviceStatsArrayLength;
- mDeviceStatsArrayLength += clusterCount;
}
public int getCpuClusterCount() {
@@ -192,86 +168,12 @@
}
/**
- * Declare that the stats array has a section capturing CPU uptime
- */
- public void addDeviceSectionUptime() {
- mDeviceCpuUptimePosition = mDeviceStatsArrayLength++;
- }
-
- /**
- * Saves the CPU uptime duration in the corresponding <code>stats</code> element.
- */
- public void setUptime(long[] stats, long value) {
- stats[mDeviceCpuUptimePosition] = value;
- }
-
- /**
- * Extracts the CPU uptime duration from the corresponding <code>stats</code> element.
- */
- public long getUptime(long[] stats) {
- return stats[mDeviceCpuUptimePosition];
- }
-
- /**
- * Declares that the stats array has a section capturing EnergyConsumer data from
- * PowerStatsService.
- */
- public void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
- mDeviceEnergyConsumerPosition = mDeviceStatsArrayLength;
- mDeviceEnergyConsumerCount = energyConsumerCount;
- mDeviceStatsArrayLength += energyConsumerCount;
- }
-
- public int getCpuClusterEnergyConsumerCount() {
- return mDeviceEnergyConsumerCount;
- }
-
- /**
- * Saves the accumulated energy for the specified rail the corresponding
- * <code>stats</code> element.
- */
- public void setConsumedEnergy(long[] stats, int index, long energy) {
- stats[mDeviceEnergyConsumerPosition + index] = energy;
- }
-
- /**
- * Extracts the EnergyConsumer data from a device stats array for the specified
- * EnergyConsumer.
- */
- public long getConsumedEnergy(long[] stats, int index) {
- return stats[mDeviceEnergyConsumerPosition + index];
- }
-
- /**
- * Declare that the stats array has a section capturing a power estimate
- */
- public void addDeviceSectionPowerEstimate() {
- mDevicePowerEstimatePosition = mDeviceStatsArrayLength++;
- }
-
- /**
- * Converts the supplied mAh power estimate to a long and saves it in the corresponding
- * element of <code>stats</code>.
- */
- public void setDevicePowerEstimate(long[] stats, double power) {
- stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
- }
-
- /**
- * Extracts the power estimate from a device stats array and converts it to mAh.
- */
- public double getDevicePowerEstimate(long[] stats) {
- return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
- }
-
- /**
* Declare that the UID stats array has a section capturing CPU time per power bracket.
*/
public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) {
mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap;
- mUidPowerBracketsPosition = mUidStatsArrayLength;
updatePowerBracketCount();
- mUidStatsArrayLength += mUidPowerBracketCount;
+ mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount);
}
private void updatePowerBracketCount() {
@@ -306,31 +208,10 @@
}
/**
- * Declare that the UID stats array has a section capturing a power estimate
- */
- public void addUidSectionPowerEstimate() {
- mUidPowerEstimatePosition = mUidStatsArrayLength++;
- }
-
- /**
- * Converts the supplied mAh power estimate to a long and saves it in the corresponding
- * element of <code>stats</code>.
- */
- public void setUidPowerEstimate(long[] stats, double power) {
- stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
- }
-
- /**
- * Extracts the power estimate from a UID stats array and converts it to mAh.
- */
- public double getUidPowerEstimate(long[] stats) {
- return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
- }
-
- /**
* Copies the elements of the stats array layout into <code>extras</code>
*/
public void toExtras(PersistableBundle extras) {
+ super.toExtras(extras);
extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION,
mDeviceCpuTimeByScalingStepPosition);
extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT,
@@ -339,22 +220,16 @@
mDeviceCpuTimeByClusterPosition);
extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT,
mDeviceCpuTimeByClusterCount);
- extras.putInt(EXTRA_DEVICE_UPTIME_POSITION, mDeviceCpuUptimePosition);
- extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION,
- mDeviceEnergyConsumerPosition);
- extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT,
- mDeviceEnergyConsumerCount);
- extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
extras.putInt(EXTRA_UID_BRACKETS_POSITION, mUidPowerBracketsPosition);
- extras.putIntArray(EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET,
+ putIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET,
mScalingStepToPowerBracketMap);
- extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
}
/**
* Retrieves elements of the stats array layout from <code>extras</code>
*/
public void fromExtras(PersistableBundle extras) {
+ super.fromExtras(extras);
mDeviceCpuTimeByScalingStepPosition =
extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION);
mDeviceCpuTimeByScalingStepCount =
@@ -363,43 +238,39 @@
extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION);
mDeviceCpuTimeByClusterCount =
extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT);
- mDeviceCpuUptimePosition = extras.getInt(EXTRA_DEVICE_UPTIME_POSITION);
- mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
- mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
- mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION);
mScalingStepToPowerBracketMap =
- extras.getIntArray(EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET);
+ getIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET);
if (mScalingStepToPowerBracketMap == null) {
mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount];
}
updatePowerBracketCount();
- mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
}
}
public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile,
- IntSupplier voltageSupplier, Handler handler, long throttlePeriodMs) {
- this(cpuScalingPolicies, powerProfile, handler, new KernelCpuStatsReader(),
+ PowerStatsUidResolver uidResolver, IntSupplier voltageSupplier, Handler handler,
+ long throttlePeriodMs) {
+ this(cpuScalingPolicies, powerProfile, handler, new KernelCpuStatsReader(), uidResolver,
() -> LocalServices.getService(PowerStatsInternal.class), voltageSupplier,
throttlePeriodMs, Clock.SYSTEM_CLOCK, DEFAULT_CPU_POWER_BRACKETS,
DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER);
}
public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile,
- Handler handler, KernelCpuStatsReader kernelCpuStatsReader,
- Supplier<PowerStatsInternal> powerStatsSupplier,
+ Handler handler, KernelCpuStatsReader kernelCpuStatsReader,
+ PowerStatsUidResolver uidResolver, Supplier<PowerStatsInternal> powerStatsSupplier,
IntSupplier voltageSupplier, long throttlePeriodMs, Clock clock,
int defaultCpuPowerBrackets, int defaultCpuPowerBracketsPerEnergyConsumer) {
super(handler, throttlePeriodMs, clock);
mCpuScalingPolicies = cpuScalingPolicies;
mPowerProfile = powerProfile;
mKernelCpuStatsReader = kernelCpuStatsReader;
+ mUidResolver = uidResolver;
mPowerStatsSupplier = powerStatsSupplier;
mVoltageSupplier = voltageSupplier;
mDefaultCpuPowerBrackets = defaultCpuPowerBrackets;
mDefaultCpuPowerBracketsPerEnergyConsumer = defaultCpuPowerBracketsPerEnergyConsumer;
-
}
/**
@@ -409,13 +280,13 @@
setEnabled(Flags.streamlinedBatteryStats());
}
- private void ensureInitialized() {
+ private boolean ensureInitialized() {
if (mIsInitialized) {
- return;
+ return true;
}
if (!isEnabled()) {
- return;
+ return false;
}
mIsPerUidTimeInStateSupported = mKernelCpuStatsReader.nativeIsSupportedFeature();
@@ -432,10 +303,10 @@
mTempCpuTimeByScalingStep = new long[cpuScalingStepCount];
int[] scalingStepToPowerBracketMap = initPowerBrackets();
- mLayout = new StatsArrayLayout();
+ mLayout = new CpuStatsArrayLayout();
mLayout.addDeviceSectionCpuTimeByScalingStep(cpuScalingStepCount);
mLayout.addDeviceSectionCpuTimeByCluster(mCpuScalingPolicies.getPolicies().length);
- mLayout.addDeviceSectionUptime();
+ mLayout.addDeviceSectionUsageDuration();
mLayout.addDeviceSectionEnergyConsumers(mCpuEnergyConsumerIds.length);
mLayout.addDeviceSectionPowerEstimate();
mLayout.addUidSectionCpuTimeByPowerBracket(scalingStepToPowerBracketMap);
@@ -451,6 +322,7 @@
mTempUidStats = new long[mLayout.getCpuPowerBracketCount()];
mIsInitialized = true;
+ return true;
}
private void readCpuEnergyConsumerIds() {
@@ -590,7 +462,9 @@
* Prints the definitions of power brackets.
*/
public void dumpCpuPowerBracketsLocked(PrintWriter pw) {
- ensureInitialized();
+ if (!ensureInitialized()) {
+ return;
+ }
if (mLayout == null) {
return;
@@ -610,7 +484,9 @@
*/
@VisibleForTesting
public String getCpuPowerBracketDescription(int powerBracket) {
- ensureInitialized();
+ if (!ensureInitialized()) {
+ return "";
+ }
int[] stepToPowerBracketMap = mLayout.getScalingStepToPowerBracketMap();
StringBuilder sb = new StringBuilder();
@@ -647,14 +523,18 @@
*/
@VisibleForTesting
public PowerStats.Descriptor getPowerStatsDescriptor() {
- ensureInitialized();
+ if (!ensureInitialized()) {
+ return null;
+ }
return mPowerStatsDescriptor;
}
@Override
protected PowerStats collectStats() {
- ensureInitialized();
+ if (!ensureInitialized()) {
+ return null;
+ }
if (!mIsPerUidTimeInStateSupported) {
return null;
@@ -682,7 +562,7 @@
if (uptimeDelta > mCpuPowerStats.durationMs) {
uptimeDelta = mCpuPowerStats.durationMs;
}
- mLayout.setUptime(mCpuPowerStats.stats, uptimeDelta);
+ mLayout.setUsageDuration(mCpuPowerStats.stats, uptimeDelta);
if (mCpuEnergyConsumerIds.length != 0) {
collectEnergyConsumers();
@@ -761,7 +641,21 @@
uidStats.timeByPowerBracket[bracket] = timeByPowerBracket[bracket];
}
if (nonzero) {
- mCpuPowerStats.uidStats.put(uid, uidStats.stats);
+ int ownerUid;
+ if (Process.isSdkSandboxUid(uid)) {
+ ownerUid = Process.getAppUidForSdkSandboxUid(uid);
+ } else {
+ ownerUid = mUidResolver.mapUid(uid);
+ }
+
+ long[] ownerStats = mCpuPowerStats.uidStats.get(ownerUid);
+ if (ownerStats == null) {
+ mCpuPowerStats.uidStats.put(ownerUid, uidStats.stats);
+ } else {
+ for (int i = 0; i < ownerStats.length; i++) {
+ ownerStats[i] += uidStats.stats[i];
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
index 2c7843e..0facb9c 100644
--- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
@@ -44,6 +44,7 @@
private static final String XML_TAG_DEVICE_STATS = "device-stats";
private static final String XML_TAG_UID_STATS = "uid-stats";
private static final String XML_ATTR_UID = "uid";
+ private static final long UNKNOWN = -1;
public final int powerComponentId;
private final MultiStateStats.States[] mDeviceStateConfig;
@@ -51,17 +52,16 @@
@NonNull
private final AggregatedPowerStatsConfig.PowerComponent mConfig;
private final int[] mDeviceStates;
- private final long[] mDeviceStateTimestamps;
private MultiStateStats.Factory mStatsFactory;
private MultiStateStats.Factory mUidStatsFactory;
private PowerStats.Descriptor mPowerStatsDescriptor;
+ private long mPowerStatsTimestamp;
private MultiStateStats mDeviceStats;
private final SparseArray<UidStats> mUidStats = new SparseArray<>();
private static class UidStats {
public int[] states;
- public long[] stateTimestampMs;
public MultiStateStats stats;
}
@@ -71,7 +71,7 @@
mDeviceStateConfig = config.getDeviceStateConfig();
mUidStateConfig = config.getUidStateConfig();
mDeviceStates = new int[mDeviceStateConfig.length];
- mDeviceStateTimestamps = new long[mDeviceStateConfig.length];
+ mPowerStatsTimestamp = UNKNOWN;
}
@NonNull
@@ -85,8 +85,11 @@
}
void setState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state, long time) {
+ if (mDeviceStats == null) {
+ createDeviceStats();
+ }
+
mDeviceStates[stateId] = state;
- mDeviceStateTimestamps[stateId] = time;
if (mDeviceStateConfig[stateId].isTracked()) {
if (mDeviceStats != null) {
@@ -97,6 +100,11 @@
if (mUidStateConfig[stateId].isTracked()) {
for (int i = mUidStats.size() - 1; i >= 0; i--) {
PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
+ if (uidStats.stats == null) {
+ createUidStats(uidStats);
+ }
+
+ uidStats.states[stateId] = state;
if (uidStats.stats != null) {
uidStats.stats.setState(stateId, state, time);
}
@@ -111,8 +119,11 @@
}
UidStats uidStats = getUidStats(uid);
+ if (uidStats.stats == null) {
+ createUidStats(uidStats);
+ }
+
uidStats.states[stateId] = state;
- uidStats.stateTimestampMs[stateId] = time;
if (uidStats.stats != null) {
uidStats.stats.setState(stateId, state, time);
@@ -150,10 +161,11 @@
}
uidStats.stats.increment(powerStats.uidStats.valueAt(i), timestampMs);
}
+
+ mPowerStatsTimestamp = timestampMs;
}
void reset() {
- mPowerStatsDescriptor = null;
mStatsFactory = null;
mUidStatsFactory = null;
mDeviceStats = null;
@@ -163,12 +175,10 @@
}
private UidStats getUidStats(int uid) {
- // TODO(b/292247660): map isolated and sandbox UIDs
UidStats uidStats = mUidStats.get(uid);
if (uidStats == null) {
uidStats = new UidStats();
uidStats.states = new int[mUidStateConfig.length];
- uidStats.stateTimestampMs = new long[mUidStateConfig.length];
mUidStats.put(uid, uidStats);
}
return uidStats;
@@ -209,42 +219,38 @@
return false;
}
- private boolean createDeviceStats() {
+ private void createDeviceStats() {
if (mStatsFactory == null) {
if (mPowerStatsDescriptor == null) {
- return false;
+ return;
}
mStatsFactory = new MultiStateStats.Factory(
mPowerStatsDescriptor.statsArrayLength, mDeviceStateConfig);
}
mDeviceStats = mStatsFactory.create();
- for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) {
- mDeviceStats.setState(stateId, mDeviceStates[stateId],
- mDeviceStateTimestamps[stateId]);
+ if (mPowerStatsTimestamp != UNKNOWN) {
+ for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) {
+ mDeviceStats.setState(stateId, mDeviceStates[stateId], mPowerStatsTimestamp);
+ }
}
- return true;
}
- private boolean createUidStats(UidStats uidStats) {
+ private void createUidStats(UidStats uidStats) {
if (mUidStatsFactory == null) {
if (mPowerStatsDescriptor == null) {
- return false;
+ return;
}
mUidStatsFactory = new MultiStateStats.Factory(
mPowerStatsDescriptor.uidStatsArrayLength, mUidStateConfig);
}
uidStats.stats = mUidStatsFactory.create();
- for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) {
- uidStats.stats.setState(stateId, mDeviceStates[stateId],
- mDeviceStateTimestamps[stateId]);
+ for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) {
+ if (mPowerStatsTimestamp != UNKNOWN) {
+ uidStats.stats.setState(stateId, uidStats.states[stateId], mPowerStatsTimestamp);
+ }
}
- for (int stateId = mDeviceStateConfig.length; stateId < mUidStateConfig.length; stateId++) {
- uidStats.stats.setState(stateId, uidStats.states[stateId],
- uidStats.stateTimestampMs[stateId]);
- }
- return true;
}
public void writeXml(TypedXmlSerializer serializer) throws IOException {
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
index 2f9d567..3f88a2d 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
@@ -29,13 +29,18 @@
* {@link AggregatedPowerStats} that adds up power stats from the samples found in battery history.
*/
public class PowerStatsAggregator {
+ private static final long UNINITIALIZED = -1;
private final AggregatedPowerStats mStats;
+ private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
private final BatteryStatsHistory mHistory;
private final SparseArray<AggregatedPowerStatsProcessor> mProcessors = new SparseArray<>();
+ private int mCurrentBatteryState = AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+ private int mCurrentScreenState = AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
public PowerStatsAggregator(AggregatedPowerStatsConfig aggregatedPowerStatsConfig,
BatteryStatsHistory history) {
mStats = new AggregatedPowerStats(aggregatedPowerStatsConfig);
+ mAggregatedPowerStatsConfig = aggregatedPowerStatsConfig;
mHistory = history;
for (AggregatedPowerStatsConfig.PowerComponent powerComponentsConfig :
aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs()) {
@@ -44,6 +49,10 @@
}
}
+ AggregatedPowerStatsConfig getConfig() {
+ return mAggregatedPowerStatsConfig;
+ }
+
/**
* Iterates of the battery history and aggregates power stats between the specified times.
* The start and end are specified in the battery-stats monotonic time, which is the
@@ -58,18 +67,20 @@
*/
public void aggregatePowerStats(long startTimeMs, long endTimeMs,
Consumer<AggregatedPowerStats> consumer) {
- int currentBatteryState = AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
- int currentScreenState = AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
- long baseTime = -1;
+ boolean clockUpdateAdded = false;
+ long baseTime = startTimeMs > 0 ? startTimeMs : UNINITIALIZED;
long lastTime = 0;
try (BatteryStatsHistoryIterator iterator =
mHistory.copy().iterate(startTimeMs, endTimeMs)) {
while (iterator.hasNext()) {
BatteryStats.HistoryItem item = iterator.next();
- if (baseTime < 0) {
+ if (!clockUpdateAdded) {
mStats.addClockUpdate(item.time, item.currentTime);
- baseTime = item.time;
+ if (baseTime == UNINITIALIZED) {
+ baseTime = item.time;
+ }
+ clockUpdateAdded = true;
} else if (item.cmd == BatteryStats.HistoryItem.CMD_CURRENT_TIME
|| item.cmd == BatteryStats.HistoryItem.CMD_RESET) {
mStats.addClockUpdate(item.time, item.currentTime);
@@ -81,20 +92,20 @@
(item.states & BatteryStats.HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0
? AggregatedPowerStatsConfig.POWER_STATE_OTHER
: AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
- if (batteryState != currentBatteryState) {
+ if (batteryState != mCurrentBatteryState) {
mStats.setDeviceState(AggregatedPowerStatsConfig.STATE_POWER, batteryState,
item.time);
- currentBatteryState = batteryState;
+ mCurrentBatteryState = batteryState;
}
int screenState =
(item.states & BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG) != 0
? AggregatedPowerStatsConfig.SCREEN_STATE_ON
: AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
- if (screenState != currentScreenState) {
+ if (screenState != mCurrentScreenState) {
mStats.setDeviceState(AggregatedPowerStatsConfig.STATE_SCREEN, screenState,
item.time);
- currentScreenState = screenState;
+ mCurrentScreenState = screenState;
}
if (item.processStateChange != null) {
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
index 84cc21e..abfe9de 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -16,10 +16,13 @@
package com.android.server.power.stats;
+import android.annotation.Nullable;
import android.os.ConditionVariable;
import android.os.Handler;
+import android.os.PersistableBundle;
import android.util.FastImmutableArraySet;
import android.util.IndentingPrintWriter;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.Clock;
@@ -38,6 +41,7 @@
* except where noted.
*/
public abstract class PowerStatsCollector {
+ private static final String TAG = "PowerStatsCollector";
private static final int MILLIVOLTS_PER_VOLT = 1000;
private final Handler mHandler;
protected final Clock mClock;
@@ -46,6 +50,200 @@
private boolean mEnabled;
private long mLastScheduledUpdateMs = -1;
+ /**
+ * Captures the positions and lengths of sections of the stats array, such as usage duration,
+ * power usage estimates etc.
+ */
+ public static class StatsArrayLayout {
+ private static final String EXTRA_DEVICE_POWER_POSITION = "dp";
+ private static final String EXTRA_DEVICE_DURATION_POSITION = "dd";
+ private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de";
+ private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
+ private static final String EXTRA_UID_POWER_POSITION = "up";
+
+ protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
+
+ private int mDeviceStatsArrayLength;
+ private int mUidStatsArrayLength;
+
+ protected int mDeviceDurationPosition;
+ private int mDeviceEnergyConsumerPosition;
+ private int mDeviceEnergyConsumerCount;
+ private int mDevicePowerEstimatePosition;
+ private int mUidPowerEstimatePosition;
+
+ public int getDeviceStatsArrayLength() {
+ return mDeviceStatsArrayLength;
+ }
+
+ public int getUidStatsArrayLength() {
+ return mUidStatsArrayLength;
+ }
+
+ protected int addDeviceSection(int length) {
+ int position = mDeviceStatsArrayLength;
+ mDeviceStatsArrayLength += length;
+ return position;
+ }
+
+ protected int addUidSection(int length) {
+ int position = mUidStatsArrayLength;
+ mUidStatsArrayLength += length;
+ return position;
+ }
+
+ /**
+ * Declare that the stats array has a section capturing usage duration
+ */
+ public void addDeviceSectionUsageDuration() {
+ mDeviceDurationPosition = addDeviceSection(1);
+ }
+
+ /**
+ * Saves the usage duration in the corresponding <code>stats</code> element.
+ */
+ public void setUsageDuration(long[] stats, long value) {
+ stats[mDeviceDurationPosition] = value;
+ }
+
+ /**
+ * Extracts the usage duration from the corresponding <code>stats</code> element.
+ */
+ public long getUsageDuration(long[] stats) {
+ return stats[mDeviceDurationPosition];
+ }
+
+ /**
+ * Declares that the stats array has a section capturing EnergyConsumer data from
+ * PowerStatsService.
+ */
+ public void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
+ mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount);
+ mDeviceEnergyConsumerCount = energyConsumerCount;
+ }
+
+ public int getEnergyConsumerCount() {
+ return mDeviceEnergyConsumerCount;
+ }
+
+ /**
+ * Saves the accumulated energy for the specified rail the corresponding
+ * <code>stats</code> element.
+ */
+ public void setConsumedEnergy(long[] stats, int index, long energy) {
+ stats[mDeviceEnergyConsumerPosition + index] = energy;
+ }
+
+ /**
+ * Extracts the EnergyConsumer data from a device stats array for the specified
+ * EnergyConsumer.
+ */
+ public long getConsumedEnergy(long[] stats, int index) {
+ return stats[mDeviceEnergyConsumerPosition + index];
+ }
+
+ /**
+ * Declare that the stats array has a section capturing a power estimate
+ */
+ public void addDeviceSectionPowerEstimate() {
+ mDevicePowerEstimatePosition = addDeviceSection(1);
+ }
+
+ /**
+ * Converts the supplied mAh power estimate to a long and saves it in the corresponding
+ * element of <code>stats</code>.
+ */
+ public void setDevicePowerEstimate(long[] stats, double power) {
+ stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+ }
+
+ /**
+ * Extracts the power estimate from a device stats array and converts it to mAh.
+ */
+ public double getDevicePowerEstimate(long[] stats) {
+ return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
+ }
+
+ /**
+ * Declare that the UID stats array has a section capturing a power estimate
+ */
+ public void addUidSectionPowerEstimate() {
+ mUidPowerEstimatePosition = addUidSection(1);
+ }
+
+ /**
+ * Converts the supplied mAh power estimate to a long and saves it in the corresponding
+ * element of <code>stats</code>.
+ */
+ public void setUidPowerEstimate(long[] stats, double power) {
+ stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+ }
+
+ /**
+ * Extracts the power estimate from a UID stats array and converts it to mAh.
+ */
+ public double getUidPowerEstimate(long[] stats) {
+ return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
+ }
+
+ /**
+ * Copies the elements of the stats array layout into <code>extras</code>
+ */
+ public void toExtras(PersistableBundle extras) {
+ extras.putInt(EXTRA_DEVICE_DURATION_POSITION, mDeviceDurationPosition);
+ extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION,
+ mDeviceEnergyConsumerPosition);
+ extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT,
+ mDeviceEnergyConsumerCount);
+ extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
+ extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
+ }
+
+ /**
+ * Retrieves elements of the stats array layout from <code>extras</code>
+ */
+ public void fromExtras(PersistableBundle extras) {
+ mDeviceDurationPosition = extras.getInt(EXTRA_DEVICE_DURATION_POSITION);
+ mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
+ mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
+ mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
+ mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
+ }
+
+ protected void putIntArray(PersistableBundle extras, String key, int[] array) {
+ if (array == null) {
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int value : array) {
+ if (!sb.isEmpty()) {
+ sb.append(',');
+ }
+ sb.append(value);
+ }
+ extras.putString(key, sb.toString());
+ }
+
+ protected int[] getIntArray(PersistableBundle extras, String key) {
+ String string = extras.getString(key);
+ if (string == null) {
+ return null;
+ }
+ String[] values = string.trim().split(",");
+ int[] result = new int[values.length];
+ for (int i = 0; i < values.length; i++) {
+ try {
+ result[i] = Integer.parseInt(values[i]);
+ } catch (NumberFormatException e) {
+ Slog.wtf(TAG, "Invalid CSV format: " + string);
+ return null;
+ }
+ }
+ return result;
+ }
+ }
+
@GuardedBy("this")
@SuppressWarnings("unchecked")
private volatile FastImmutableArraySet<Consumer<PowerStats>> mConsumerList =
@@ -141,6 +339,7 @@
return true;
}
+ @Nullable
protected abstract PowerStats collectStats();
/**
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
new file mode 100644
index 0000000..70c24d5
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
@@ -0,0 +1,227 @@
+/*
+ * 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.power.stats;
+
+import android.os.AggregateBatteryConsumer;
+import android.os.BatteryConsumer;
+import android.os.BatteryUsageStats;
+import android.os.UidBatteryConsumer;
+import android.util.Slog;
+
+import com.android.internal.os.MultiStateStats;
+import com.android.internal.os.PowerStats;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Given a time range, converts accumulated PowerStats to BatteryUsageStats. Combines
+ * stores spans of PowerStats with the yet-unprocessed tail of battery history.
+ */
+public class PowerStatsExporter {
+ private static final String TAG = "PowerStatsExporter";
+ private final PowerStatsStore mPowerStatsStore;
+ private final PowerStatsAggregator mPowerStatsAggregator;
+ private final long mBatterySessionTimeSpanSlackMillis;
+ private static final long BATTERY_SESSION_TIME_SPAN_SLACK_MILLIS = TimeUnit.MINUTES.toMillis(2);
+
+ public PowerStatsExporter(PowerStatsStore powerStatsStore,
+ PowerStatsAggregator powerStatsAggregator) {
+ this(powerStatsStore, powerStatsAggregator, BATTERY_SESSION_TIME_SPAN_SLACK_MILLIS);
+ }
+
+ public PowerStatsExporter(PowerStatsStore powerStatsStore,
+ PowerStatsAggregator powerStatsAggregator,
+ long batterySessionTimeSpanSlackMillis) {
+ mPowerStatsStore = powerStatsStore;
+ mPowerStatsAggregator = powerStatsAggregator;
+ mBatterySessionTimeSpanSlackMillis = batterySessionTimeSpanSlackMillis;
+ }
+
+ /**
+ * Populates the provided BatteryUsageStats.Builder with power estimates from the accumulated
+ * PowerStats, both stored in PowerStatsStore and not-yet processed.
+ */
+ public void exportAggregatedPowerStats(BatteryUsageStats.Builder batteryUsageStatsBuilder,
+ long monotonicStartTime, long monotonicEndTime) {
+ long maxEndTime = monotonicStartTime;
+ List<PowerStatsSpan.Metadata> spans = mPowerStatsStore.getTableOfContents();
+ for (int i = spans.size() - 1; i >= 0; i--) {
+ PowerStatsSpan.Metadata metadata = spans.get(i);
+ if (!metadata.getSections().contains(AggregatedPowerStatsSection.TYPE)) {
+ continue;
+ }
+
+ List<PowerStatsSpan.TimeFrame> timeFrames = metadata.getTimeFrames();
+ long spanMinTime = Long.MAX_VALUE;
+ long spanMaxTime = Long.MIN_VALUE;
+ for (int j = 0; j < timeFrames.size(); j++) {
+ PowerStatsSpan.TimeFrame timeFrame = timeFrames.get(j);
+ long startMonotonicTime = timeFrame.startMonotonicTime;
+ long endMonotonicTime = startMonotonicTime + timeFrame.duration;
+ if (startMonotonicTime < spanMinTime) {
+ spanMinTime = startMonotonicTime;
+ }
+ if (endMonotonicTime > spanMaxTime) {
+ spanMaxTime = endMonotonicTime;
+ }
+ }
+
+ if (!(spanMinTime >= monotonicStartTime && spanMaxTime < monotonicEndTime)) {
+ continue;
+ }
+
+ if (spanMaxTime > maxEndTime) {
+ maxEndTime = spanMaxTime;
+ }
+
+ PowerStatsSpan span = mPowerStatsStore.loadPowerStatsSpan(metadata.getId(),
+ AggregatedPowerStatsSection.TYPE);
+ if (span == null) {
+ Slog.e(TAG, "Could not read PowerStatsStore section " + metadata);
+ continue;
+ }
+ List<PowerStatsSpan.Section> sections = span.getSections();
+ for (int k = 0; k < sections.size(); k++) {
+ PowerStatsSpan.Section section = sections.get(k);
+ populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder,
+ ((AggregatedPowerStatsSection) section).getAggregatedPowerStats());
+ }
+ }
+
+ if (maxEndTime < monotonicEndTime - mBatterySessionTimeSpanSlackMillis) {
+ mPowerStatsAggregator.aggregatePowerStats(maxEndTime, monotonicEndTime,
+ stats -> populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, stats));
+ }
+ }
+
+ private void populateBatteryUsageStatsBuilder(
+ BatteryUsageStats.Builder batteryUsageStatsBuilder, AggregatedPowerStats stats) {
+ AggregatedPowerStatsConfig config = mPowerStatsAggregator.getConfig();
+ List<AggregatedPowerStatsConfig.PowerComponent> powerComponents =
+ config.getPowerComponentsAggregatedStatsConfigs();
+ for (int i = powerComponents.size() - 1; i >= 0; i--) {
+ populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, stats,
+ powerComponents.get(i));
+ }
+ }
+
+ private void populateBatteryUsageStatsBuilder(
+ BatteryUsageStats.Builder batteryUsageStatsBuilder, AggregatedPowerStats stats,
+ AggregatedPowerStatsConfig.PowerComponent powerComponent) {
+ int powerComponentId = powerComponent.getPowerComponentId();
+ PowerComponentAggregatedPowerStats powerComponentStats = stats.getPowerComponentStats(
+ powerComponentId);
+ if (powerComponentStats == null) {
+ return;
+ }
+
+ PowerStats.Descriptor descriptor = powerComponentStats.getPowerStatsDescriptor();
+ if (descriptor == null) {
+ return;
+ }
+
+ PowerStatsCollector.StatsArrayLayout layout = new PowerStatsCollector.StatsArrayLayout();
+ layout.fromExtras(descriptor.extras);
+
+ long[] deviceStats = new long[descriptor.statsArrayLength];
+ double[] totalPower = new double[1];
+ MultiStateStats.States.forEachTrackedStateCombination(powerComponent.getDeviceStateConfig(),
+ states -> {
+ if (states[AggregatedPowerStatsConfig.STATE_POWER]
+ != AggregatedPowerStatsConfig.POWER_STATE_BATTERY) {
+ return;
+ }
+
+ if (!powerComponentStats.getDeviceStats(deviceStats, states)) {
+ return;
+ }
+
+ totalPower[0] += layout.getDevicePowerEstimate(deviceStats);
+ });
+
+ AggregateBatteryConsumer.Builder deviceScope =
+ batteryUsageStatsBuilder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+ deviceScope.addConsumedPower(powerComponentId,
+ totalPower[0], BatteryConsumer.POWER_MODEL_UNDEFINED);
+
+ long[] uidStats = new long[descriptor.uidStatsArrayLength];
+ ArrayList<Integer> uids = new ArrayList<>();
+ powerComponentStats.collectUids(uids);
+
+ boolean breakDownByProcState =
+ batteryUsageStatsBuilder.isProcessStateDataNeeded()
+ && powerComponent
+ .getUidStateConfig()[AggregatedPowerStatsConfig.STATE_PROCESS_STATE]
+ .isTracked();
+
+ double[] powerByProcState =
+ new double[breakDownByProcState ? BatteryConsumer.PROCESS_STATE_COUNT : 1];
+ double powerAllApps = 0;
+ for (int uid : uids) {
+ UidBatteryConsumer.Builder builder =
+ batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid);
+
+ Arrays.fill(powerByProcState, 0);
+
+ MultiStateStats.States.forEachTrackedStateCombination(
+ powerComponent.getUidStateConfig(),
+ states -> {
+ if (states[AggregatedPowerStatsConfig.STATE_POWER]
+ != AggregatedPowerStatsConfig.POWER_STATE_BATTERY) {
+ return;
+ }
+
+ if (!powerComponentStats.getUidStats(uidStats, uid, states)) {
+ return;
+ }
+
+ double power = layout.getUidPowerEstimate(uidStats);
+ int procState = breakDownByProcState
+ ? states[AggregatedPowerStatsConfig.STATE_PROCESS_STATE]
+ : BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
+ powerByProcState[procState] += power;
+ });
+
+ double powerAllProcStates = 0;
+ for (int procState = 0; procState < powerByProcState.length; procState++) {
+ double power = powerByProcState[procState];
+ if (power == 0) {
+ continue;
+ }
+ powerAllProcStates += power;
+ if (breakDownByProcState
+ && procState != BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+ builder.addConsumedPower(builder.getKey(powerComponentId, procState),
+ power, BatteryConsumer.POWER_MODEL_UNDEFINED);
+ }
+ }
+ builder.addConsumedPower(powerComponentId, powerAllProcStates,
+ BatteryConsumer.POWER_MODEL_UNDEFINED);
+ powerAllApps += powerAllProcStates;
+ }
+
+ AggregateBatteryConsumer.Builder allAppsScope =
+ batteryUsageStatsBuilder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
+ allAppsScope.addConsumedPower(powerComponentId, powerAllApps,
+ BatteryConsumer.POWER_MODEL_UNDEFINED);
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
index 551302e..97d872a 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
@@ -19,8 +19,6 @@
import android.annotation.DurationMillisLong;
import android.app.AlarmManager;
import android.content.Context;
-import android.os.BatteryUsageStats;
-import android.os.BatteryUsageStatsQuery;
import android.os.ConditionVariable;
import android.os.Handler;
import android.util.IndentingPrintWriter;
@@ -52,7 +50,6 @@
private final MonotonicClock mMonotonicClock;
private final Handler mHandler;
private final BatteryStatsImpl mBatteryStats;
- private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
private final PowerStatsAggregator mPowerStatsAggregator;
private long mLastSavedSpanEndMonotonicTime;
@@ -60,7 +57,7 @@
@DurationMillisLong long aggregatedPowerStatsSpanDuration,
@DurationMillisLong long powerStatsAggregationPeriod, PowerStatsStore powerStatsStore,
Clock clock, MonotonicClock monotonicClock, Handler handler,
- BatteryStatsImpl batteryStats, BatteryUsageStatsProvider batteryUsageStatsProvider) {
+ BatteryStatsImpl batteryStats) {
mContext = context;
mPowerStatsAggregator = powerStatsAggregator;
mAggregatedPowerStatsSpanDuration = aggregatedPowerStatsSpanDuration;
@@ -70,16 +67,15 @@
mMonotonicClock = monotonicClock;
mHandler = handler;
mBatteryStats = batteryStats;
- mBatteryUsageStatsProvider = batteryUsageStatsProvider;
}
/**
* Kicks off the scheduling of power stats aggregation spans.
*/
public void start(boolean enablePeriodicPowerStatsCollection) {
- mBatteryStats.setBatteryResetListener(this::storeBatteryUsageStatsOnReset);
mEnablePeriodicPowerStatsCollection = enablePeriodicPowerStatsCollection;
if (mEnablePeriodicPowerStatsCollection) {
+ schedulePowerStatsAggregation();
scheduleNextPowerStatsAggregation();
}
}
@@ -235,28 +231,6 @@
mPowerStatsStore.storeAggregatedPowerStats(stats);
}
- private void storeBatteryUsageStatsOnReset(int resetReason) {
- if (resetReason == BatteryStatsImpl.RESET_REASON_CORRUPT_FILE) {
- return;
- }
-
- final BatteryUsageStats batteryUsageStats =
- mBatteryUsageStatsProvider.getBatteryUsageStats(
- new BatteryUsageStatsQuery.Builder()
- .setMaxStatsAgeMs(0)
- .includePowerModels()
- .includeProcessStateData()
- .build());
-
- // TODO(b/188068523): BatteryUsageStats should use monotonic time for start and end
- // Once that change is made, we will be able to use the BatteryUsageStats' monotonic
- // start time
- long monotonicStartTime =
- mMonotonicClock.monotonicTime() - batteryUsageStats.getStatsDuration();
- mHandler.post(() ->
- mPowerStatsStore.storeBatteryUsageStats(monotonicStartTime, batteryUsageStats));
- }
-
private void awaitCompletion() {
ConditionVariable done = new ConditionVariable();
mHandler.post(done::open);
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsUidResolver.java b/services/core/java/com/android/server/power/stats/PowerStatsUidResolver.java
new file mode 100644
index 0000000..8dc3609
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PowerStatsUidResolver.java
@@ -0,0 +1,241 @@
+/*
+ * 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.power.stats;
+
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseIntArray;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Maintains a map of isolated UIDs to their respective owner UIDs, to support combining
+ * power stats for isolated UIDs, which are typically short-lived, into the corresponding app UID.
+ */
+public class PowerStatsUidResolver {
+ private static final String TAG = "PowerStatsUidResolver";
+
+ /**
+ * Listener notified when isolated UIDs are created and removed.
+ */
+ public interface Listener {
+
+ /**
+ * Callback invoked when a new isolated UID is registered.
+ */
+ void onIsolatedUidAdded(int isolatedUid, int parentUid);
+
+ /**
+ * Callback invoked before an isolated UID is evicted from the resolver.
+ * If the listener calls {@link PowerStatsUidResolver#retainIsolatedUid}, the mapping
+ * will be retained until {@link PowerStatsUidResolver#releaseIsolatedUid} is called.
+ */
+ void onBeforeIsolatedUidRemoved(int isolatedUid, int parentUid);
+
+ /**
+ * Callback invoked when an isolated UID to owner UID mapping is removed.
+ */
+ void onAfterIsolatedUidRemoved(int isolatedUid, int parentUid);
+ }
+
+ /**
+ * Mapping isolated uids to the actual owning app uid.
+ */
+ private final SparseIntArray mIsolatedUids = new SparseIntArray();
+
+ /**
+ * Internal reference count of isolated uids.
+ */
+ private final SparseIntArray mIsolatedUidRefCounts = new SparseIntArray();
+
+ // Keep the list read-only in order to avoid locking during the delivery of listener calls.
+ private volatile List<Listener> mListeners = Collections.emptyList();
+
+ /**
+ * Adds a listener.
+ */
+ public void addListener(Listener listener) {
+ synchronized (this) {
+ List<Listener> newList = new ArrayList<>(mListeners);
+ newList.add(listener);
+ mListeners = Collections.unmodifiableList(newList);
+ }
+ }
+
+ /**
+ * Removes a listener.
+ */
+ public void removeListener(Listener listener) {
+ synchronized (this) {
+ List<Listener> newList = new ArrayList<>(mListeners);
+ newList.remove(listener);
+ mListeners = Collections.unmodifiableList(newList);
+ }
+ }
+
+ /**
+ * Remembers the connection between a newly created isolated UID and its owner app UID.
+ * Calls {@link Listener#onIsolatedUidAdded} on each registered listener.
+ */
+ public void noteIsolatedUidAdded(int isolatedUid, int parentUid) {
+ synchronized (this) {
+ mIsolatedUids.put(isolatedUid, parentUid);
+ mIsolatedUidRefCounts.put(isolatedUid, 1);
+ }
+
+ List<Listener> listeners = mListeners;
+ for (int i = listeners.size() - 1; i >= 0; i--) {
+ listeners.get(i).onIsolatedUidAdded(isolatedUid, parentUid);
+ }
+ }
+
+ /**
+ * Handles the removal of an isolated UID by invoking
+ * {@link Listener#onBeforeIsolatedUidRemoved} on each registered listener and the releases
+ * the UID, see {@link #releaseIsolatedUid}.
+ */
+ public void noteIsolatedUidRemoved(int isolatedUid, int parentUid) {
+ synchronized (this) {
+ int curUid = mIsolatedUids.get(isolatedUid, -1);
+ if (curUid != parentUid) {
+ Slog.wtf(TAG, "Attempt to remove an isolated UID " + isolatedUid
+ + " with the parent UID " + parentUid
+ + ". The registered parent UID is " + curUid);
+ return;
+ }
+ }
+
+ List<Listener> listeners = mListeners;
+ for (int i = listeners.size() - 1; i >= 0; i--) {
+ listeners.get(i).onBeforeIsolatedUidRemoved(isolatedUid, parentUid);
+ }
+
+ releaseIsolatedUid(isolatedUid);
+ }
+
+ /**
+ * Increments the ref count for an isolated uid.
+ * Call #releaseIsolatedUid to decrement.
+ */
+ public void retainIsolatedUid(int uid) {
+ synchronized (this) {
+ final int refCount = mIsolatedUidRefCounts.get(uid, 0);
+ if (refCount <= 0) {
+ // Uid is not mapped or referenced
+ Slog.w(TAG,
+ "Attempted to increment ref counted of untracked isolated uid (" + uid
+ + ")");
+ return;
+ }
+ mIsolatedUidRefCounts.put(uid, refCount + 1);
+ }
+ }
+
+ /**
+ * Decrements the ref count for the given isolated UID. If the ref count drops to zero,
+ * removes the mapping and calls {@link Listener#onAfterIsolatedUidRemoved} on each registered
+ * listener.
+ */
+ public void releaseIsolatedUid(int isolatedUid) {
+ int parentUid;
+ synchronized (this) {
+ final int refCount = mIsolatedUidRefCounts.get(isolatedUid, 0) - 1;
+ if (refCount > 0) {
+ // Isolated uid is still being tracked
+ mIsolatedUidRefCounts.put(isolatedUid, refCount);
+ return;
+ }
+
+ final int idx = mIsolatedUids.indexOfKey(isolatedUid);
+ if (idx >= 0) {
+ parentUid = mIsolatedUids.valueAt(idx);
+ mIsolatedUids.removeAt(idx);
+ mIsolatedUidRefCounts.delete(isolatedUid);
+ } else {
+ Slog.w(TAG, "Attempted to remove untracked child uid (" + isolatedUid + ")");
+ return;
+ }
+ }
+
+ List<Listener> listeners = mListeners;
+ for (int i = listeners.size() - 1; i >= 0; i--) {
+ listeners.get(i).onAfterIsolatedUidRemoved(isolatedUid, parentUid);
+ }
+ }
+
+ /**
+ * Releases all isolated UIDs in the specified range, both ends inclusive.
+ */
+ public void releaseUidsInRange(int startUid, int endUid) {
+ IntArray toRelease;
+ synchronized (this) {
+ int startIndex = mIsolatedUids.indexOfKey(startUid);
+ int endIndex = mIsolatedUids.indexOfKey(endUid);
+
+ if (startIndex < 0) {
+ startIndex = ~startIndex;
+ }
+
+ if (endIndex < 0) {
+ // In this ~endIndex is pointing just past where endUid would be, so we must -1.
+ endIndex = ~endIndex - 1;
+ }
+
+ if (startIndex > endIndex) {
+ return;
+ }
+
+ toRelease = new IntArray(endIndex - startIndex);
+ for (int i = startIndex; i <= endIndex; i++) {
+ toRelease.add(mIsolatedUids.keyAt(i));
+ }
+ }
+
+ for (int i = toRelease.size() - 1; i >= 0; i--) {
+ releaseIsolatedUid(toRelease.get(i));
+ }
+ }
+
+ /**
+ * Given an isolated UID, returns the corresponding owner UID. For a non-isolated
+ * UID, returns the UID itself.
+ */
+ public int mapUid(int uid) {
+ synchronized (this) {
+ return mIsolatedUids.get(/*key=*/uid, /*valueIfKeyNotFound=*/uid);
+ }
+ }
+
+ /**
+ * Dumps the current contents of the resolver for the sake of dumpsys.
+ */
+ public void dump(PrintWriter pw) {
+ pw.println("Currently mapped isolated uids:");
+ synchronized (this) {
+ final int numIsolatedUids = mIsolatedUids.size();
+ for (int i = 0; i < numIsolatedUids; i++) {
+ final int isolatedUid = mIsolatedUids.keyAt(i);
+ final int ownerUid = mIsolatedUids.valueAt(i);
+ final int refs = mIsolatedUidRefCounts.get(isolatedUid);
+ pw.println(" " + isolatedUid + "->" + ownerUid + " (ref count = " + refs + ")");
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 0656a6a..59766ec 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -35,6 +35,7 @@
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.hardware.SensorPrivacyManager.EXTRA_ALL_SENSORS;
+import static android.hardware.SensorPrivacyManager.EXTRA_NOTIFICATION_ID;
import static android.hardware.SensorPrivacyManager.EXTRA_SENSOR;
import static android.hardware.SensorPrivacyManager.EXTRA_TOGGLE_TYPE;
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
@@ -164,6 +165,7 @@
private final AppOpsManagerInternal mAppOpsManagerInternal;
private final TelephonyManager mTelephonyManager;
private final PackageManagerInternal mPackageManagerInternal;
+ private final NotificationManager mNotificationManager;
private CameraPrivacyLightController mCameraPrivacyLightController;
@@ -188,6 +190,7 @@
mActivityTaskManager = context.getSystemService(ActivityTaskManager.class);
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mPackageManagerInternal = getLocalService(PackageManagerInternal.class);
+ mNotificationManager = mContext.getSystemService(NotificationManager.class);
mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl();
}
@@ -288,9 +291,18 @@
@Override
public void onReceive(Context context, Intent intent) {
setToggleSensorPrivacy(
- ((UserHandle) intent.getParcelableExtra(
- Intent.EXTRA_USER, android.os.UserHandle.class)).getIdentifier(), OTHER,
- intent.getIntExtra(EXTRA_SENSOR, UNKNOWN), false);
+ intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class)
+ .getIdentifier(),
+ OTHER,
+ intent.getIntExtra(EXTRA_SENSOR, UNKNOWN),
+ false
+ );
+
+ int notificationId =
+ intent.getIntExtra(EXTRA_NOTIFICATION_ID, SystemMessage.NOTE_UNKNOWN);
+ if (notificationId != SystemMessage.NOTE_UNKNOWN) {
+ mNotificationManager.cancel(notificationId);
+ }
}
}, new IntentFilter(ACTION_DISABLE_TOGGLE_SENSOR_PRIVACY),
MANAGE_SENSOR_PRIVACY, null, Context.RECEIVER_EXPORTED);
@@ -635,8 +647,6 @@
notificationId = SystemMessage.NOTE_UNBLOCK_CAM_TOGGLE;
}
- NotificationManager notificationManager =
- mContext.getSystemService(NotificationManager.class);
NotificationChannel channel = new NotificationChannel(
SENSOR_PRIVACY_CHANNEL_ID,
getUiContext().getString(R.string.sensor_privacy_notification_channel_label),
@@ -646,7 +656,7 @@
channel.enableVibration(false);
channel.setBlockable(false);
- notificationManager.createNotificationChannel(channel);
+ mNotificationManager.createNotificationChannel(channel);
Icon icon = Icon.createWithResource(getUiContext().getResources(), iconRes);
@@ -669,10 +679,11 @@
new Intent(ACTION_DISABLE_TOGGLE_SENSOR_PRIVACY)
.setPackage(mContext.getPackageName())
.putExtra(EXTRA_SENSOR, sensor)
+ .putExtra(EXTRA_NOTIFICATION_ID, notificationId)
.putExtra(Intent.EXTRA_USER, user),
PendingIntent.FLAG_IMMUTABLE
| PendingIntent.FLAG_UPDATE_CURRENT);
- notificationManager.notify(notificationId,
+ mNotificationManager.notify(notificationId,
new Notification.Builder(mContext, SENSOR_PRIVACY_CHANNEL_ID)
.setContentTitle(contentTitle)
.setContentText(contentText)
diff --git a/services/core/java/com/android/server/utils/UserSettingDeviceConfigMediator.java b/services/core/java/com/android/server/utils/UserSettingDeviceConfigMediator.java
new file mode 100644
index 0000000..e542349
--- /dev/null
+++ b/services/core/java/com/android/server/utils/UserSettingDeviceConfigMediator.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.provider.DeviceConfig;
+import android.util.KeyValueListParser;
+
+/**
+ * Helper class to mediate the value to use when a constant exists in both a key=value pair Settings
+ * constant (that can be parsed by {@link KeyValueListParser})
+ * and the {@link DeviceConfig} properties.
+ */
+public abstract class UserSettingDeviceConfigMediator {
+ private static final String TAG = UserSettingDeviceConfigMediator.class.getSimpleName();
+
+ @Nullable
+ protected DeviceConfig.Properties mProperties;
+ @NonNull
+ protected final KeyValueListParser mSettingsParser;
+
+ /**
+ * @param keyValueListDelimiter The delimiter passed into the {@link KeyValueListParser}.
+ */
+ protected UserSettingDeviceConfigMediator(char keyValueListDelimiter) {
+ mSettingsParser = new KeyValueListParser(keyValueListDelimiter);
+ }
+
+ /**
+ * Sets the key=value list string to read from. Setting {@code null} will clear any previously
+ * set string.
+ */
+ public void setSettingsString(@Nullable String settings) {
+ mSettingsParser.setString(settings);
+ }
+
+ /**
+ * Sets the DeviceConfig Properties to read from. Setting {@code null} will clear any previously
+ * set properties.
+ */
+ public void setDeviceConfigProperties(@Nullable DeviceConfig.Properties properties) {
+ mProperties = properties;
+ }
+
+ /**
+ * Get the value for key as a boolean.
+ *
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found, or not properly defined.
+ */
+ public abstract boolean getBoolean(@NonNull String key, boolean defaultValue);
+
+ /**
+ * Get the value for key as a float.
+ *
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found, or not properly defined.
+ */
+ public abstract float getFloat(@NonNull String key, float defaultValue);
+
+ /**
+ * Get the value for key as an int.
+ *
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found, or not properly defined.
+ */
+ public abstract int getInt(@NonNull String key, int defaultValue);
+
+ /**
+ * Get the value for key as a long.
+ *
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found, or not properly defined.
+ */
+ public abstract long getLong(@NonNull String key, long defaultValue);
+
+ /**
+ * Get the value for key as a String.
+ *
+ * @param key The key to lookup.
+ * @param defaultValue The value to return if the key was not found, or not properly defined.
+ */
+ public abstract String getString(@NonNull String key, @Nullable String defaultValue);
+
+ /**
+ * A mediator in which the existence of a single settings key-value pair will override usage
+ * of DeviceConfig properties. That is, if the Settings constant has any values set,
+ * then everything in the DeviceConfig namespace will be ignored.
+ */
+ public static class SettingsOverridesAllMediator extends UserSettingDeviceConfigMediator {
+ public SettingsOverridesAllMediator(char keyValueListDelimiter) {
+ super(keyValueListDelimiter);
+ }
+
+ @Override
+ public boolean getBoolean(@NonNull String key, boolean defaultValue) {
+ if (mSettingsParser.size() == 0) {
+ return mProperties == null
+ ? defaultValue : mProperties.getBoolean(key, defaultValue);
+ }
+ return mSettingsParser.getBoolean(key, defaultValue);
+ }
+
+ @Override
+ public float getFloat(@NonNull String key, float defaultValue) {
+ if (mSettingsParser.size() == 0) {
+ return mProperties == null ? defaultValue : mProperties.getFloat(key, defaultValue);
+ }
+ return mSettingsParser.getFloat(key, defaultValue);
+ }
+
+ @Override
+ public int getInt(@NonNull String key, int defaultValue) {
+ if (mSettingsParser.size() == 0) {
+ return mProperties == null ? defaultValue : mProperties.getInt(key, defaultValue);
+ }
+ return mSettingsParser.getInt(key, defaultValue);
+ }
+
+ @Override
+ public long getLong(@NonNull String key, long defaultValue) {
+ if (mSettingsParser.size() == 0) {
+ return mProperties == null ? defaultValue : mProperties.getLong(key, defaultValue);
+ }
+ return mSettingsParser.getLong(key, defaultValue);
+ }
+
+ @Override
+ public String getString(@NonNull String key, @Nullable String defaultValue) {
+ if (mSettingsParser.size() == 0) {
+ return mProperties == null
+ ? defaultValue : mProperties.getString(key, defaultValue);
+ }
+ return mSettingsParser.getString(key, defaultValue);
+ }
+ }
+
+ /**
+ * A mediator in which only individual keys in the DeviceConfig namespace will be overridden
+ * by the same key in the Settings constant. If the Settings constant does not have a specific
+ * key set, then the DeviceConfig value will be used instead.
+ */
+ public static class SettingsOverridesIndividualMediator
+ extends UserSettingDeviceConfigMediator {
+ public SettingsOverridesIndividualMediator(char keyValueListDelimiter) {
+ super(keyValueListDelimiter);
+ }
+
+ @Override
+ public boolean getBoolean(@NonNull String key, boolean defaultValue) {
+ return mSettingsParser.getBoolean(key,
+ mProperties == null ? defaultValue : mProperties.getBoolean(key, defaultValue));
+ }
+
+ @Override
+ public float getFloat(@NonNull String key, float defaultValue) {
+ return mSettingsParser.getFloat(key,
+ mProperties == null ? defaultValue : mProperties.getFloat(key, defaultValue));
+ }
+
+ @Override
+ public int getInt(@NonNull String key, int defaultValue) {
+ return mSettingsParser.getInt(key,
+ mProperties == null ? defaultValue : mProperties.getInt(key, defaultValue));
+ }
+
+ @Override
+ public long getLong(@NonNull String key, long defaultValue) {
+ return mSettingsParser.getLong(key,
+ mProperties == null ? defaultValue : mProperties.getLong(key, defaultValue));
+ }
+
+ @Override
+ public String getString(@NonNull String key, @Nullable String defaultValue) {
+ return mSettingsParser.getString(key,
+ mProperties == null ? defaultValue : mProperties.getString(key, defaultValue));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 68f554c..ea8a801 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -16,6 +16,8 @@
package com.android.server.webkit;
+import static android.webkit.Flags.updateServiceV2;
+
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.content.Context;
@@ -237,18 +239,30 @@
@Override
public int getMultiProcessSetting(Context context) {
- return Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.WEBVIEW_MULTIPROCESS, 0);
+ if (updateServiceV2()) {
+ throw new IllegalStateException(
+ "getMultiProcessSetting shouldn't be called if update_service_v2 flag is set.");
+ }
+ return Settings.Global.getInt(
+ context.getContentResolver(), Settings.Global.WEBVIEW_MULTIPROCESS, 0);
}
@Override
public void setMultiProcessSetting(Context context, int value) {
- Settings.Global.putInt(context.getContentResolver(),
- Settings.Global.WEBVIEW_MULTIPROCESS, value);
+ if (updateServiceV2()) {
+ throw new IllegalStateException(
+ "setMultiProcessSetting shouldn't be called if update_service_v2 flag is set.");
+ }
+ Settings.Global.putInt(
+ context.getContentResolver(), Settings.Global.WEBVIEW_MULTIPROCESS, value);
}
@Override
public void notifyZygote(boolean enableMultiProcess) {
+ if (updateServiceV2()) {
+ throw new IllegalStateException(
+ "notifyZygote shouldn't be called if update_service_v2 flag is set.");
+ }
WebViewZygote.setMultiprocessEnabled(enableMultiProcess);
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index b3672ec..e9c4096 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -157,8 +157,13 @@
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
- (new WebViewUpdateServiceShellCommand(this)).exec(
- this, in, out, err, args, callback, resultReceiver);
+ if (updateServiceV2()) {
+ (new WebViewUpdateServiceShellCommand2(this))
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ } else {
+ (new WebViewUpdateServiceShellCommand(this))
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
}
@@ -253,6 +258,11 @@
}
@Override // Binder call
+ public WebViewProviderInfo getDefaultWebViewPackage() {
+ return WebViewUpdateService.this.mImpl.getDefaultWebViewPackage();
+ }
+
+ @Override // Binder call
public WebViewProviderInfo[] getAllWebViewPackages() {
return WebViewUpdateService.this.mImpl.getWebViewPackages();
}
@@ -275,18 +285,31 @@
@Override // Binder call
public boolean isMultiProcessEnabled() {
+ if (updateServiceV2()) {
+ throw new IllegalStateException(
+ "isMultiProcessEnabled shouldn't be called if update_service_v2 flag is"
+ + " set.");
+ }
return WebViewUpdateService.this.mImpl.isMultiProcessEnabled();
}
@Override // Binder call
public void enableMultiProcess(boolean enable) {
- if (getContext().checkCallingPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ if (updateServiceV2()) {
+ throw new IllegalStateException(
+ "enableMultiProcess shouldn't be called if update_service_v2 flag is set.");
+ }
+ if (getContext()
+ .checkCallingPermission(
+ android.Manifest.permission.WRITE_SECURE_SETTINGS)
!= PackageManager.PERMISSION_GRANTED) {
- String msg = "Permission Denial: enableMultiProcess() from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid()
- + " requires " + android.Manifest.permission.WRITE_SECURE_SETTINGS;
+ String msg =
+ "Permission Denial: enableMultiProcess() from pid="
+ + Binder.getCallingPid()
+ + ", uid="
+ + Binder.getCallingUid()
+ + " requires "
+ + android.Manifest.permission.WRITE_SECURE_SETTINGS;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index cfdef14..60dc4ff 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -17,6 +17,7 @@
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
@@ -29,8 +30,12 @@
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
+import com.android.server.LocalServices;
+import com.android.server.PinnerService;
+
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -88,6 +93,8 @@
private static final int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE;
private static final int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE;
+ private static final String PIN_GROUP = "webview";
+
private final SystemInterface mSystemInterface;
private final Context mContext;
@@ -339,6 +346,34 @@
return newPackage;
}
+ private void pinWebviewIfRequired(ApplicationInfo appInfo) {
+ PinnerService pinnerService = LocalServices.getService(PinnerService.class);
+ int webviewPinQuota = pinnerService.getWebviewPinQuota();
+ if (webviewPinQuota <= 0) {
+ return;
+ }
+
+ pinnerService.unpinGroup(PIN_GROUP);
+
+ ArrayList<String> apksToPin = new ArrayList<>();
+ boolean pinSharedFirst = appInfo.metaData.getBoolean("PIN_SHARED_LIBS_FIRST", true);
+ for (String sharedLib : appInfo.sharedLibraryFiles) {
+ apksToPin.add(sharedLib);
+ }
+ apksToPin.add(appInfo.sourceDir);
+ if (!pinSharedFirst) {
+ // We want to prioritize pinning of the native library that is most likely used by apps
+ // which in some build flavors live in the main apk and as a shared library for others.
+ Collections.reverse(apksToPin);
+ }
+ for (String apk : apksToPin) {
+ if (webviewPinQuota <= 0) {
+ break;
+ }
+ int bytesPinned = pinnerService.pinFile(apk, webviewPinQuota, appInfo, PIN_GROUP);
+ webviewPinQuota -= bytesPinned;
+ }
+ }
/**
* This is called when we change WebView provider, either when the current provider is
* updated or a new provider is chosen / takes precedence.
@@ -347,6 +382,7 @@
synchronized (mLock) {
mAnyWebViewInstalled = true;
if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+ pinWebviewIfRequired(newPackage.applicationInfo);
mCurrentWebViewPackage = newPackage;
// The relro creations might 'finish' (not start at all) before
@@ -385,6 +421,13 @@
return providers;
}
+ @Override
+ public WebViewProviderInfo getDefaultWebViewPackage() {
+ throw new IllegalStateException(
+ "getDefaultWebViewPackage shouldn't be called if update_service_v2 flag is"
+ + " disabled.");
+ }
+
private static class ProviderAndPackageInfo {
public final WebViewProviderInfo provider;
public final PackageInfo packageInfo;
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
index e618c7e..29782d9 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -23,6 +23,8 @@
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;
@@ -70,10 +72,6 @@
WebViewPackageMissingException(String message) {
super(message);
}
-
- WebViewPackageMissingException(Exception e) {
- super(e);
- }
}
private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
@@ -85,9 +83,6 @@
private static final int VALIDITY_INCORRECT_SIGNATURE = 3;
private static final int VALIDITY_NO_LIBRARY_FLAG = 4;
- private static final int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE;
- private static final int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE;
-
private final SystemInterface mSystemInterface;
private final Context mContext;
@@ -166,7 +161,6 @@
@Override
public void prepareWebViewInSystemServer() {
- mSystemInterface.notifyZygote(isMultiProcessEnabled());
try {
synchronized (mLock) {
mCurrentWebViewPackage = findPreferredWebViewPackage();
@@ -366,14 +360,10 @@
// Once we've notified the system that the provider has changed and started RELRO creation,
// try to restart the zygote so that it will be ready when apps use it.
- if (isMultiProcessEnabled()) {
- AsyncTask.THREAD_POOL_EXECUTOR.execute(this::startZygoteWhenReady);
- }
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(this::startZygoteWhenReady);
}
- /**
- * Fetch only the currently valid WebView packages.
- **/
+ /** Fetch only the currently valid WebView packages. */
@Override
public WebViewProviderInfo[] getValidWebViewPackages() {
ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
@@ -385,6 +375,23 @@
return providers;
}
+ /**
+ * Returns the default WebView provider which should be first availableByDefault option in the
+ * system config.
+ */
+ @Override
+ public WebViewProviderInfo getDefaultWebViewPackage() {
+ WebViewProviderInfo[] webviewProviders = getWebViewPackages();
+ for (WebViewProviderInfo provider : webviewProviders) {
+ if (provider.availableByDefault) {
+ return provider;
+ }
+ }
+ // This should be unreachable because the config parser enforces that there is at least one
+ // availableByDefault provider.
+ throw new AndroidRuntimeException("No available by default WebView Provider.");
+ }
+
private static class ProviderAndPackageInfo {
public final WebViewProviderInfo provider;
public final PackageInfo packageInfo;
@@ -632,62 +639,56 @@
@Override
public boolean isMultiProcessEnabled() {
- int settingValue = mSystemInterface.getMultiProcessSetting(mContext);
- if (mSystemInterface.isMultiProcessDefaultEnabled()) {
- // Multiprocess should be enabled unless the user has turned it off manually.
- return settingValue > MULTIPROCESS_SETTING_OFF_VALUE;
- } else {
- // Multiprocess should not be enabled, unless the user has turned it on manually.
- return settingValue >= MULTIPROCESS_SETTING_ON_VALUE;
- }
+ throw new IllegalStateException(
+ "isMultiProcessEnabled shouldn't be called if update_service_v2 flag is set.");
}
@Override
public void enableMultiProcess(boolean enable) {
- PackageInfo current = getCurrentWebViewPackage();
- mSystemInterface.setMultiProcessSetting(mContext,
- enable ? MULTIPROCESS_SETTING_ON_VALUE : MULTIPROCESS_SETTING_OFF_VALUE);
- mSystemInterface.notifyZygote(enable);
- if (current != null) {
- mSystemInterface.killPackageDependents(current.packageName);
- }
+ throw new IllegalStateException(
+ "enableMultiProcess shouldn't be called if update_service_v2 flag is set.");
}
- /**
- * Dump the state of this Service.
- */
+ /** Dump the state of this Service. */
@Override
public void dumpState(PrintWriter pw) {
pw.println("Current WebView Update Service state");
- pw.println(String.format(" Multiprocess enabled: %b", isMultiProcessEnabled()));
synchronized (mLock) {
if (mCurrentWebViewPackage == null) {
pw.println(" Current WebView package is null");
} else {
- pw.println(String.format(" Current WebView package (name, version): (%s, %s)",
- mCurrentWebViewPackage.packageName,
- mCurrentWebViewPackage.versionName));
+ pw.println(
+ TextUtils.formatSimple(
+ " Current WebView package (name, version): (%s, %s)",
+ mCurrentWebViewPackage.packageName,
+ mCurrentWebViewPackage.versionName));
}
- pw.println(String.format(" Minimum targetSdkVersion: %d",
- UserPackage.MINIMUM_SUPPORTED_SDK));
- pw.println(String.format(" Minimum WebView version code: %d",
- mMinimumVersionCode));
- pw.println(String.format(" Number of relros started: %d",
- mNumRelroCreationsStarted));
- pw.println(String.format(" Number of relros finished: %d",
- mNumRelroCreationsFinished));
- pw.println(String.format(" WebView package dirty: %b", mWebViewPackageDirty));
- pw.println(String.format(" Any WebView package installed: %b",
- mAnyWebViewInstalled));
+ pw.println(
+ TextUtils.formatSimple(
+ " Minimum targetSdkVersion: %d", UserPackage.MINIMUM_SUPPORTED_SDK));
+ pw.println(
+ TextUtils.formatSimple(
+ " Minimum WebView version code: %d", mMinimumVersionCode));
+ pw.println(
+ TextUtils.formatSimple(
+ " Number of relros started: %d", mNumRelroCreationsStarted));
+ pw.println(
+ TextUtils.formatSimple(
+ " Number of relros finished: %d", mNumRelroCreationsFinished));
+ pw.println(TextUtils.formatSimple(" WebView package dirty: %b", mWebViewPackageDirty));
+ pw.println(
+ TextUtils.formatSimple(
+ " Any WebView package installed: %b", mAnyWebViewInstalled));
try {
PackageInfo preferredWebViewPackage = findPreferredWebViewPackage();
- pw.println(String.format(
- " Preferred WebView package (name, version): (%s, %s)",
- preferredWebViewPackage.packageName,
- preferredWebViewPackage.versionName));
+ pw.println(
+ TextUtils.formatSimple(
+ " Preferred WebView package (name, version): (%s, %s)",
+ preferredWebViewPackage.packageName,
+ preferredWebViewPackage.versionName));
} catch (WebViewPackageMissingException e) {
- pw.println(String.format(" Preferred WebView package: none"));
+ pw.println(" Preferred WebView package: none");
}
dumpAllPackageInformationLocked(pw);
@@ -703,29 +704,36 @@
PackageInfo systemUserPackageInfo =
userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo();
if (systemUserPackageInfo == null) {
- pw.println(String.format(" %s is NOT installed.", provider.packageName));
+ pw.println(
+ TextUtils.formatSimple(" %s is NOT installed.", provider.packageName));
continue;
}
int validity = validityResult(provider, systemUserPackageInfo);
- String packageDetails = String.format(
- "versionName: %s, versionCode: %d, targetSdkVersion: %d",
- systemUserPackageInfo.versionName,
- systemUserPackageInfo.getLongVersionCode(),
- systemUserPackageInfo.applicationInfo.targetSdkVersion);
+ String packageDetails =
+ TextUtils.formatSimple(
+ "versionName: %s, versionCode: %d, targetSdkVersion: %d",
+ systemUserPackageInfo.versionName,
+ systemUserPackageInfo.getLongVersionCode(),
+ systemUserPackageInfo.applicationInfo.targetSdkVersion);
if (validity == VALIDITY_OK) {
- boolean installedForAllUsers = isInstalledAndEnabledForAllUsers(
- mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider));
- pw.println(String.format(
- " Valid package %s (%s) is %s installed/enabled for all users",
- systemUserPackageInfo.packageName,
- packageDetails,
- installedForAllUsers ? "" : "NOT"));
+ boolean installedForAllUsers =
+ isInstalledAndEnabledForAllUsers(
+ mSystemInterface.getPackageInfoForProviderAllUsers(
+ mContext, provider));
+ pw.println(
+ TextUtils.formatSimple(
+ " Valid package %s (%s) is %s installed/enabled for all users",
+ systemUserPackageInfo.packageName,
+ packageDetails,
+ installedForAllUsers ? "" : "NOT"));
} else {
- pw.println(String.format(" Invalid package %s (%s), reason: %s",
- systemUserPackageInfo.packageName,
- packageDetails,
- getInvalidityReason(validity)));
+ pw.println(
+ TextUtils.formatSimple(
+ " Invalid package %s (%s), reason: %s",
+ systemUserPackageInfo.packageName,
+ packageDetails,
+ getInvalidityReason(validity)));
}
}
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
index a9c3dc4..1772ef9 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
@@ -40,6 +40,8 @@
WebViewProviderInfo[] getValidWebViewPackages();
+ WebViewProviderInfo getDefaultWebViewPackage();
+
PackageInfo getCurrentWebViewPackage();
boolean isMultiProcessEnabled();
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand2.java
new file mode 100644
index 0000000..ce95b18
--- /dev/null
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand2.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 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.webkit;
+
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.text.TextUtils;
+import android.webkit.IWebViewUpdateService;
+
+import java.io.PrintWriter;
+
+class WebViewUpdateServiceShellCommand2 extends ShellCommand {
+ final IWebViewUpdateService mInterface;
+
+ WebViewUpdateServiceShellCommand2(IWebViewUpdateService service) {
+ mInterface = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+
+ final PrintWriter pw = getOutPrintWriter();
+ try {
+ switch (cmd) {
+ case "set-webview-implementation":
+ return setWebViewImplementation();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (RemoteException e) {
+ pw.println("Remote exception: " + e);
+ }
+ return -1;
+ }
+
+ private int setWebViewImplementation() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ String shellChosenPackage = getNextArg();
+ if (shellChosenPackage == null) {
+ pw.println("Failed to switch, no PACKAGE provided.");
+ pw.println("");
+ helpSetWebViewImplementation();
+ return 1;
+ }
+ String newPackage = mInterface.changeProviderAndSetting(shellChosenPackage);
+ if (shellChosenPackage.equals(newPackage)) {
+ pw.println("Success");
+ return 0;
+ } else {
+ pw.println(
+ TextUtils.formatSimple(
+ "Failed to switch to %s, the WebView implementation is now provided by"
+ + " %s.",
+ shellChosenPackage, newPackage));
+ return 1;
+ }
+ }
+
+ public void helpSetWebViewImplementation() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println(" set-webview-implementation PACKAGE");
+ pw.println(" Set the WebView implementation to the specified package.");
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("WebView updater commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println("");
+ helpSetWebViewImplementation();
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 8498368..75e6faf 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2731,7 +2731,7 @@
* Receive the splash screen data from shell, sending to client.
* @param parcelable The data to reconstruct the splash screen view, null mean unable to copy.
*/
- void onCopySplashScreenFinish(SplashScreenViewParcelable parcelable) {
+ void onCopySplashScreenFinish(@Nullable SplashScreenViewParcelable parcelable) {
removeTransferSplashScreenTimeout();
final SurfaceControl windowAnimationLeash = (parcelable == null
|| mTransferringSplashScreenState != TRANSFER_SPLASH_SCREEN_COPYING
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d6302e0..bbaa691 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1582,6 +1582,7 @@
// An activity has changed order/visibility or the task is occluded by a transient
// activity, so this isn't just deliver-to-top
&& mMovedToTopActivity == null
+ && !transitionController.hasOrderChanges()
&& !transitionController.isTransientHide(startedActivityRootTask)) {
// We just delivered to top, so there isn't an actual transition here.
if (!forceTransientTransition) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 34c7eee..6f5c676 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3618,8 +3618,9 @@
* @hide
*/
@Override
- public void onSplashScreenViewCopyFinished(int taskId, SplashScreenViewParcelable parcelable)
- throws RemoteException {
+ public void onSplashScreenViewCopyFinished(int taskId,
+ @Nullable SplashScreenViewParcelable parcelable)
+ throws RemoteException {
mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_TASKS,
"copySplashScreenViewFinish()");
synchronized (mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 43f3209..be7b855 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -429,9 +429,12 @@
final TaskFragment prevTFAdjacent = prevTF.getAdjacentTaskFragment();
if (prevTFAdjacent != null) {
if (prevTFAdjacent == currTF) {
- // Cannot predict what will happen when app receive back key, skip animation.
outPrevActivities.clear();
- return false;
+ // No more activity in task, so it can predict if previous task exists.
+ // Otherwise, unable to predict what will happen when app receive
+ // back key, skip animation.
+ return currentTask.getActivity((below) -> !below.finishing, prevActivity,
+ false /*includeBoundary*/, true /*traverseTopToBottom*/) == null;
} else {
final ActivityRecord prevActivityAdjacent =
prevTFAdjacent.getTopNonFinishingActivity();
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 39e900a..6e7cf12 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -63,6 +63,7 @@
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;
@@ -267,20 +268,25 @@
mIntent = intent;
mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
if (originatingPendingIntent == null) {
- // grant creator BAL privileges unless explicitly opted out
+ // grant BAL privileges unless explicitly opted out
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(
callingUid, callingPackage, checkedOptions);
+ mBalAllowedByPiSender =
+ PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
+ checkedOptions, realCallingUid, mRealCallingPackage);
}
- mBalAllowedByPiSender =
- PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
- checkedOptions, realCallingUid, mRealCallingPackage);
mAppSwitchState = mService.getBalAppSwitchesState();
mCallingUidProcState = mService.mActiveUids.getUidState(callingUid);
mIsCallingUidPersistentSystemProcess =
@@ -579,11 +585,12 @@
resultForCaller.allows() && resultForRealCaller.blocks());
}
+ // Handle cases with explicit opt-in
if (resultForCaller.allows()
&& checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Activity start explicitly allowed by PI creator. "
+ Slog.d(TAG, "Activity start explicitly allowed by caller. "
+ state.dump(resultForCaller, resultForRealCaller));
}
return statsLog(resultForCaller, state);
@@ -592,11 +599,12 @@
&& checkedOptions.getPendingIntentBackgroundActivityStartMode()
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "Activity start explicitly allowed by PI sender. "
+ Slog.d(TAG, "Activity start explicitly allowed by real caller. "
+ state.dump(resultForCaller, resultForRealCaller));
}
return statsLog(resultForRealCaller, state);
}
+ // Handle PendingIntent cases with default behavior next
boolean callerCanAllow = resultForCaller.allows()
&& checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
@@ -716,17 +724,18 @@
// is allowed, or apps like live wallpaper with non app visible window will be allowed.
final boolean appSwitchAllowedOrFg =
appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
- final boolean allowCallingUidStartActivity =
- ((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
- && callingUidHasAnyVisibleWindow)
- || isCallingUidPersistentSystemProcess;
- if (allowCallingUidStartActivity) {
+ if (appSwitchAllowedOrFg && callingUidHasAnyVisibleWindow) {
return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
- /*background*/ false,
- "callingUidHasAnyVisibleWindow = "
- + callingUid
- + ", isCallingUidPersistentSystemProcess = "
- + isCallingUidPersistentSystemProcess);
+ /*background*/ false, "callingUid has visible window");
+ }
+ if (mService.mActiveUids.hasNonAppVisibleWindow(callingUid)) {
+ return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
+ /*background*/ false, "callingUid has non-app visible window");
+ }
+
+ if (isCallingUidPersistentSystemProcess) {
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
+ /*background*/ false, "callingUid is persistent system process");
}
// don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
@@ -806,13 +815,29 @@
"realCallingUid has BAL permission.");
}
- // don't abort if the realCallingUid has a visible window
- // TODO(b/171459802): We should check appSwitchAllowed also
- if (state.mRealCallingUidHasAnyVisibleWindow) {
- return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
- /*background*/ false,
- "realCallingUid has visible (non-toast) window.");
+ // Normal apps with visible app window will be allowed to start activity if app switching
+ // is allowed, or apps like live wallpaper with non app visible window will be allowed.
+ final boolean appSwitchAllowedOrFg = state.mAppSwitchState == APP_SWITCH_ALLOW
+ || state.mAppSwitchState == APP_SWITCH_FG_ONLY;
+ if (Flags.balImproveRealCallerVisibilityCheck()) {
+ if (appSwitchAllowedOrFg && state.mRealCallingUidHasAnyVisibleWindow) {
+ return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ /*background*/ false, "realCallingUid has visible window");
+ }
+ if (mService.mActiveUids.hasNonAppVisibleWindow(state.mRealCallingUid)) {
+ return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ /*background*/ false, "realCallingUid has non-app visible window");
+ }
+ } else {
+ // don't abort if the realCallingUid has a visible window
+ // TODO(b/171459802): We should check appSwitchAllowed also
+ if (state.mRealCallingUidHasAnyVisibleWindow) {
+ return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ /*background*/ false,
+ "realCallingUid has visible (non-toast) window.");
+ }
}
+
// if the realCallingUid is a persistent system process, abort if the IntentSender
// wasn't allowed to start an activity
if (state.mForcedBalByPiSender.allowsBackgroundActivityStarts()
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 2fabb0e..7ce9de4 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -83,6 +83,7 @@
/**
* Mark all dims as pending completion on the next call to {@link #updateDims}
*
+ * Called before iterating on mHost's children, first step of dimming.
* This is intended for us by the host container, to be called at the beginning of
* {@link WindowContainer#prepareSurfaces}. After calling this, the container should
* chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
@@ -100,8 +101,7 @@
/**
* Call after invoking {@link WindowContainer#prepareSurfaces} on children as
- * described in {@link #resetDimStates}. The dim bounds returned by {@link #resetDimStates}
- * should be set before calling this method.
+ * described in {@link #resetDimStates}.
*
* @param t A transaction in which to update the dims.
* @return true if any Dims were updated.
diff --git a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
new file mode 100644
index 0000000..e91857f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DIMMER;
+import static com.android.server.wm.AlphaAnimationSpecProto.DURATION_MS;
+import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
+import static com.android.server.wm.AlphaAnimationSpecProto.TO;
+import static com.android.server.wm.AnimationSpecProto.ALPHA;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+import android.view.SurfaceControl;
+
+import com.android.internal.protolog.common.ProtoLog;
+
+import java.io.PrintWriter;
+
+/**
+ * Contains the information relative to the changes to apply to the dim layer
+ */
+public class DimmerAnimationHelper {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "DimmerAnimationHelper" : TAG_WM;
+ private static final int DEFAULT_DIM_ANIM_DURATION_MS = 200;
+
+ /**
+ * Contains the requested changes
+ */
+ static class Change {
+ private float mAlpha = -1f;
+ private int mBlurRadius = -1;
+ private WindowContainer mDimmingContainer = null;
+ private int mRelativeLayer = -1;
+ private static final float EPSILON = 0.0001f;
+
+ Change() {}
+
+ Change(Change other) {
+ mAlpha = other.mAlpha;
+ mBlurRadius = other.mBlurRadius;
+ mDimmingContainer = other.mDimmingContainer;
+ mRelativeLayer = other.mRelativeLayer;
+ }
+
+ // Same alpha and blur
+ boolean hasSameVisualProperties(Change other) {
+ return Math.abs(mAlpha - other.mAlpha) < EPSILON && mBlurRadius == other.mBlurRadius;
+ }
+
+ boolean hasSameDimmingContainer(Change other) {
+ return mDimmingContainer != null && mDimmingContainer == other.mDimmingContainer;
+ }
+
+ void inheritPropertiesFromAnimation(AnimationSpec anim) {
+ mAlpha = anim.mCurrentAlpha;
+ mBlurRadius = anim.mCurrentBlur;
+ }
+
+ @Override
+ public String toString() {
+ return "Dim state: alpha=" + mAlpha + ", blur=" + mBlurRadius + ", container="
+ + mDimmingContainer + ", relativePosition=" + mRelativeLayer;
+ }
+ }
+
+ private Change mCurrentProperties = new Change();
+ private Change mRequestedProperties = new Change();
+ private AnimationSpec mAlphaAnimationSpec;
+
+ private final AnimationAdapterFactory mAnimationAdapterFactory;
+ private AnimationAdapter mLocalAnimationAdapter;
+
+ DimmerAnimationHelper(AnimationAdapterFactory animationFactory) {
+ mAnimationAdapterFactory = animationFactory;
+ }
+
+ void setExitParameters() {
+ setRequestedRelativeParent(mRequestedProperties.mDimmingContainer, -1 /* relativeLayer */);
+ setRequestedAppearance(0f /* alpha */, 0 /* blur */);
+ }
+
+ // Sets a requested change without applying it immediately
+ void setRequestedRelativeParent(WindowContainer relativeParent, int relativeLayer) {
+ mRequestedProperties.mDimmingContainer = relativeParent;
+ mRequestedProperties.mRelativeLayer = relativeLayer;
+ }
+
+ // Sets a requested change without applying it immediately
+ void setRequestedAppearance(float alpha, int blurRadius) {
+ mRequestedProperties.mAlpha = alpha;
+ mRequestedProperties.mBlurRadius = blurRadius;
+ }
+
+ /**
+ * Commit the last changes we received. Called after
+ * {@link Change#setExitParameters()},
+ * {@link Change#setRequestedRelativeParent(WindowContainer, int)}, or
+ * {@link Change#setRequestedAppearance(float, int)}
+ */
+ void applyChanges(SurfaceControl.Transaction t, SmoothDimmer.DimState dim) {
+ if (mRequestedProperties.mDimmingContainer == null) {
+ Log.e(TAG, this + " does not have a dimming container. Have you forgotten to "
+ + "call adjustRelativeLayer?");
+ return;
+ }
+ if (mRequestedProperties.mDimmingContainer.mSurfaceControl == null) {
+ Log.w(TAG, "container " + mRequestedProperties.mDimmingContainer
+ + "does not have a surface");
+ dim.remove(t);
+ return;
+ }
+
+ dim.ensureVisible(t);
+ relativeReparent(dim.mDimSurface,
+ mRequestedProperties.mDimmingContainer.getSurfaceControl(),
+ mRequestedProperties.mRelativeLayer, t);
+
+ if (!mCurrentProperties.hasSameVisualProperties(mRequestedProperties)) {
+ stopCurrentAnimation(dim.mDimSurface);
+
+ if (dim.mSkipAnimation
+ // If the container doesn't change but requests a dim change, then it is
+ // directly providing us the animated values
+ || (mRequestedProperties.hasSameDimmingContainer(mCurrentProperties)
+ && dim.isDimming())) {
+ ProtoLog.d(WM_DEBUG_DIMMER,
+ "%s skipping animation and directly setting alpha=%f, blur=%d",
+ dim, mRequestedProperties.mAlpha,
+ mRequestedProperties.mBlurRadius);
+ setAlphaBlur(dim.mDimSurface, mRequestedProperties.mAlpha,
+ mRequestedProperties.mBlurRadius, t);
+ dim.mSkipAnimation = false;
+ } else {
+ startAnimation(t, dim);
+ }
+
+ } else if (!dim.isDimming()) {
+ // We are not dimming, so we tried the exit animation but the alpha is already 0,
+ // therefore, let's just remove this surface
+ dim.remove(t);
+ }
+ mCurrentProperties = new Change(mRequestedProperties);
+ }
+
+ private void startAnimation(
+ SurfaceControl.Transaction t, SmoothDimmer.DimState dim) {
+ ProtoLog.v(WM_DEBUG_DIMMER, "Starting animation on %s", dim);
+ mAlphaAnimationSpec = getRequestedAnimationSpec();
+ mLocalAnimationAdapter = mAnimationAdapterFactory.get(mAlphaAnimationSpec,
+ dim.mHostContainer.mWmService.mSurfaceAnimationRunner);
+
+ float targetAlpha = mRequestedProperties.mAlpha;
+ int targetBlur = mRequestedProperties.mBlurRadius;
+
+ mLocalAnimationAdapter.startAnimation(dim.mDimSurface, t,
+ ANIMATION_TYPE_DIMMER, /* finishCallback */ (type, animator) -> {
+ setAlphaBlur(dim.mDimSurface, targetAlpha, targetBlur, t);
+ if (targetAlpha == 0f && !dim.isDimming()) {
+ dim.remove(t);
+ }
+ mLocalAnimationAdapter = null;
+ mAlphaAnimationSpec = null;
+ });
+ }
+
+ private boolean isAnimating() {
+ return mAlphaAnimationSpec != null;
+ }
+
+ void stopCurrentAnimation(SurfaceControl surface) {
+ if (mLocalAnimationAdapter != null && isAnimating()) {
+ // Save the current animation progress and cancel the animation
+ mCurrentProperties.inheritPropertiesFromAnimation(mAlphaAnimationSpec);
+ mLocalAnimationAdapter.onAnimationCancelled(surface);
+ mLocalAnimationAdapter = null;
+ mAlphaAnimationSpec = null;
+ }
+ }
+
+ private AnimationSpec getRequestedAnimationSpec() {
+ final float startAlpha = Math.max(mCurrentProperties.mAlpha, 0f);
+ final int startBlur = Math.max(mCurrentProperties.mBlurRadius, 0);
+ long duration = (long) (getDimDuration(mRequestedProperties.mDimmingContainer)
+ * Math.abs(mRequestedProperties.mAlpha - startAlpha));
+
+ final AnimationSpec spec = new AnimationSpec(
+ new AnimationSpec.AnimationExtremes<>(startAlpha, mRequestedProperties.mAlpha),
+ new AnimationSpec.AnimationExtremes<>(startBlur, mRequestedProperties.mBlurRadius),
+ duration
+ );
+ ProtoLog.v(WM_DEBUG_DIMMER, "Dim animation requested: %s", spec);
+ return spec;
+ }
+
+ /**
+ * Change the relative parent of this dim layer
+ */
+ void relativeReparent(SurfaceControl dimLayer, SurfaceControl relativeParent,
+ int relativePosition, SurfaceControl.Transaction t) {
+ try {
+ t.setRelativeLayer(dimLayer, relativeParent, relativePosition);
+ } catch (NullPointerException e) {
+ Log.w(TAG, "Tried to change parent of dim " + dimLayer + " after remove", e);
+ }
+ }
+
+ void setAlphaBlur(SurfaceControl sc, float alpha, int blur, SurfaceControl.Transaction t) {
+ try {
+ t.setAlpha(sc, alpha);
+ t.setBackgroundBlurRadius(sc, blur);
+ } catch (NullPointerException e) {
+ Log.w(TAG , "Tried to change look of dim " + sc + " after remove", e);
+ }
+ }
+
+ private long getDimDuration(WindowContainer container) {
+ // Use the same duration as the animation on the WindowContainer
+ AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
+ final float durationScale = container.mWmService.getTransitionAnimationScaleLocked();
+ return animationAdapter == null ? (long) (DEFAULT_DIM_ANIM_DURATION_MS * durationScale)
+ : animationAdapter.getDurationHint();
+ }
+
+ /**
+ * Collects the animation specifics
+ */
+ static class AnimationSpec implements LocalAnimationAdapter.AnimationSpec {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "DimmerAnimationSpec" : TAG_WM;
+
+ static class AnimationExtremes<T> {
+ final T mStartValue;
+ final T mFinishValue;
+
+ AnimationExtremes(T fromValue, T toValue) {
+ mStartValue = fromValue;
+ mFinishValue = toValue;
+ }
+
+ @Override
+ public String toString() {
+ return "[" + mStartValue + "->" + mFinishValue + "]";
+ }
+ }
+
+ private final long mDuration;
+ private final AnimationSpec.AnimationExtremes<Float> mAlpha;
+ private final AnimationSpec.AnimationExtremes<Integer> mBlur;
+
+ float mCurrentAlpha = 0;
+ int mCurrentBlur = 0;
+ boolean mStarted = false;
+
+ AnimationSpec(AnimationSpec.AnimationExtremes<Float> alpha,
+ AnimationSpec.AnimationExtremes<Integer> blur, long duration) {
+ mAlpha = alpha;
+ mBlur = blur;
+ mDuration = duration;
+ }
+
+ @Override
+ public long getDuration() {
+ return mDuration;
+ }
+
+ @Override
+ public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
+ if (!mStarted) {
+ // The first frame would end up in the sync transaction, and since this could be
+ // applied after the animation transaction, we avoid putting visible changes here.
+ // The initial state of the animation matches the current state of the dim anyway.
+ mStarted = true;
+ return;
+ }
+ final float fraction = getFraction(currentPlayTime);
+ mCurrentAlpha =
+ fraction * (mAlpha.mFinishValue - mAlpha.mStartValue) + mAlpha.mStartValue;
+ mCurrentBlur =
+ (int) fraction * (mBlur.mFinishValue - mBlur.mStartValue) + mBlur.mStartValue;
+ if (sc.isValid()) {
+ t.setAlpha(sc, mCurrentAlpha);
+ t.setBackgroundBlurRadius(sc, mCurrentBlur);
+ } else {
+ Log.w(TAG, "Dimmer#AnimationSpec tried to access " + sc + " after release");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Animation spec: alpha=" + mAlpha + ", blur=" + mBlur;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("from_alpha="); pw.print(mAlpha.mStartValue);
+ pw.print(" to_alpha="); pw.print(mAlpha.mFinishValue);
+ pw.print(prefix); pw.print("from_blur="); pw.print(mBlur.mStartValue);
+ pw.print(" to_blur="); pw.print(mBlur.mFinishValue);
+ pw.print(" duration="); pw.println(mDuration);
+ }
+
+ @Override
+ public void dumpDebugInner(ProtoOutputStream proto) {
+ final long token = proto.start(ALPHA);
+ proto.write(FROM, mAlpha.mStartValue);
+ proto.write(TO, mAlpha.mFinishValue);
+ proto.write(DURATION_MS, mDuration);
+ proto.end(token);
+ }
+ }
+
+ static class AnimationAdapterFactory {
+ public AnimationAdapter get(LocalAnimationAdapter.AnimationSpec alphaAnimationSpec,
+ SurfaceAnimationRunner runner) {
+ return new LocalAnimationAdapter(alphaAnimationSpec, runner);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0c4ca5f..50376fe 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -156,6 +156,8 @@
import static com.android.server.wm.WindowState.EXCLUSION_RIGHT;
import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
+import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELDS;
+import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields;
import static com.android.server.wm.utils.RegionUtils.forEachRectReverse;
import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
import static com.android.window.flags.Flags.explicitRefreshRateHints;
@@ -463,11 +465,20 @@
boolean mDisplayScalingDisabled;
final Display mDisplay;
private final DisplayInfo mDisplayInfo = new DisplayInfo();
+
+ /**
+ * Contains the last DisplayInfo override that was sent to DisplayManager or null if we haven't
+ * set an override yet
+ */
+ @Nullable
+ private DisplayInfo mLastDisplayInfoOverride;
+
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
private final DisplayPolicy mDisplayPolicy;
private final DisplayRotation mDisplayRotation;
@Nullable final DisplayRotationCompatPolicy mDisplayRotationCompatPolicy;
DisplayFrames mDisplayFrames;
+ private final DisplayUpdater mDisplayUpdater;
private boolean mInTouchMode;
@@ -621,7 +632,7 @@
@VisibleForTesting
final DeviceStateController mDeviceStateController;
final Consumer<DeviceStateController.DeviceState> mDeviceStateConsumer;
- private final PhysicalDisplaySwitchTransitionLauncher mDisplaySwitchTransitionLauncher;
+ final PhysicalDisplaySwitchTransitionLauncher mDisplaySwitchTransitionLauncher;
final RemoteDisplayChangeController mRemoteDisplayChangeController;
/** Windows added since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
@@ -1147,6 +1158,7 @@
mWallpaperController.resetLargestDisplay(display);
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
+ mDisplayUpdater = new ImmediateDisplayUpdater(this);
mSystemGestureExclusionLimit = mWmService.mConstants.mSystemGestureExclusionLimitDp
* mDisplayMetrics.densityDpi / DENSITY_DEFAULT;
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
@@ -1636,7 +1648,7 @@
? new Transition.ReadyCondition("displayConfig", this) : null;
if (displayConfig != null) {
mTransitionController.waitFor(displayConfig);
- } else if (mTransitionController.isShellTransitionsEnabled()) {
+ } else if (mTransitionController.isShellTransitionsEnabled() && mLastHasContent) {
Slog.e(TAG, "Display reconfigured outside of a transition: " + this);
}
final boolean configUpdated = updateDisplayOverrideConfigurationLocked();
@@ -2277,8 +2289,7 @@
computeSizeRanges(mDisplayInfo, rotated, dw, dh, mDisplayMetrics.density, outConfig);
- mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
- mDisplayInfo);
+ setDisplayInfoOverride();
if (isDefaultDisplay) {
mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(mDisplayMetrics,
@@ -2290,6 +2301,20 @@
return mDisplayInfo;
}
+ /**
+ * Sets the current DisplayInfo in DisplayContent as an override to DisplayManager
+ */
+ private void setDisplayInfoOverride() {
+ mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
+ mDisplayInfo);
+
+ if (mLastDisplayInfoOverride == null) {
+ mLastDisplayInfoOverride = new DisplayInfo();
+ }
+
+ mLastDisplayInfoOverride.copyFrom(mDisplayInfo);
+ }
+
DisplayCutout calculateDisplayCutoutForRotation(int rotation) {
return mDisplayCutoutCache.getOrCompute(
mIsSizeForced ? mBaseDisplayCutout : mInitialDisplayCutout, rotation)
@@ -2861,12 +2886,15 @@
return orientation;
}
- void updateDisplayInfo() {
+ void updateDisplayInfo(@NonNull DisplayInfo newDisplayInfo) {
// Check if display metrics changed and update base values if needed.
- updateBaseDisplayMetricsIfNeeded();
+ updateBaseDisplayMetricsIfNeeded(newDisplayInfo);
- mDisplay.getDisplayInfo(mDisplayInfo);
- mDisplay.getMetrics(mDisplayMetrics);
+ // Update mDisplayInfo with (newDisplayInfo + mLastDisplayInfoOverride) as
+ // updateBaseDisplayMetricsIfNeeded could have updated mLastDisplayInfoOverride
+ copyDisplayInfoFields(/* out= */ mDisplayInfo, /* base= */ newDisplayInfo,
+ /* override= */ mLastDisplayInfoOverride, /* fields= */ WM_OVERRIDE_FIELDS);
+ mDisplayInfo.getAppMetrics(mDisplayMetrics, mDisplay.getDisplayAdjustments());
onDisplayInfoChanged();
onDisplayChanged(this);
@@ -2952,9 +2980,9 @@
* If display metrics changed, overrides are not set and it's not just a rotation - update base
* values.
*/
- private void updateBaseDisplayMetricsIfNeeded() {
+ private void updateBaseDisplayMetricsIfNeeded(DisplayInfo newDisplayInfo) {
// Get real display metrics without overrides from WM.
- mWmService.mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mDisplayInfo);
+ mDisplayInfo.copyFrom(newDisplayInfo);
final int currentRotation = getRotation();
final int orientation = mDisplayInfo.rotation;
final boolean rotated = (orientation == ROTATION_90 || orientation == ROTATION_270);
@@ -2986,7 +3014,7 @@
// metrics are updated as rotation settings might depend on them
mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this,
/* includeRotationSettings */ false);
- mDisplaySwitchTransitionLauncher.requestDisplaySwitchTransitionIfNeeded(mDisplayId,
+ mDisplayUpdater.onDisplayContentDisplayPropertiesPreChanged(mDisplayId,
mInitialDisplayWidth, mInitialDisplayHeight, newWidth, newHeight);
mDisplayRotation.physicalDisplayChanged();
mDisplayPolicy.physicalDisplayChanged();
@@ -3022,8 +3050,8 @@
if (physicalDisplayChanged) {
mDisplayPolicy.physicalDisplayUpdated();
- mDisplaySwitchTransitionLauncher.onDisplayUpdated(currentRotation, getRotation(),
- getDisplayAreaInfo());
+ mDisplayUpdater.onDisplayContentDisplayPropertiesPostChanged(currentRotation,
+ getRotation(), getDisplayAreaInfo());
}
}
}
@@ -5470,8 +5498,7 @@
mDisplayReady = true;
if (mWmService.mDisplayManagerInternal != null) {
- mWmService.mDisplayManagerInternal
- .setDisplayInfoOverrideFromWindowManager(mDisplayId, getDisplayInfo());
+ setDisplayInfoOverride();
configureDisplayPolicy();
}
@@ -6114,9 +6141,17 @@
return mMetricsLogger;
}
- void onDisplayChanged() {
+ /**
+ * Triggers an update of DisplayInfo from DisplayManager
+ * @param onDisplayChangeApplied callback that is called when the changes are applied
+ */
+ void requestDisplayUpdate(@NonNull Runnable onDisplayChangeApplied) {
+ mDisplayUpdater.updateDisplayInfo(onDisplayChangeApplied);
+ }
+
+ void onDisplayInfoUpdated(@NonNull DisplayInfo newDisplayInfo) {
final int lastDisplayState = mDisplayInfo.state;
- updateDisplayInfo();
+ updateDisplayInfo(newDisplayInfo);
// The window policy is responsible for stopping activities on the default display.
final int displayId = mDisplay.getDisplayId();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 708ee7f..b862d7c 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1737,11 +1737,12 @@
void onOverlayChanged() {
updateCurrentUserResources();
// Update the latest display size, cutout.
- mDisplayContent.updateDisplayInfo();
- onConfigurationChanged();
- if (!CLIENT_TRANSIENT) {
- mSystemGestures.onConfigurationChanged();
- }
+ mDisplayContent.requestDisplayUpdate(() -> {
+ onConfigurationChanged();
+ if (!CLIENT_TRANSIENT) {
+ mSystemGestures.onConfigurationChanged();
+ }
+ });
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayUpdater.java b/services/core/java/com/android/server/wm/DisplayUpdater.java
new file mode 100644
index 0000000..e611177
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayUpdater.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+import android.view.Surface;
+import android.window.DisplayAreaInfo;
+
+/**
+ * Interface for a helper class that manages updates of DisplayInfo coming from DisplayManager
+ */
+interface DisplayUpdater {
+ /**
+ * Reads the latest display parameters from the display manager and returns them in a callback.
+ * If there are pending display updates, it will wait for them to finish first and only then it
+ * will call the callback with the latest display parameters.
+ *
+ * @param callback is called when all pending display updates are finished
+ */
+ void updateDisplayInfo(@NonNull Runnable callback);
+
+ /**
+ * Called when physical display has changed and before DisplayContent has applied new display
+ * properties
+ */
+ default void onDisplayContentDisplayPropertiesPreChanged(int displayId, int initialDisplayWidth,
+ int initialDisplayHeight, int newWidth, int newHeight) {
+ }
+
+ /**
+ * Called after physical display has changed and after DisplayContent applied new display
+ * properties
+ */
+ default void onDisplayContentDisplayPropertiesPostChanged(
+ @Surface.Rotation int previousRotation, @Surface.Rotation int newRotation,
+ @NonNull DisplayAreaInfo newDisplayAreaInfo) {
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ImmediateDisplayUpdater.java b/services/core/java/com/android/server/wm/ImmediateDisplayUpdater.java
new file mode 100644
index 0000000..4af9013
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ImmediateDisplayUpdater.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+import android.view.DisplayInfo;
+import android.window.DisplayAreaInfo;
+
+/**
+ * DisplayUpdater that immediately applies new DisplayInfo properties
+ */
+public class ImmediateDisplayUpdater implements DisplayUpdater {
+
+ private final DisplayContent mDisplayContent;
+ private final DisplayInfo mDisplayInfo = new DisplayInfo();
+
+ public ImmediateDisplayUpdater(@NonNull DisplayContent displayContent) {
+ mDisplayContent = displayContent;
+ mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
+ }
+
+ @Override
+ public void updateDisplayInfo(Runnable callback) {
+ mDisplayContent.mWmService.mDisplayManagerInternal.getNonOverrideDisplayInfo(
+ mDisplayContent.mDisplayId, mDisplayInfo);
+ mDisplayContent.onDisplayInfoUpdated(mDisplayInfo);
+ callback.run();
+ }
+
+ @Override
+ public void onDisplayContentDisplayPropertiesPreChanged(int displayId, int initialDisplayWidth,
+ int initialDisplayHeight, int newWidth, int newHeight) {
+ mDisplayContent.mDisplaySwitchTransitionLauncher.requestDisplaySwitchTransitionIfNeeded(
+ displayId, initialDisplayWidth, initialDisplayHeight, newWidth, newHeight);
+ }
+
+ @Override
+ public void onDisplayContentDisplayPropertiesPostChanged(int previousRotation, int newRotation,
+ DisplayAreaInfo newDisplayAreaInfo) {
+ mDisplayContent.mDisplaySwitchTransitionLauncher.onDisplayUpdated(previousRotation,
+ newRotation,
+ newDisplayAreaInfo);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 0c235ba..522e7d2 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -627,7 +627,7 @@
void refreshSecureSurfaceState() {
forAllWindows((w) -> {
if (w.mHasSurface) {
- w.mWinAnimator.setSecureLocked(w.isSecureLocked());
+ w.setSecureLocked(w.isSecureLocked());
}
}, true /* traverseTopToBottom */);
}
@@ -2171,12 +2171,16 @@
// now, it will take focus briefly which confuses the RecentTasks tracker.
rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
}
-
+ // Temporarily disable focus when reparenting to avoid intermediate focus change
+ // (because the task is on top and the activity is resumed), which could cause the
+ // task to be added in recents task list unexpectedly.
+ rootTask.setFocusable(false);
// There are multiple activities in the task and moving the top activity should
// reveal/leave the other activities in their original task.
// On the other hand, ActivityRecord#onParentChanged takes care of setting the
// up-to-dated root pinned task information on this newly created root task.
r.reparent(rootTask, MAX_VALUE, reason);
+ rootTask.setFocusable(true);
// Ensure the leash of new task is in sync with its current bounds after reparent.
rootTask.maybeApplyLastRecentsAnimationTransaction();
@@ -2716,15 +2720,20 @@
synchronized (mService.mGlobalLock) {
final DisplayContent displayContent = getDisplayContent(displayId);
if (displayContent != null) {
- displayContent.onDisplayChanged();
+ displayContent.requestDisplayUpdate(() -> clearDisplayInfoCaches(displayId));
+ } else {
+ clearDisplayInfoCaches(displayId);
}
- // Drop any cached DisplayInfos associated with this display id - the values are now
- // out of date given this display changed event.
- mWmService.mPossibleDisplayInfoMapper.removePossibleDisplayInfos(displayId);
- updateDisplayImePolicyCache();
}
}
+ private void clearDisplayInfoCaches(int displayId) {
+ // Drop any cached DisplayInfos associated with this display id - the values are now
+ // out of date given this display changed event.
+ mWmService.mPossibleDisplayInfoMapper.removePossibleDisplayInfos(displayId);
+ updateDisplayImePolicyCache();
+ }
+
void updateDisplayImePolicyCache() {
ArrayMap<Integer, Integer> displayImePolicyMap = new ArrayMap<>();
forAllDisplays(dc -> displayImePolicyMap.put(dc.getDisplayId(), dc.getImePolicy()));
diff --git a/services/core/java/com/android/server/wm/SmoothDimmer.java b/services/core/java/com/android/server/wm/SmoothDimmer.java
index 2549bbf..b5d94a2 100644
--- a/services/core/java/com/android/server/wm/SmoothDimmer.java
+++ b/services/core/java/com/android/server/wm/SmoothDimmer.java
@@ -17,265 +17,203 @@
package com.android.server.wm;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DIMMER;
-import static com.android.server.wm.AlphaAnimationSpecProto.DURATION_MS;
-import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
-import static com.android.server.wm.AlphaAnimationSpecProto.TO;
-import static com.android.server.wm.AnimationSpecProto.ALPHA;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.graphics.Rect;
import android.util.Log;
-import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
-import java.io.PrintWriter;
-
class SmoothDimmer extends Dimmer {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "Dimmer" : TAG_WM;
- private static final float EPSILON = 0.0001f;
- // This is in milliseconds.
- private static final int DEFAULT_DIM_ANIM_DURATION = 200;
- DimState mDimState;
- private WindowContainer mLastRequestedDimContainer;
- private final AnimationAdapterFactory mAnimationAdapterFactory;
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "Dimmer" : TAG_WM;
+ DimState mDimState;
+ final DimmerAnimationHelper.AnimationAdapterFactory mAnimationAdapterFactory;
+
+ /**
+ * Controls the dim behaviour
+ */
@VisibleForTesting
class DimState {
- /**
- * The layer where property changes should be invoked on.
- */
- SurfaceControl mDimLayer;
- boolean mDimming;
- boolean mIsVisible;
-
+ /** Related objects */
+ SurfaceControl mDimSurface;
+ final WindowContainer mHostContainer;
+ // The last container to request to dim
+ private WindowContainer mLastRequestedDimContainer;
+ /** Animation */
+ private final DimmerAnimationHelper mAnimationHelper;
+ boolean mSkipAnimation = false;
+ // Determines whether the dim layer should animate before destroying.
+ boolean mAnimateExit = true;
+ /** Surface visibility and bounds */
+ private boolean mIsVisible = false;
// TODO(b/64816140): Remove after confirming dimmer layer always matches its container.
final Rect mDimBounds = new Rect();
- /**
- * Determines whether the dim layer should animate before destroying.
- */
- boolean mAnimateExit = true;
-
- /**
- * Used for Dims not associated with a WindowContainer.
- * See {@link Dimmer#adjustRelativeLayer(WindowContainer, int)} for details on Dim
- * lifecycle.
- */
- boolean mDontReset;
-
- Change mCurrentProperties;
- Change mRequestedProperties;
- private AnimationSpec mAlphaAnimationSpec;
- private AnimationAdapter mLocalAnimationAdapter;
-
- static class Change {
- private float mAlpha = -1f;
- private int mBlurRadius = -1;
- private WindowContainer mDimmingContainer = null;
- private int mRelativeLayer = -1;
- private boolean mSkipAnimation = false;
-
- Change() {}
-
- Change(Change other) {
- mAlpha = other.mAlpha;
- mBlurRadius = other.mBlurRadius;
- mDimmingContainer = other.mDimmingContainer;
- mRelativeLayer = other.mRelativeLayer;
- }
-
- @Override
- public String toString() {
- return "Dim state: alpha=" + mAlpha + ", blur=" + mBlurRadius + ", container="
- + mDimmingContainer + ", relativePosition=" + mRelativeLayer
- + ", skipAnimation=" + mSkipAnimation;
- }
- }
-
- DimState(SurfaceControl dimLayer) {
- mDimLayer = dimLayer;
- mDimming = true;
- mCurrentProperties = new Change();
- mRequestedProperties = new Change();
- }
-
- void setExitParameters(WindowContainer container) {
- setRequestedRelativeParent(container, -1 /* relativeLayer */);
- setRequestedAppearance(0f /* alpha */, 0 /* blur */);
- }
-
- // Sets a requested change without applying it immediately
- void setRequestedRelativeParent(WindowContainer relativeParent, int relativeLayer) {
- mRequestedProperties.mDimmingContainer = relativeParent;
- mRequestedProperties.mRelativeLayer = relativeLayer;
- }
-
- // Sets a requested change without applying it immediately
- void setRequestedAppearance(float alpha, int blurRadius) {
- mRequestedProperties.mAlpha = alpha;
- mRequestedProperties.mBlurRadius = blurRadius;
- }
-
- /**
- * Commit the last changes we received. Called after
- * {@link Change#setExitParameters(WindowContainer)},
- * {@link Change#setRequestedRelativeParent(WindowContainer, int)}, or
- * {@link Change#setRequestedAppearance(float, int)}
- */
- void applyChanges(SurfaceControl.Transaction t) {
- if (mRequestedProperties.mDimmingContainer == null) {
- Log.e(TAG, this + " does not have a dimming container. Have you forgotten to "
- + "call adjustRelativeLayer?");
- return;
- }
- if (mRequestedProperties.mDimmingContainer.mSurfaceControl == null) {
- Log.w(TAG, "container " + mRequestedProperties.mDimmingContainer
- + "does not have a surface");
- return;
- }
- if (!mDimState.mIsVisible) {
- mDimState.mIsVisible = true;
- t.show(mDimState.mDimLayer);
- }
- t.setRelativeLayer(mDimLayer,
- mRequestedProperties.mDimmingContainer.getSurfaceControl(),
- mRequestedProperties.mRelativeLayer);
-
- if (aspectChanged()) {
- if (isAnimating()) {
- mLocalAnimationAdapter.onAnimationCancelled(mDimLayer);
- }
- if (mRequestedProperties.mSkipAnimation
- || (!dimmingContainerChanged() && mDimming)) {
- // If the dimming container has not changed, then it is running its own
- // animation, thus we can directly set the values we get requested, unless it's
- // the exiting animation
- ProtoLog.d(WM_DEBUG_DIMMER,
- "Dim %s skipping animation and directly setting alpha=%f, blur=%d",
- mDimLayer, mRequestedProperties.mAlpha,
- mRequestedProperties.mBlurRadius);
- t.setAlpha(mDimLayer, mRequestedProperties.mAlpha);
- t.setBackgroundBlurRadius(mDimLayer, mRequestedProperties.mBlurRadius);
- mRequestedProperties.mSkipAnimation = false;
- } else {
- startAnimation(t);
- }
- }
- mCurrentProperties = new Change(mRequestedProperties);
- }
-
- private void startAnimation(SurfaceControl.Transaction t) {
- mAlphaAnimationSpec = getRequestedAnimationSpec(mRequestedProperties.mAlpha,
- mRequestedProperties.mBlurRadius);
- mLocalAnimationAdapter = mAnimationAdapterFactory.get(mAlphaAnimationSpec,
- mHost.mWmService.mSurfaceAnimationRunner);
-
- mLocalAnimationAdapter.startAnimation(mDimLayer, t,
- ANIMATION_TYPE_DIMMER, (type, animator) -> {
- t.setAlpha(mDimLayer, mRequestedProperties.mAlpha);
- t.setBackgroundBlurRadius(mDimLayer, mRequestedProperties.mBlurRadius);
- if (mRequestedProperties.mAlpha == 0f && !mDimming) {
- ProtoLog.d(WM_DEBUG_DIMMER,
- "Removing dim surface %s on transaction %s", mDimLayer, t);
- t.remove(mDimLayer);
- }
- mLocalAnimationAdapter = null;
- mAlphaAnimationSpec = null;
- });
- }
-
- private boolean isAnimating() {
- return mAlphaAnimationSpec != null;
- }
-
- private boolean aspectChanged() {
- return Math.abs(mRequestedProperties.mAlpha - mCurrentProperties.mAlpha) > EPSILON
- || mRequestedProperties.mBlurRadius != mCurrentProperties.mBlurRadius;
- }
-
- private boolean dimmingContainerChanged() {
- return mRequestedProperties.mDimmingContainer != mCurrentProperties.mDimmingContainer;
- }
-
- private AnimationSpec getRequestedAnimationSpec(float targetAlpha, int targetBlur) {
- final float startAlpha;
- final int startBlur;
- if (mAlphaAnimationSpec != null) {
- startAlpha = mAlphaAnimationSpec.mCurrentAlpha;
- startBlur = mAlphaAnimationSpec.mCurrentBlur;
- } else {
- startAlpha = Math.max(mCurrentProperties.mAlpha, 0f);
- startBlur = Math.max(mCurrentProperties.mBlurRadius, 0);
- }
- long duration = (long) (getDimDuration(mRequestedProperties.mDimmingContainer)
- * Math.abs(targetAlpha - startAlpha));
-
- ProtoLog.v(WM_DEBUG_DIMMER, "Starting animation on dim layer %s, requested by %s, "
- + "alpha: %f -> %f, blur: %d -> %d",
- mDimLayer, mRequestedProperties.mDimmingContainer, startAlpha, targetAlpha,
- startBlur, targetBlur);
- return new AnimationSpec(
- new AnimationExtremes<>(startAlpha, targetAlpha),
- new AnimationExtremes<>(startBlur, targetBlur),
- duration
- );
- }
- }
-
- protected SmoothDimmer(WindowContainer host) {
- this(host, new AnimationAdapterFactory());
- }
-
- @VisibleForTesting
- SmoothDimmer(WindowContainer host, AnimationAdapterFactory animationFactory) {
- super(host);
- mAnimationAdapterFactory = animationFactory;
- }
-
- private DimState obtainDimState(WindowContainer container) {
- if (mDimState == null) {
+ DimState() {
+ mHostContainer = mHost;
+ mAnimationHelper = new DimmerAnimationHelper(mAnimationAdapterFactory);
try {
- final SurfaceControl ctl = makeDimLayer();
- mDimState = new DimState(ctl);
+ mDimSurface = makeDimLayer();
} catch (Surface.OutOfResourcesException e) {
Log.w(TAG, "OutOfResourcesException creating dim surface");
}
}
- mLastRequestedDimContainer = container;
- return mDimState;
+ void ensureVisible(SurfaceControl.Transaction t) {
+ if (!mIsVisible) {
+ t.show(mDimSurface);
+ t.setAlpha(mDimSurface, 0f);
+ mIsVisible = true;
+ }
+ }
+
+ void adjustSurfaceLayout(SurfaceControl.Transaction t) {
+ // TODO: Once we use geometry from hierarchy this falls away.
+ t.setPosition(mDimSurface, mDimBounds.left, mDimBounds.top);
+ t.setWindowCrop(mDimSurface, mDimBounds.width(), mDimBounds.height());
+ }
+
+ /**
+ * Set the parameters to prepare the dim to change its appearance
+ */
+ void prepareLookChange(float alpha, int blurRadius) {
+ mAnimationHelper.setRequestedAppearance(alpha, blurRadius);
+ }
+
+ /**
+ * Prepare the dim for the exit animation
+ */
+ void exit(SurfaceControl.Transaction t) {
+ if (!mAnimateExit) {
+ remove(t);
+ } else {
+ mAnimationHelper.setExitParameters();
+ setReady(t);
+ }
+ }
+
+ void remove(SurfaceControl.Transaction t) {
+ mAnimationHelper.stopCurrentAnimation(mDimSurface);
+ if (mDimSurface.isValid()) {
+ t.remove(mDimSurface);
+ ProtoLog.d(WM_DEBUG_DIMMER,
+ "Removing dim surface %s on transaction %s", this, t);
+ } else {
+ Log.w(TAG, "Tried to remove " + mDimSurface + " multiple times\n");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SmoothDimmer#DimState with host=" + mHostContainer + ", surface=" + mDimSurface;
+ }
+
+ /**
+ * Set the parameters to prepare the dim to be relative parented to the dimming container
+ */
+ void prepareReparent(WindowContainer relativeParent, int relativeLayer) {
+ mAnimationHelper.setRequestedRelativeParent(relativeParent, relativeLayer);
+ }
+
+ /**
+ * Call when all the changes have been requested to have them applied
+ * @param t The transaction in which to apply the changes
+ */
+ void setReady(SurfaceControl.Transaction t) {
+ mAnimationHelper.applyChanges(t, this);
+ }
+
+ /**
+ * Whether anyone is currently requesting the dim
+ */
+ boolean isDimming() {
+ return mLastRequestedDimContainer != null;
+ }
+
+ private SurfaceControl makeDimLayer() {
+ return mHost.makeChildSurface(null)
+ .setParent(mHost.getSurfaceControl())
+ .setColorLayer()
+ .setName("Dim Layer for - " + mHost.getName())
+ .setCallsite("DimLayer.makeDimLayer")
+ .build();
+ }
}
- private SurfaceControl makeDimLayer() {
- return mHost.makeChildSurface(null)
- .setParent(mHost.getSurfaceControl())
- .setColorLayer()
- .setName("Dim Layer for - " + mHost.getName())
- .setCallsite("Dimmer.makeDimLayer")
- .build();
+ protected SmoothDimmer(WindowContainer host) {
+ this(host, new DimmerAnimationHelper.AnimationAdapterFactory());
}
- @Override
- SurfaceControl getDimLayer() {
- return mDimState != null ? mDimState.mDimLayer : null;
+ @VisibleForTesting
+ SmoothDimmer(WindowContainer host,
+ DimmerAnimationHelper.AnimationAdapterFactory animationFactory) {
+ super(host);
+ mAnimationAdapterFactory = animationFactory;
}
@Override
void resetDimStates() {
+ if (mDimState != null) {
+ mDimState.mLastRequestedDimContainer = null;
+ }
+ }
+
+ @Override
+ protected void adjustAppearance(WindowContainer container, float alpha, int blurRadius) {
+ final DimState d = obtainDimState(container);
+ d.prepareLookChange(alpha, blurRadius);
+ }
+
+ @Override
+ protected void adjustRelativeLayer(WindowContainer container, int relativeLayer) {
+ if (mDimState != null) {
+ mDimState.prepareReparent(container, relativeLayer);
+ }
+ }
+
+ @Override
+ boolean updateDims(SurfaceControl.Transaction t) {
if (mDimState == null) {
- return;
+ return false;
}
- if (!mDimState.mDontReset) {
- mDimState.mDimming = false;
+ if (!mDimState.isDimming()) {
+ // No one is dimming, fade out and remove the dim
+ mDimState.exit(t);
+ mDimState = null;
+ return false;
+ } else {
+ // Someone is dimming, show the requested changes
+ mDimState.adjustSurfaceLayout(t);
+ final WindowState ws = mDimState.mLastRequestedDimContainer.asWindowState();
+ if (!mDimState.mIsVisible && ws != null && ws.mActivityRecord != null
+ && ws.mActivityRecord.mStartingData != null) {
+ // Skip enter animation while starting window is on top of its activity
+ mDimState.mSkipAnimation = true;
+ }
+ mDimState.setReady(t);
+ return true;
}
}
+ private DimState obtainDimState(WindowContainer container) {
+ if (mDimState == null) {
+ mDimState = new DimState();
+ }
+ mDimState.mLastRequestedDimContainer = container;
+ return mDimState;
+ }
+
+ @Override
+ @VisibleForTesting
+ SurfaceControl getDimLayer() {
+ return mDimState != null ? mDimState.mDimSurface : null;
+ }
+
@Override
Rect getDimBounds() {
return mDimState != null ? mDimState.mDimBounds : null;
@@ -287,127 +225,4 @@
mDimState.mAnimateExit = false;
}
}
-
- @Override
- protected void adjustAppearance(WindowContainer container, float alpha, int blurRadius) {
- final DimState d = obtainDimState(container);
- mDimState.setRequestedAppearance(alpha, blurRadius);
- d.mDimming = true;
- }
-
- @Override
- protected void adjustRelativeLayer(WindowContainer container, int relativeLayer) {
- if (mDimState != null) {
- mDimState.setRequestedRelativeParent(container, relativeLayer);
- }
- }
-
- boolean updateDims(SurfaceControl.Transaction t) {
- if (mDimState == null) {
- return false;
- }
-
- if (!mDimState.mDimming) {
- // No one is dimming anymore, fade out dim and remove
- if (!mDimState.mAnimateExit) {
- if (mDimState.mDimLayer.isValid()) {
- t.remove(mDimState.mDimLayer);
- }
- } else {
- mDimState.setExitParameters(
- mDimState.mRequestedProperties.mDimmingContainer);
- mDimState.applyChanges(t);
- }
- mDimState = null;
- return false;
- }
- final Rect bounds = mDimState.mDimBounds;
- // TODO: Once we use geometry from hierarchy this falls away.
- t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
- t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height());
- // Skip enter animation while starting window is on top of its activity
- final WindowState ws = mLastRequestedDimContainer.asWindowState();
- if (!mDimState.mIsVisible && ws != null && ws.mActivityRecord != null
- && ws.mActivityRecord.mStartingData != null) {
- mDimState.mRequestedProperties.mSkipAnimation = true;
- }
- mDimState.applyChanges(t);
- return true;
- }
-
- private long getDimDuration(WindowContainer container) {
- // Use the same duration as the animation on the WindowContainer
- AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
- final float durationScale = container.mWmService.getTransitionAnimationScaleLocked();
- return animationAdapter == null ? (long) (DEFAULT_DIM_ANIM_DURATION * durationScale)
- : animationAdapter.getDurationHint();
- }
-
- private static class AnimationExtremes<T> {
- final T mStartValue;
- final T mFinishValue;
-
- AnimationExtremes(T fromValue, T toValue) {
- mStartValue = fromValue;
- mFinishValue = toValue;
- }
- }
-
- private static class AnimationSpec implements LocalAnimationAdapter.AnimationSpec {
- private final long mDuration;
- private final AnimationExtremes<Float> mAlpha;
- private final AnimationExtremes<Integer> mBlur;
-
- float mCurrentAlpha = 0;
- int mCurrentBlur = 0;
-
- AnimationSpec(AnimationExtremes<Float> alpha,
- AnimationExtremes<Integer> blur, long duration) {
- mAlpha = alpha;
- mBlur = blur;
- mDuration = duration;
- }
-
- @Override
- public long getDuration() {
- return mDuration;
- }
-
- @Override
- public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
- final float fraction = getFraction(currentPlayTime);
- mCurrentAlpha =
- fraction * (mAlpha.mFinishValue - mAlpha.mStartValue) + mAlpha.mStartValue;
- mCurrentBlur =
- (int) fraction * (mBlur.mFinishValue - mBlur.mStartValue) + mBlur.mStartValue;
- t.setAlpha(sc, mCurrentAlpha);
- t.setBackgroundBlurRadius(sc, mCurrentBlur);
- }
-
- @Override
- public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("from_alpha="); pw.print(mAlpha.mStartValue);
- pw.print(" to_alpha="); pw.print(mAlpha.mFinishValue);
- pw.print(prefix); pw.print("from_blur="); pw.print(mBlur.mStartValue);
- pw.print(" to_blur="); pw.print(mBlur.mFinishValue);
- pw.print(" duration="); pw.println(mDuration);
- }
-
- @Override
- public void dumpDebugInner(ProtoOutputStream proto) {
- final long token = proto.start(ALPHA);
- proto.write(FROM, mAlpha.mStartValue);
- proto.write(TO, mAlpha.mFinishValue);
- proto.write(DURATION_MS, mDuration);
- proto.end(token);
- }
- }
-
- static class AnimationAdapterFactory {
-
- public AnimationAdapter get(LocalAnimationAdapter.AnimationSpec alphaAnimationSpec,
- SurfaceAnimationRunner runner) {
- return new LocalAnimationAdapter(alphaAnimationSpec, runner);
- }
- }
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7375512..de802b9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -6177,6 +6177,7 @@
// Avoid resuming activities on secondary displays since we don't want bubble
// activities to be resumed while bubble is still collapsed.
// TODO(b/113840485): Having keyguard going away state for secondary displays.
+ && display != null
&& display.isDefaultDisplay) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index caa57bb..e5604ec 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1737,8 +1737,8 @@
// Since we created root-leash but no longer reference it from core, release it now
info.releaseAnimSurfaces();
- mLogger.logOnSendAsync(mController.mLoggerHandler);
if (mLogger.mInfo != null) {
+ mLogger.logOnSendAsync(mController.mLoggerHandler);
mController.mTransitionTracer.logSentTransition(this, mTargets);
}
}
@@ -1756,6 +1756,27 @@
}
/**
+ * Checks if the transition contains order changes.
+ *
+ * This is a shallow check that doesn't account for collection in parallel, unlike
+ * {@code collectOrderChanges}
+ */
+ boolean hasOrderChanges() {
+ ArrayList<Task> onTopTasks = new ArrayList<>();
+ // Iterate over target displays to get up to date on top tasks.
+ // Cannot use `mOnTopTasksAtReady` as it's not populated before the `applyReady` is called.
+ for (DisplayContent dc : mTargetDisplays) {
+ addOnTopTasks(dc, onTopTasks);
+ }
+ for (Task task : onTopTasks) {
+ if (!mOnTopTasksStart.contains(task)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Collect tasks which moved-to-top as part of this transition. This also updates the
* controller's latest-reported when relevant.
*
@@ -3305,7 +3326,6 @@
*/
void addGroup(WindowContainer wc) {
if (mReadyGroups.containsKey(wc)) {
- Slog.e(TAG, "Trying to add a ready-group twice: " + wc);
return;
}
mReadyGroups.put(wc, false);
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index bacfda5..e648d64 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -792,6 +792,12 @@
mCollectingTransition.recordTaskOrder(wc);
}
+ /** @see Transition#hasOrderChanges */
+ boolean hasOrderChanges() {
+ if (mCollectingTransition == null) return false;
+ return mCollectingTransition.hasOrderChanges();
+ }
+
/**
* Collects the window containers which need to be synced with the changing display area into
* the current collecting transition.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a69a07f..dd2b48b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2327,8 +2327,8 @@
boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
&& win.hasWallpaper();
wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
- if ((flagChanges & FLAG_SECURE) != 0 && winAnimator.mSurfaceController != null) {
- winAnimator.mSurfaceController.setSecure(win.isSecureLocked());
+ if ((flagChanges & FLAG_SECURE) != 0) {
+ win.setSecureLocked(win.isSecureLocked());
}
final boolean wasVisible = win.isVisible();
@@ -6238,9 +6238,11 @@
return;
}
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.doStartFreezingDisplay");
- doStartFreezingDisplay(exitAnim, enterAnim, displayContent, overrideOriginalRotation);
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ displayContent.requestDisplayUpdate(() -> {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.doStartFreezingDisplay");
+ doStartFreezingDisplay(exitAnim, enterAnim, displayContent, overrideOriginalRotation);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ });
}
private void doStartFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent,
@@ -6276,7 +6278,6 @@
mExitAnimId = exitAnim;
mEnterAnimId = enterAnim;
- displayContent.updateDisplayInfo();
final int originalRotation = overrideOriginalRotation != ROTATION_UNDEFINED
? overrideOriginalRotation
: displayContent.getDisplayInfo().rotation;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3e43908..e1f1f66 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -109,6 +109,7 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_INSETS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
@@ -177,6 +178,7 @@
import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
+import static com.android.window.flags.Flags.secureWindowState;
import static com.android.window.flags.Flags.surfaceTrustedOverlay;
import android.annotation.CallSuper;
@@ -1195,6 +1197,9 @@
if (surfaceTrustedOverlay() && isWindowTrustedOverlay()) {
getPendingTransaction().setTrustedOverlay(mSurfaceControl, true);
}
+ if (secureWindowState()) {
+ getPendingTransaction().setSecure(mSurfaceControl, isSecureLocked());
+ }
}
void updateTrustedOverlay() {
@@ -3276,7 +3281,9 @@
// just kill it. And if it is a window of foreground activity, the activity can be
// restarted automatically if needed.
Slog.w(TAG, "Exception thrown during dispatchAppVisibility " + this, e);
- android.os.Process.killProcess(mSession.mPid);
+ if (android.os.Process.getUidForPid(mSession.mPid) == mSession.mUid) {
+ android.os.Process.killProcess(mSession.mPid);
+ }
}
}
@@ -6040,4 +6047,25 @@
// Cancel any draw requests during a sync.
return mPrepareSyncSeqId > 0;
}
+
+ void setSecureLocked(boolean isSecure) {
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isSecure=%b: %s", isSecure, getName());
+ if (secureWindowState()) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+ getPendingTransaction().setSecure(mSurfaceControl, isSecure);
+ } else {
+ if (mWinAnimator.mSurfaceController == null
+ || mWinAnimator.mSurfaceController.mSurfaceControl == null) {
+ return;
+ }
+ getPendingTransaction().setSecure(mWinAnimator.mSurfaceController.mSurfaceControl,
+ isSecure);
+ }
+ if (mDisplayContent != null) {
+ mDisplayContent.refreshImeSecureFlag(getSyncTransaction());
+ }
+ mWmService.scheduleAnimationLocked();
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 3aac816..44cd23d 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -44,6 +44,7 @@
import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE;
import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
+import static com.android.window.flags.Flags.secureWindowState;
import android.content.Context;
import android.graphics.PixelFormat;
@@ -286,8 +287,10 @@
int flags = SurfaceControl.HIDDEN;
final WindowManager.LayoutParams attrs = w.mAttrs;
- if (w.isSecureLocked()) {
- flags |= SurfaceControl.SECURE;
+ if (!secureWindowState()) {
+ if (w.isSecureLocked()) {
+ flags |= SurfaceControl.SECURE;
+ }
}
if ((mWin.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0) {
@@ -488,13 +491,6 @@
mSurfaceController.setOpaque(isOpaque);
}
- void setSecureLocked(boolean isSecure) {
- if (mSurfaceController == null) {
- return;
- }
- mSurfaceController.setSecure(isSecure);
- }
-
void setColorSpaceAgnosticLocked(boolean agnostic) {
if (mSurfaceController == null) {
return;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index d348491..4456a94 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -24,7 +24,6 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
@@ -152,24 +151,6 @@
mService.scheduleAnimationLocked();
}
- void setSecure(boolean isSecure) {
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isSecure=%b: %s", isSecure, title);
-
- if (mSurfaceControl == null) {
- return;
- }
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setSecureLocked");
-
- final SurfaceControl.Transaction t = mAnimator.mWin.getPendingTransaction();
- t.setSecure(mSurfaceControl, isSecure);
-
- final DisplayContent dc = mAnimator.mWin.mDisplayContent;
- if (dc != null) {
- dc.refreshImeSecureFlag(t);
- }
- mService.scheduleAnimationLocked();
- }
-
void setColorSpaceAgnostic(SurfaceControl.Transaction t, boolean agnostic) {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isColorSpaceAgnostic=%b: %s", agnostic, title);
diff --git a/services/core/java/com/android/server/wm/utils/DisplayInfoOverrides.java b/services/core/java/com/android/server/wm/utils/DisplayInfoOverrides.java
new file mode 100644
index 0000000..8c8f6a6
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/DisplayInfoOverrides.java
@@ -0,0 +1,78 @@
+/*
+ * 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 android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.DisplayInfo;
+
+/**
+ * Helper class to copy only subset of fields of DisplayInfo object or to perform
+ * comparison operation between DisplayInfo objects only with a subset of fields.
+ */
+public class DisplayInfoOverrides {
+
+ /**
+ * Set of DisplayInfo fields that are overridden in DisplayManager using values from
+ * WindowManager
+ */
+ public static final DisplayInfoFields WM_OVERRIDE_FIELDS = (out, source) -> {
+ out.appWidth = source.appWidth;
+ out.appHeight = source.appHeight;
+ out.smallestNominalAppWidth = source.smallestNominalAppWidth;
+ out.smallestNominalAppHeight = source.smallestNominalAppHeight;
+ out.largestNominalAppWidth = source.largestNominalAppWidth;
+ out.largestNominalAppHeight = source.largestNominalAppHeight;
+ out.logicalWidth = source.logicalWidth;
+ out.logicalHeight = source.logicalHeight;
+ out.physicalXDpi = source.physicalXDpi;
+ out.physicalYDpi = source.physicalYDpi;
+ out.rotation = source.rotation;
+ out.displayCutout = source.displayCutout;
+ out.logicalDensityDpi = source.logicalDensityDpi;
+ out.roundedCorners = source.roundedCorners;
+ out.displayShape = source.displayShape;
+ };
+
+ /**
+ * Gets {@param base} DisplayInfo, overrides WindowManager-specific overrides using
+ * {@param override} and writes the result to {@param out}
+ */
+ public static void copyDisplayInfoFields(@NonNull DisplayInfo out,
+ @NonNull DisplayInfo base,
+ @Nullable DisplayInfo override,
+ @NonNull DisplayInfoFields fields) {
+ out.copyFrom(base);
+
+ if (override != null) {
+ fields.setFields(out, override);
+ }
+ }
+
+ /**
+ * Callback interface that allows to specify a subset of fields of DisplayInfo object
+ */
+ public interface DisplayInfoFields {
+ /**
+ * Copies a subset of fields from {@param source} to {@param out}
+ *
+ * @param out resulting DisplayInfo object
+ * @param source source DisplayInfo to copy fields from
+ */
+ void setFields(@NonNull DisplayInfo out, @NonNull DisplayInfo source);
+ }
+}
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index cca4261..c625b1e 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -159,6 +159,12 @@
<xs:element type="usiVersion" name="usiVersion">
<xs:annotation name="final"/>
</xs:element>
+ <!-- Maximum screen brightness setting when screen brightness capped in
+ Wear Bedtime mode. This must be a non-negative decimal within the range defined by
+ the first and the last brightness value in screenBrightnessMap. -->
+ <xs:element type="nonNegativeDecimal" name="screenBrightnessCapForWearBedtimeMode">
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
@@ -586,42 +592,39 @@
minOccurs="0" maxOccurs="1">
<xs:annotation name="final"/>
</xs:element>
- <!-- Sets the brightness mapping of the desired screen brightness in nits to the
- corresponding lux for the current display -->
- <xs:element name="displayBrightnessMapping" type="displayBrightnessMapping"
+ <!-- Sets the brightness mapping of the desired screen brightness to the corresponding
+ lux for the current display -->
+ <xs:element name="luxToBrightnessMapping" type="luxToBrightnessMapping"
minOccurs="0" maxOccurs="1">
<xs:annotation name="final"/>
</xs:element>
</xs:sequence>
</xs:complexType>
- <!-- Represents the brightness mapping of the desired screen brightness in nits to the
- corresponding lux for the current display -->
- <xs:complexType name="displayBrightnessMapping">
- <xs:sequence>
- <!-- Sets the list of display brightness points, each representing the desired screen
- brightness in nits to the corresponding lux for the current display
+ <!-- Sets the list of display brightness points, each representing the desired screen brightness
+ in a certain lux environment.
- The N entries of this array define N + 1 control points as follows:
- (1-based arrays)
+ The first value of each point is the lux value and the second value is the brightness value.
- Point 1: (0, nits[1]): currentLux <= 0
- Point 2: (lux[1], nits[2]): 0 < currentLux <= lux[1]
- Point 3: (lux[2], nits[3]): lux[2] < currentLux <= lux[3]
- ...
- Point N+1: (lux[N], nits[N+1]): lux[N] < currentLux
+ The first lux value must be 0.
- The control points must be strictly increasing. Each control point
- corresponds to an entry in the brightness backlight values arrays.
- For example, if currentLux == lux[1] (first element of the levels array)
- then the brightness will be determined by nits[2] (second element
- of the brightness values array).
- -->
- <xs:element name="displayBrightnessPoint" type="displayBrightnessPoint"
- minOccurs="1" maxOccurs="unbounded">
- <xs:annotation name="final"/>
- </xs:element>
- </xs:sequence>
+ The control points must be strictly increasing.
+
+ Example: if currentLux == the second lux value in the mapping then the brightness will be
+ determined by the second brightness value in the mapping. Spline interpolation is used
+ to determine the auto-brightness values for lux levels between these control points.
+
+ The brightness values must be non-negative decimals within the range between the first and
+ the last brightness values in screenBrightnessMap.
+
+ This is used in place of config_autoBrightnessLevels and config_autoBrightnessLcdBacklightValues
+ defined in the config XML resource.
+ -->
+ <xs:complexType name="luxToBrightnessMapping">
+ <xs:element name="map" type="nonNegativeFloatToFloatMap">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:complexType>
<!-- Represents a point in the display brightness mapping, representing the lux level from the
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index f767291..8c8c123 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -7,14 +7,14 @@
method public final java.math.BigInteger getBrighteningLightDebounceMillis();
method public final java.math.BigInteger getDarkeningLightDebounceIdleMillis();
method public final java.math.BigInteger getDarkeningLightDebounceMillis();
- method public final com.android.server.display.config.DisplayBrightnessMapping getDisplayBrightnessMapping();
method public boolean getEnabled();
+ method public final com.android.server.display.config.LuxToBrightnessMapping getLuxToBrightnessMapping();
method public final void setBrighteningLightDebounceIdleMillis(java.math.BigInteger);
method public final void setBrighteningLightDebounceMillis(java.math.BigInteger);
method public final void setDarkeningLightDebounceIdleMillis(java.math.BigInteger);
method public final void setDarkeningLightDebounceMillis(java.math.BigInteger);
- method public final void setDisplayBrightnessMapping(com.android.server.display.config.DisplayBrightnessMapping);
method public void setEnabled(boolean);
+ method public final void setLuxToBrightnessMapping(com.android.server.display.config.LuxToBrightnessMapping);
}
public class BlockingZoneConfig {
@@ -78,11 +78,6 @@
method public java.util.List<com.android.server.display.config.Density> getDensity();
}
- public class DisplayBrightnessMapping {
- ctor public DisplayBrightnessMapping();
- method public final java.util.List<com.android.server.display.config.DisplayBrightnessPoint> getDisplayBrightnessPoint();
- }
-
public class DisplayBrightnessPoint {
ctor public DisplayBrightnessPoint();
method public final java.math.BigInteger getLux();
@@ -110,6 +105,7 @@
method public final com.android.server.display.config.SensorDetails getProxSensor();
method public com.android.server.display.config.DisplayQuirks getQuirks();
method public com.android.server.display.config.RefreshRateConfigs getRefreshRate();
+ method public final java.math.BigDecimal getScreenBrightnessCapForWearBedtimeMode();
method @NonNull public final java.math.BigDecimal getScreenBrightnessDefault();
method @NonNull public final com.android.server.display.config.NitsMap getScreenBrightnessMap();
method public final java.math.BigInteger getScreenBrightnessRampDecreaseMaxIdleMillis();
@@ -143,6 +139,7 @@
method public final void setProxSensor(com.android.server.display.config.SensorDetails);
method public void setQuirks(com.android.server.display.config.DisplayQuirks);
method public void setRefreshRate(com.android.server.display.config.RefreshRateConfigs);
+ method public final void setScreenBrightnessCapForWearBedtimeMode(java.math.BigDecimal);
method public final void setScreenBrightnessDefault(@NonNull java.math.BigDecimal);
method public final void setScreenBrightnessMap(@NonNull com.android.server.display.config.NitsMap);
method public final void setScreenBrightnessRampDecreaseMaxIdleMillis(java.math.BigInteger);
@@ -220,6 +217,12 @@
method @NonNull public final java.util.List<com.android.server.display.config.BrightnessLimitMap> getBrightnessLimitMap();
}
+ public class LuxToBrightnessMapping {
+ ctor public LuxToBrightnessMapping();
+ method @NonNull public final com.android.server.display.config.NonNegativeFloatToFloatMap getMap();
+ method public final void setMap(@NonNull com.android.server.display.config.NonNegativeFloatToFloatMap);
+ }
+
public class NitsMap {
ctor public NitsMap();
method public String getInterpolation();
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index 1988bb6..da44aac 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -23,12 +23,14 @@
import android.content.Context;
import android.content.Intent;
import android.credentials.CredentialProviderInfo;
+import android.credentials.flags.Flags;
import android.credentials.ui.ProviderData;
import android.credentials.ui.UserSelectionDialogResult;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IInterface;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -94,6 +96,9 @@
private final Set<ComponentName> mEnabledProviders;
+ private final RequestSessionDeathRecipient mDeathRecipient =
+ new RequestSessionDeathRecipient();
+
protected PendingIntent mPendingIntent;
@NonNull
@@ -141,11 +146,26 @@
mRequestSessionMetric.collectInitialPhaseMetricInfo(timestampStarted,
mCallingUid, ApiName.getMetricCodeFromRequestInfo(mRequestType));
setCancellationListener();
+ if (Flags.clearSessionEnabled()) {
+ setUpClientCallbackListener();
+ }
+ }
+
+ private void setUpClientCallbackListener() {
+ if (mClientCallback != null && mClientCallback instanceof IInterface) {
+ IInterface callback = (IInterface) mClientCallback;
+ try {
+ callback.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, e.getMessage());
+ }
+ }
}
private void setCancellationListener() {
mCancellationSignal.setOnCancelListener(
() -> {
+ Slog.d(TAG, "Cancellation invoked from the client - clearing session");
boolean isUiActive = maybeCancelUi();
finishSession(!isUiActive);
}
@@ -168,6 +188,17 @@
return false;
}
+ private boolean isUiWaitingForData() {
+ // Technically, the status can also be IN_PROGRESS when the user has made a selection
+ // so this an over estimation, but safe to do so as it is used for cancellation
+ // propagation to the provider in a very narrow time frame. If provider has
+ // already responded, cancellation is not an issue as the cancellation listener
+ // is independent of the service binding.
+ // TODO(b/313512500): Do not propagate cancelation if provider has responded in
+ // query phase.
+ return mCredentialManagerUi.getStatus() == CredentialManagerUi.UiStatus.IN_PROGRESS;
+ }
+
public abstract ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo,
RemoteCredentialService remoteCredentialService);
@@ -373,4 +404,12 @@
return chosenProviderSession != null && chosenProviderSession.mProviderInfo != null
&& chosenProviderSession.mProviderInfo.isPrimary();
}
+
+ private class RequestSessionDeathRecipient implements IBinder.DeathRecipient {
+ @Override
+ public void binderDied() {
+ Slog.d(TAG, "Client binder died - clearing session");
+ finishSession(isUiWaitingForData());
+ }
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9b62a2c..e0a2f30 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -18182,7 +18182,8 @@
private static boolean hasAccountFeatures(AccountManager am, Account account,
String[] features) {
try {
- return am.hasFeatures(account, features, null, null).getResult();
+ return am.hasFeatures(account, features, null, null)
+ .getResult(30, TimeUnit.SECONDS);
} catch (Exception e) {
Slogf.w(LOG_TAG, "Failed to get account feature", e);
return false;
diff --git a/services/foldables/devicestateprovider/Android.bp b/services/foldables/devicestateprovider/Android.bp
index 34737ef..56daea7 100644
--- a/services/foldables/devicestateprovider/Android.bp
+++ b/services/foldables/devicestateprovider/Android.bp
@@ -5,9 +5,12 @@
java_library {
name: "foldable-device-state-provider",
srcs: [
- "src/**/*.java"
+ "src/**/*.java",
],
libs: [
"services",
],
+ static_libs: [
+ "device_state_flags_lib",
+ ],
}
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
index aea46d1..4c487a7 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
@@ -21,6 +21,7 @@
import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.TYPE_EXTERNAL;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -33,11 +34,14 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
-import android.os.PowerManager;
import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.view.Display;
import com.android.internal.annotations.GuardedBy;
@@ -45,24 +49,26 @@
import com.android.internal.util.Preconditions;
import com.android.server.devicestate.DeviceState;
import com.android.server.devicestate.DeviceStateProvider;
+import com.android.server.policy.feature.flags.FeatureFlags;
+import com.android.server.policy.feature.flags.FeatureFlagsImpl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.function.BooleanSupplier;
-import java.util.function.Function;
+import java.util.function.Predicate;
/**
* Device state provider for foldable devices.
- *
+ * <p>
* It is an implementation of {@link DeviceStateProvider} tailored specifically for
* foldable devices and allows simple callback-based configuration with hall sensor
* and hinge angle sensor values.
*/
public final class FoldableDeviceStateProvider implements DeviceStateProvider,
SensorEventListener, PowerManager.OnThermalStatusChangedListener,
- DisplayManager.DisplayListener {
+ DisplayManager.DisplayListener {
private static final String TAG = "FoldableDeviceStateProvider";
private static final boolean DEBUG = false;
@@ -77,9 +83,17 @@
// are met for the device to be in the state.
private final SparseArray<BooleanSupplier> mStateConditions = new SparseArray<>();
+ // Map of state identifier to a boolean supplier that returns true when the device state has all
+ // the conditions needed for availability.
+ private final SparseArray<BooleanSupplier> mStateAvailabilityConditions = new SparseArray<>();
+
+ @GuardedBy("mLock")
+ private final SparseBooleanArray mExternalDisplaysConnected = new SparseBooleanArray();
+
private final Sensor mHingeAngleSensor;
private final DisplayManager mDisplayManager;
private final Sensor mHallSensor;
+ private static final Predicate<FoldableDeviceStateProvider> ALLOWED = p -> true;
@Nullable
@GuardedBy("mLock")
@@ -99,7 +113,23 @@
@GuardedBy("mLock")
private boolean mPowerSaveModeEnabled;
- public FoldableDeviceStateProvider(@NonNull Context context,
+ private final boolean mIsDualDisplayBlockingEnabled;
+
+ public FoldableDeviceStateProvider(
+ @NonNull Context context,
+ @NonNull SensorManager sensorManager,
+ @NonNull Sensor hingeAngleSensor,
+ @NonNull Sensor hallSensor,
+ @NonNull DisplayManager displayManager,
+ @NonNull DeviceStateConfiguration[] deviceStateConfigurations) {
+ this(new FeatureFlagsImpl(), context, sensorManager, hingeAngleSensor, hallSensor,
+ displayManager, deviceStateConfigurations);
+ }
+
+ @VisibleForTesting
+ public FoldableDeviceStateProvider(
+ @NonNull FeatureFlags featureFlags,
+ @NonNull Context context,
@NonNull SensorManager sensorManager,
@NonNull Sensor hingeAngleSensor,
@NonNull Sensor hallSensor,
@@ -112,6 +142,7 @@
mHingeAngleSensor = hingeAngleSensor;
mHallSensor = hallSensor;
mDisplayManager = displayManager;
+ mIsDualDisplayBlockingEnabled = featureFlags.enableDualDisplayBlocking();
sensorManager.registerListener(this, mHingeAngleSensor, SENSOR_DELAY_FASTEST);
sensorManager.registerListener(this, mHallSensor, SENSOR_DELAY_FASTEST);
@@ -121,20 +152,15 @@
final DeviceStateConfiguration configuration = deviceStateConfigurations[i];
mOrderedStates[i] = configuration.mDeviceState;
- if (mStateConditions.get(configuration.mDeviceState.getIdentifier()) != null) {
- throw new IllegalArgumentException("Device state configurations must have unique"
- + " device state identifiers, found duplicated identifier: " +
- configuration.mDeviceState.getIdentifier());
- }
-
- mStateConditions.put(configuration.mDeviceState.getIdentifier(), () ->
- configuration.mPredicate.apply(this));
+ assertUniqueDeviceStateIdentifier(configuration);
+ initialiseStateConditions(configuration);
+ initialiseStateAvailabilityConditions(configuration);
}
+ Handler handler = new Handler(Looper.getMainLooper());
mDisplayManager.registerDisplayListener(
/* listener = */ this,
- /* handler= */ null,
- /* eventsMask= */ DisplayManager.EVENT_FLAG_DISPLAY_CHANGED);
+ /* handler= */ handler);
Arrays.sort(mOrderedStates, Comparator.comparingInt(DeviceState::getIdentifier));
@@ -167,6 +193,24 @@
}
}
+ private void assertUniqueDeviceStateIdentifier(DeviceStateConfiguration configuration) {
+ if (mStateConditions.get(configuration.mDeviceState.getIdentifier()) != null) {
+ throw new IllegalArgumentException("Device state configurations must have unique"
+ + " device state identifiers, found duplicated identifier: "
+ + configuration.mDeviceState.getIdentifier());
+ }
+ }
+
+ private void initialiseStateConditions(DeviceStateConfiguration configuration) {
+ mStateConditions.put(configuration.mDeviceState.getIdentifier(), () ->
+ configuration.mActiveStatePredicate.test(this));
+ }
+
+ private void initialiseStateAvailabilityConditions(DeviceStateConfiguration configuration) {
+ mStateAvailabilityConditions.put(configuration.mDeviceState.getIdentifier(), () ->
+ configuration.mAvailabilityPredicate.test(this));
+ }
+
@Override
public void setListener(Listener listener) {
synchronized (mLock) {
@@ -189,16 +233,9 @@
}
listener = mListener;
for (DeviceState deviceState : mOrderedStates) {
- if (isThermalStatusCriticalOrAbove(mThermalStatus)
- && deviceState.hasFlag(
- DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL)) {
- continue;
+ if (isStateSupported(deviceState)) {
+ supportedStates.add(deviceState);
}
- if (mPowerSaveModeEnabled && deviceState.hasFlag(
- DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)) {
- continue;
- }
- supportedStates.add(deviceState);
}
}
@@ -206,6 +243,26 @@
supportedStates.toArray(new DeviceState[supportedStates.size()]), reason);
}
+ @GuardedBy("mLock")
+ private boolean isStateSupported(DeviceState deviceState) {
+ if (isThermalStatusCriticalOrAbove(mThermalStatus)
+ && deviceState.hasFlag(
+ DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL)) {
+ return false;
+ }
+ if (mPowerSaveModeEnabled && deviceState.hasFlag(
+ DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)) {
+ return false;
+ }
+ if (mIsDualDisplayBlockingEnabled
+ && mStateAvailabilityConditions.contains(deviceState.getIdentifier())) {
+ return mStateAvailabilityConditions
+ .get(deviceState.getIdentifier())
+ .getAsBoolean();
+ }
+ return true;
+ }
+
/** Computes the current device state and notifies the listener of a change, if needed. */
void notifyDeviceStateChangedIfNeeded() {
int stateToReport = INVALID_DEVICE_STATE;
@@ -294,7 +351,7 @@
private void dumpSensorValues() {
Slog.i(TAG, "Sensor values:");
dumpSensorValues("Hall Sensor", mHallSensor, mLastHallSensorEvent);
- dumpSensorValues("Hinge Angle Sensor",mHingeAngleSensor, mLastHingeAngleSensorEvent);
+ dumpSensorValues("Hinge Angle Sensor", mHingeAngleSensor, mLastHingeAngleSensorEvent);
Slog.i(TAG, "isScreenOn: " + isScreenOn());
}
@@ -307,12 +364,35 @@
@Override
public void onDisplayAdded(int displayId) {
+ // TODO(b/312397262): consider virtual displays cases
+ synchronized (mLock) {
+ if (mIsDualDisplayBlockingEnabled
+ && !mExternalDisplaysConnected.get(displayId, false)
+ && mDisplayManager.getDisplay(displayId).getType() == TYPE_EXTERNAL) {
+ mExternalDisplaysConnected.put(displayId, true);
+ // Only update the supported state when going from 0 external display to 1
+ if (mExternalDisplaysConnected.size() == 1) {
+ notifySupportedStatesChanged(
+ SUPPORTED_DEVICE_STATES_CHANGED_EXTERNAL_DISPLAY_ADDED);
+ }
+ }
+ }
}
@Override
public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ if (mIsDualDisplayBlockingEnabled && mExternalDisplaysConnected.get(displayId, false)) {
+ mExternalDisplaysConnected.delete(displayId);
+ // Only update the supported states when going from 1 external display to 0
+ if (mExternalDisplaysConnected.size() == 0) {
+ notifySupportedStatesChanged(
+ SUPPORTED_DEVICE_STATES_CHANGED_EXTERNAL_DISPLAY_REMOVED);
+ }
+ }
+ }
}
@Override
@@ -338,48 +418,71 @@
*/
public static class DeviceStateConfiguration {
private final DeviceState mDeviceState;
- private final Function<FoldableDeviceStateProvider, Boolean> mPredicate;
+ private final Predicate<FoldableDeviceStateProvider> mActiveStatePredicate;
+ private final Predicate<FoldableDeviceStateProvider> mAvailabilityPredicate;
- private DeviceStateConfiguration(DeviceState deviceState,
- Function<FoldableDeviceStateProvider, Boolean> predicate) {
+ private DeviceStateConfiguration(
+ @NonNull DeviceState deviceState,
+ @NonNull Predicate<FoldableDeviceStateProvider> predicate) {
+ this(deviceState, predicate, ALLOWED);
+ }
+
+ private DeviceStateConfiguration(
+ @NonNull DeviceState deviceState,
+ @NonNull Predicate<FoldableDeviceStateProvider> activeStatePredicate,
+ @NonNull Predicate<FoldableDeviceStateProvider> availabilityPredicate) {
+
mDeviceState = deviceState;
- mPredicate = predicate;
+ mActiveStatePredicate = activeStatePredicate;
+ mAvailabilityPredicate = availabilityPredicate;
}
public static DeviceStateConfiguration createConfig(
@IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
@NonNull String name,
@DeviceState.DeviceStateFlags int flags,
- Function<FoldableDeviceStateProvider, Boolean> predicate
+ @NonNull Predicate<FoldableDeviceStateProvider> activeStatePredicate
) {
return new DeviceStateConfiguration(new DeviceState(identifier, name, flags),
- predicate);
+ activeStatePredicate);
}
public static DeviceStateConfiguration createConfig(
@IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
@NonNull String name,
- Function<FoldableDeviceStateProvider, Boolean> predicate
+ @NonNull Predicate<FoldableDeviceStateProvider> activeStatePredicate
) {
return new DeviceStateConfiguration(new DeviceState(identifier, name, /* flags= */ 0),
- predicate);
+ activeStatePredicate);
+ }
+
+ /** Create a configuration with availability predicate **/
+ public static DeviceStateConfiguration createConfig(
+ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @NonNull String name,
+ @DeviceState.DeviceStateFlags int flags,
+ @NonNull Predicate<FoldableDeviceStateProvider> activeStatePredicate,
+ @NonNull Predicate<FoldableDeviceStateProvider> availabilityPredicate
+ ) {
+ return new DeviceStateConfiguration(new DeviceState(identifier, name, flags),
+ activeStatePredicate, availabilityPredicate);
}
/**
* Creates a device state configuration for a closed tent-mode aware state.
- *
+ * <p>
* During tent mode:
* - The inner display is OFF
* - The outer display is ON
* - The device is partially unfolded (left and right edges could be on the table)
* In this mode the device the device so it could be used in a posture where both left
* and right edges of the unfolded device are on the table.
- *
+ * <p>
* The predicate returns false after the hinge angle reaches
* {@code tentModeSwitchAngleDegrees}. Then it switches back only when the hinge angle
* becomes less than {@code maxClosedAngleDegrees}. Hinge angle is 0 degrees when the device
* is fully closed and 180 degrees when it is fully unfolded.
- *
+ * <p>
* For example, when tentModeSwitchAngleDegrees = 90 and maxClosedAngleDegrees = 5 degrees:
* - when unfolding the device from fully closed posture (last state == closed or it is
* undefined yet) this state will become not matching after reaching the angle
@@ -435,6 +538,15 @@
}
/**
+ * @return Whether there is an external connected display.
+ */
+ public boolean hasNoConnectedExternalDisplay() {
+ synchronized (mLock) {
+ return mExternalDisplaysConnected.size() == 0;
+ }
+ }
+
+ /**
* @return Whether the screen is on.
*/
public boolean isScreenOn() {
@@ -442,6 +554,7 @@
return mIsScreenOn;
}
}
+
/**
* @return current hinge angle value of a foldable device
*/
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/TentModeDeviceStatePolicy.java b/services/foldables/devicestateprovider/src/com/android/server/policy/TentModeDeviceStatePolicy.java
index 5f2cf3c..5968b63 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/TentModeDeviceStatePolicy.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/TentModeDeviceStatePolicy.java
@@ -33,6 +33,10 @@
import com.android.server.devicestate.DeviceStatePolicy;
import com.android.server.devicestate.DeviceStateProvider;
import com.android.server.policy.FoldableDeviceStateProvider.DeviceStateConfiguration;
+import com.android.server.policy.feature.flags.FeatureFlags;
+import com.android.server.policy.feature.flags.FeatureFlagsImpl;
+
+import java.util.function.Predicate;
/**
* Device state policy for a foldable device that supports tent mode: a mode when the device
@@ -55,6 +59,10 @@
private final DeviceStateProvider mProvider;
+ private final boolean mIsDualDisplayBlockingEnabled;
+ private static final Predicate<FoldableDeviceStateProvider> ALLOWED = p -> true;
+ private static final Predicate<FoldableDeviceStateProvider> NOT_ALLOWED = p -> false;
+
/**
* Creates TentModeDeviceStatePolicy
*
@@ -67,6 +75,12 @@
*/
public TentModeDeviceStatePolicy(@NonNull Context context,
@NonNull Sensor hingeAngleSensor, @NonNull Sensor hallSensor, int closeAngleDegrees) {
+ this(new FeatureFlagsImpl(), context, hingeAngleSensor, hallSensor, closeAngleDegrees);
+ }
+
+ public TentModeDeviceStatePolicy(@NonNull FeatureFlags featureFlags, @NonNull Context context,
+ @NonNull Sensor hingeAngleSensor, @NonNull Sensor hallSensor,
+ int closeAngleDegrees) {
super(context);
final SensorManager sensorManager = mContext.getSystemService(SensorManager.class);
@@ -74,8 +88,10 @@
final DeviceStateConfiguration[] configuration = createConfiguration(closeAngleDegrees);
- mProvider = new FoldableDeviceStateProvider(mContext, sensorManager, hingeAngleSensor,
- hallSensor, displayManager, configuration);
+ mIsDualDisplayBlockingEnabled = featureFlags.enableDualDisplayBlocking();
+
+ mProvider = new FoldableDeviceStateProvider(mContext, sensorManager,
+ hingeAngleSensor, hallSensor, displayManager, configuration);
}
private DeviceStateConfiguration[] createConfiguration(int closeAngleDegrees) {
@@ -83,24 +99,27 @@
createClosedConfiguration(closeAngleDegrees),
createConfig(DEVICE_STATE_HALF_OPENED,
/* name= */ "HALF_OPENED",
- (provider) -> {
+ /* activeStatePredicate= */ (provider) -> {
final float hingeAngle = provider.getHingeAngle();
return hingeAngle >= MAX_CLOSED_ANGLE_DEGREES
&& hingeAngle <= TABLE_TOP_MODE_SWITCH_ANGLE_DEGREES;
}),
createConfig(DEVICE_STATE_OPENED,
/* name= */ "OPENED",
- (provider) -> true),
+ /* activeStatePredicate= */ ALLOWED),
createConfig(DEVICE_STATE_REAR_DISPLAY_STATE,
/* name= */ "REAR_DISPLAY_STATE",
/* flags= */ FLAG_EMULATED_ONLY,
- (provider) -> false),
+ /* activeStatePredicate= */ NOT_ALLOWED),
createConfig(DEVICE_STATE_CONCURRENT_INNER_DEFAULT,
/* name= */ "CONCURRENT_INNER_DEFAULT",
/* flags= */ FLAG_EMULATED_ONLY | FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP
| FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
| FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE,
- (provider) -> false)
+ /* activeStatePredicate= */ NOT_ALLOWED,
+ /* availabilityPredicate= */
+ provider -> !mIsDualDisplayBlockingEnabled
+ || provider.hasNoConnectedExternalDisplay())
};
}
@@ -111,7 +130,7 @@
DEVICE_STATE_CLOSED,
/* name= */ "CLOSED",
/* flags= */ FLAG_CANCEL_OVERRIDE_REQUESTS,
- (provider) -> {
+ /* activeStatePredicate= */ (provider) -> {
final float hingeAngle = provider.getHingeAngle();
return hingeAngle <= closeAngleDegrees;
}
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp
new file mode 100644
index 0000000..6ad8d79
--- /dev/null
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp
@@ -0,0 +1,12 @@
+aconfig_declarations {
+ name: "device_state_flags",
+ package: "com.android.server.policy.feature.flags",
+ srcs: [
+ "device_state_flags.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "device_state_flags_lib",
+ aconfig_declarations: "device_state_flags",
+}
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
new file mode 100644
index 0000000..47c2a1b
--- /dev/null
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.policy.feature.flags"
+
+flag {
+ name: "enable_dual_display_blocking"
+ namespace: "display_manager"
+ description: "Feature flag for dual display blocking"
+ bug: "278667199"
+}
\ No newline at end of file
diff --git a/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java
index 8fa4ce5..ddf4a08 100644
--- a/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java
+++ b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java
@@ -17,18 +17,21 @@
package com.android.server.policy;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.STATE_OFF;
+import static android.view.Display.STATE_ON;
+import static android.view.Display.TYPE_EXTERNAL;
+import static android.view.Display.TYPE_INTERNAL;
+
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_EXTERNAL_DISPLAY_ADDED;
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_EXTERNAL_DISPLAY_REMOVED;
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED;
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED;
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED;
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL;
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL;
-import com.android.server.policy.FoldableDeviceStateProvider.DeviceStateConfiguration;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.STATE_OFF;
-import static android.view.Display.STATE_ON;
-
import static com.android.server.policy.FoldableDeviceStateProvider.DeviceStateConfiguration.createConfig;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
@@ -36,12 +39,11 @@
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.nullable;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -51,20 +53,21 @@
import android.hardware.SensorManager;
import android.hardware.display.DisplayManager;
import android.hardware.input.InputSensorInfo;
-import android.os.PowerManager;
import android.os.Handler;
+import android.os.PowerManager;
import android.testing.AndroidTestingRunner;
import android.view.Display;
import com.android.server.devicestate.DeviceState;
-import com.android.server.devicestate.DeviceStateProvider;
import com.android.server.devicestate.DeviceStateProvider.Listener;
+import com.android.server.policy.FoldableDeviceStateProvider.DeviceStateConfiguration;
+import com.android.server.policy.feature.flags.FakeFeatureFlagsImpl;
+import com.android.server.policy.feature.flags.Flags;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -95,10 +98,16 @@
@Mock
private DisplayManager mDisplayManager;
private FoldableDeviceStateProvider mProvider;
+ @Mock
+ private Display mDefaultDisplay;
+ @Mock
+ private Display mExternalDisplay;
+ private final FakeFeatureFlagsImpl mFakeFeatureFlags = new FakeFeatureFlagsImpl();
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ mFakeFeatureFlags.setFlag(Flags.FLAG_ENABLE_DUAL_DISPLAY_BLOCKING, true);
mHallSensor = new Sensor(mInputSensorInfo);
mHingeAngleSensor = new Sensor(mInputSensorInfo);
@@ -473,6 +482,133 @@
assertThat(mProvider.isScreenOn()).isFalse();
}
+ @Test
+ public void test_dualScreenDisabledWhenExternalScreenIsConnected() throws Exception {
+ when(mDisplayManager.getDisplays()).thenReturn(new Display[]{mDefaultDisplay});
+ when(mDefaultDisplay.getType()).thenReturn(TYPE_INTERNAL);
+
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "CLOSED",
+ (c) -> c.getHingeAngle() < 5f),
+ createConfig(/* identifier= */ 2, /* name= */ "HALF_OPENED",
+ (c) -> c.getHingeAngle() < 90f),
+ createConfig(/* identifier= */ 3, /* name= */ "OPENED",
+ (c) -> c.getHingeAngle() < 180f),
+ createConfig(/* identifier= */ 4, /* name= */ "DUAL_DISPLAY", /* flags */ 0,
+ (c) -> false, FoldableDeviceStateProvider::hasNoConnectedExternalDisplay));
+
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ assertThat(mDeviceStateArrayCaptor.getValue()).asList().containsExactly(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */),
+ new DeviceState(4, "DUAL_DISPLAY", 0 /* flags */)}).inOrder();
+
+ clearInvocations(listener);
+
+ when(mDisplayManager.getDisplays())
+ .thenReturn(new Display[]{mDefaultDisplay, mExternalDisplay});
+ when(mDisplayManager.getDisplay(1)).thenReturn(mExternalDisplay);
+ when(mExternalDisplay.getType()).thenReturn(TYPE_EXTERNAL);
+
+ // The DUAL_DISPLAY state should be disabled.
+ mProvider.onDisplayAdded(1);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_EXTERNAL_DISPLAY_ADDED));
+ assertThat(mDeviceStateArrayCaptor.getValue()).asList().containsExactly(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */)}).inOrder();
+ clearInvocations(listener);
+
+ // The DUAL_DISPLAY state should be re-enabled.
+ when(mDisplayManager.getDisplays()).thenReturn(new Display[]{mDefaultDisplay});
+ mProvider.onDisplayRemoved(1);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_EXTERNAL_DISPLAY_REMOVED));
+ assertThat(mDeviceStateArrayCaptor.getValue()).asList().containsExactly(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */),
+ new DeviceState(4, "DUAL_DISPLAY", 0 /* flags */)}).inOrder();
+ }
+
+ @Test
+ public void test_notifySupportedStatesChangedCalledOnlyOnInitialExternalScreenAddition() {
+ when(mDisplayManager.getDisplays()).thenReturn(new Display[]{mDefaultDisplay});
+ when(mDefaultDisplay.getType()).thenReturn(TYPE_INTERNAL);
+
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "CLOSED",
+ (c) -> c.getHingeAngle() < 5f),
+ createConfig(/* identifier= */ 2, /* name= */ "HALF_OPENED",
+ (c) -> c.getHingeAngle() < 90f),
+ createConfig(/* identifier= */ 3, /* name= */ "OPENED",
+ (c) -> c.getHingeAngle() < 180f),
+ createConfig(/* identifier= */ 4, /* name= */ "DUAL_DISPLAY", /* flags */ 0,
+ (c) -> false, FoldableDeviceStateProvider::hasNoConnectedExternalDisplay));
+
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ assertThat(mDeviceStateArrayCaptor.getValue()).asList().containsExactly(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */),
+ new DeviceState(4, "DUAL_DISPLAY", 0 /* flags */)}).inOrder();
+
+ clearInvocations(listener);
+
+ addExternalDisplay(1);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_EXTERNAL_DISPLAY_ADDED));
+ addExternalDisplay(2);
+ addExternalDisplay(3);
+ addExternalDisplay(4);
+ verify(listener, times(1))
+ .onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_EXTERNAL_DISPLAY_ADDED));
+ }
+
+ @Test
+ public void hasNoConnectedDisplay_afterExternalDisplayAdded_returnsFalse() {
+ createProvider(
+ createConfig(
+ /* identifier= */ 1, /* name= */ "ONE",
+ /* flags= */0, (c) -> true,
+ FoldableDeviceStateProvider::hasNoConnectedExternalDisplay)
+ );
+
+ addExternalDisplay(/* displayId */ 1);
+
+ assertThat(mProvider.hasNoConnectedExternalDisplay()).isFalse();
+ }
+
+ @Test
+ public void hasNoConnectedDisplay_afterExternalDisplayAddedAndRemoved_returnsTrue() {
+ createProvider(
+ createConfig(
+ /* identifier= */ 1, /* name= */ "ONE",
+ /* flags= */0, (c) -> true,
+ FoldableDeviceStateProvider::hasNoConnectedExternalDisplay)
+ );
+
+ addExternalDisplay(/* displayId */ 1);
+ mProvider.onDisplayRemoved(1);
+
+ assertThat(mProvider.hasNoConnectedExternalDisplay()).isTrue();
+ }
+ private void addExternalDisplay(int displayId) {
+ when(mDisplayManager.getDisplay(displayId)).thenReturn(mExternalDisplay);
+ when(mExternalDisplay.getType()).thenReturn(TYPE_EXTERNAL);
+ mProvider.onDisplayAdded(displayId);
+ }
private void setScreenOn(boolean isOn) {
Display mockDisplay = mock(Display.class);
int state = isOn ? STATE_ON : STATE_OFF;
@@ -508,12 +644,11 @@
}
private void createProvider(DeviceStateConfiguration... configurations) {
- mProvider = new FoldableDeviceStateProvider(mContext, mSensorManager, mHingeAngleSensor,
- mHallSensor, mDisplayManager, configurations);
+ mProvider = new FoldableDeviceStateProvider(mFakeFeatureFlags, mContext, mSensorManager,
+ mHingeAngleSensor, mHallSensor, mDisplayManager, configurations);
verify(mDisplayManager)
.registerDisplayListener(
mDisplayListenerCaptor.capture(),
- nullable(Handler.class),
- anyLong());
+ nullable(Handler.class));
}
}
diff --git a/services/permission/OWNERS b/services/permission/OWNERS
index e464038..487c992 100644
--- a/services/permission/OWNERS
+++ b/services/permission/OWNERS
@@ -1,5 +1,3 @@
#Bug component: 137825
-joecastro@google.com
-ntmyren@google.com
-zhanghai@google.com
+include platform/frameworks/base:/core/java/android/permission/OWNERS
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
index f94a0d6..8f464d4 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
@@ -71,7 +71,7 @@
// Not implemented because upgrades are handled automatically.
}
- override fun getNonDefaultUidModes(uid: Int): SparseIntArray {
+ override fun getNonDefaultUidModes(uid: Int, persistentDeviceId: String): SparseIntArray {
return opNameMapToOpSparseArray(getUidModes(uid))
}
@@ -79,7 +79,7 @@
return opNameMapToOpSparseArray(getPackageModes(packageName, userId))
}
- override fun getUidMode(uid: Int, op: Int): Int {
+ override fun getUidMode(uid: Int, persistentDeviceId: String, op: Int): Int {
val appId = UserHandle.getAppId(uid)
val userId = UserHandle.getUserId(uid)
val opName = AppOpsManager.opToPublicName(op)
@@ -92,7 +92,7 @@
return service.getState { with(appIdPolicy) { getAppOpModes(appId, userId) } }?.map
}
- override fun setUidMode(uid: Int, op: Int, mode: Int): Boolean {
+ override fun setUidMode(uid: Int, persistentDeviceId: String, op: Int, mode: Int): Boolean {
val appId = UserHandle.getAppId(uid)
val userId = UserHandle.getUserId(uid)
val opName = AppOpsManager.opToPublicName(op)
@@ -150,7 +150,7 @@
// and we have our own persistence.
}
- override fun getForegroundOps(uid: Int): SparseBooleanArray {
+ override fun getForegroundOps(uid: Int, persistentDeviceId: String): SparseBooleanArray {
return SparseBooleanArray().apply {
getUidModes(uid)?.forEachIndexed { _, op, mode ->
if (mode == AppOpsManager.MODE_FOREGROUND) {
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index 4e46836..d62da1a 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -57,11 +57,11 @@
import androidx.test.core.app.ApplicationProvider;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
import com.android.server.LocalServices;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.testing.shadows.ShadowApplicationPackageManager;
import com.android.server.testing.shadows.ShadowUserManager;
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 8d76fdd..3011fa1 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -24,6 +24,8 @@
import android.os.Binder
import android.os.UserHandle
import android.util.ArrayMap
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
+import com.android.internal.pm.parsing.pkg.ParsedPackage
import com.android.internal.pm.pkg.component.ParsedActivity
import com.android.server.pm.AppsFilterImpl
import com.android.server.pm.PackageManagerService
@@ -36,9 +38,7 @@
import com.android.server.pm.SharedLibrariesImpl
import com.android.server.pm.UserManagerInternal
import com.android.server.pm.UserManagerService
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal
import com.android.server.pm.parsing.pkg.PackageImpl
-import com.android.server.pm.parsing.pkg.ParsedPackage
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.resolution.ComponentResolver
import com.android.server.pm.snapshot.PackageDataSnapshot
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
index 25146a8..3461bb6 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
@@ -49,11 +49,12 @@
import androidx.annotation.NonNull;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.pm.pkg.component.ParsedPermission;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.server.om.OverlayReferenceMapper;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.component.ParsedActivityImpl;
import com.android.server.pm.pkg.component.ParsedComponentImpl;
@@ -62,7 +63,6 @@
import com.android.server.pm.pkg.component.ParsedPermissionImpl;
import com.android.server.pm.pkg.component.ParsedProviderImpl;
import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.utils.WatchableTester;
import org.junit.Before;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index 2810145..a0dc2b6 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -63,10 +63,10 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.permission.persistence.RuntimePermissionsPersistence;
import com.android.server.LocalServices;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.LegacyPermissionDataProvider;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.ArchiveState;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
index 7552800..9c48af8 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
@@ -72,7 +72,7 @@
import com.android.compatibility.common.util.CddTest;
import com.android.internal.content.InstallLocationUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.test.service.server.R;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
index 7c28e13..ea88ec2 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
@@ -58,6 +58,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.pm.pkg.component.ParsedApexSystemService;
import com.android.internal.pm.pkg.component.ParsedComponent;
@@ -68,6 +69,7 @@
import com.android.internal.pm.pkg.component.ParsedProvider;
import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.pm.pkg.component.ParsedUsesPermission;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageInfoUtils;
@@ -75,7 +77,6 @@
import com.android.server.pm.parsing.TestPackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.CompatibilityPermissionInfo;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageUserStateInternal;
@@ -88,7 +89,6 @@
import com.android.server.pm.pkg.component.ParsedProviderImpl;
import com.android.server.pm.pkg.component.ParsedServiceImpl;
import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
import org.junit.Before;
import org.junit.Rule;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java
index 38d01d0..8a74e24 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ParallelPackageParserTest.java
@@ -21,9 +21,9 @@
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.TestPackageParser2;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import junit.framework.Assert;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanRequestBuilder.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanRequestBuilder.java
index 1c3673e..2a8e5b1 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanRequestBuilder.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanRequestBuilder.java
@@ -20,7 +20,7 @@
import android.annotation.Nullable;
import android.os.UserHandle;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
class ScanRequestBuilder {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
index e2939c1..decb44c 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
@@ -50,13 +50,13 @@
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import org.hamcrest.BaseMatcher;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index 7123c20..b102ab4 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -38,12 +38,12 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.component.ParsedComponent;
import com.android.internal.pm.pkg.component.ParsedIntentInfo;
import com.android.internal.pm.pkg.component.ParsedPermission;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.PackageManagerException;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.component.ParsedActivityUtils;
import com.android.server.pm.pkg.component.ParsedPermissionUtils;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
index 3b926c2..67b91d2 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt
@@ -18,12 +18,11 @@
import android.annotation.RawRes
import android.content.Context
-import com.android.server.pm.pkg.parsing.ParsingPackage
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils
import android.content.pm.parsing.result.ParseResult
import android.platform.test.annotations.Presubmit
import androidx.test.InstrumentationRegistry
-import com.android.server.pm.parsing.pkg.ParsedPackage
+import com.android.internal.pm.parsing.pkg.ParsedPackage
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils
import com.android.server.pm.test.service.server.R
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java
index f376e73..6cd7123 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java
@@ -24,8 +24,8 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
index 9248da6..27fd781 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
@@ -21,8 +21,8 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java
index 23a2c20..b13d6de 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java
@@ -23,8 +23,8 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
index 2060caa..fa69f84 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
@@ -24,9 +24,9 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.parsing.library.PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
index b3ad861..856013a 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
@@ -22,9 +22,9 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import org.junit.Before;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java
index 558c0e8..ae5ea21 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java
@@ -21,8 +21,8 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
index 7a2ac75..e126ffc 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
@@ -23,8 +23,8 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
index c4b8e6f..d0b0cf8 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
@@ -28,11 +28,11 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
import org.junit.Assume;
import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageSharedLibraryUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
index 33fc261..d60c457 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
@@ -18,7 +18,7 @@
import static org.junit.Assert.assertEquals;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import java.util.function.Supplier;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
index 8918233..c141c03 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
@@ -23,9 +23,9 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
index 3e9ec0e..a58604b 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
@@ -23,9 +23,9 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryOrgApacheHttpLegacyLibrary;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import org.junit.Test;
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
index 766ab94..9341e9d 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
@@ -21,9 +21,9 @@
import android.os.Build
import android.os.PatternMatcher
import android.util.ArraySet
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
import com.android.server.SystemConfig
import com.android.server.compat.PlatformCompat
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.pkg.component.ParsedActivityImpl
import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 9fbf86e..a737b90 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -28,9 +28,8 @@
import android.util.IndentingPrintWriter
import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
import com.android.server.pm.Computer
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal
-import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.pkg.PackageStateInternal
import com.android.server.pm.pkg.PackageUserStateInternal
import com.android.server.pm.pkg.component.ParsedActivityImpl
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
index 47d9196..f38df22 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -29,7 +29,7 @@
import android.os.Process
import android.util.ArraySet
import android.util.SparseArray
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
import com.android.server.pm.pkg.PackageStateInternal
import com.android.server.pm.pkg.PackageUserStateInternal
import com.android.server.pm.pkg.component.ParsedActivityImpl
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index 98d7801..874e0d2 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -36,9 +36,8 @@
import android.util.ArraySet
import android.util.SparseArray
import android.util.Xml
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
import com.android.server.pm.Computer
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal
-import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.pkg.PackageStateInternal
import com.android.server.pm.pkg.PackageUserStateInternal
import com.android.server.pm.pkg.component.ParsedActivityImpl
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 4a211df..3207e6c 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -24,7 +24,7 @@
import android.os.Process
import android.util.ArraySet
import android.util.SparseArray
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
import com.android.server.pm.pkg.PackageStateInternal
import com.android.server.pm.pkg.PackageUserStateInternal
import com.android.server.pm.pkg.component.ParsedActivityImpl
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index d54d608..a90b7d5 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -26,8 +26,7 @@
import android.os.Process
import android.util.ArraySet
import android.util.SparseArray
-import com.android.server.pm.parsing.pkg.AndroidPackageInternal
-import com.android.server.pm.pkg.AndroidPackage
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
import com.android.server.pm.pkg.PackageStateInternal
import com.android.server.pm.pkg.PackageUserStateInternal
import com.android.server.pm.pkg.component.ParsedActivityImpl
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index 97e5826..a2e80f0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -104,7 +104,7 @@
468.5f,
};
- private static final int[] DISPLAY_LEVELS_BACKLIGHT = {
+ private static final int[] DISPLAY_LEVELS_INT = {
9,
30,
45,
@@ -118,6 +118,20 @@
255
};
+ private static final float[] DISPLAY_LEVELS = {
+ 0.03f,
+ 0.11f,
+ 0.17f,
+ 0.24f,
+ 0.3f,
+ 0.37f,
+ 0.46f,
+ 0.57f,
+ 0.7f,
+ 0.87f,
+ 1
+ };
+
private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f };
private static final float[] DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT = { 0.03149606299f, 1.0f };
@@ -155,23 +169,23 @@
DisplayWhiteBalanceController mMockDwbc;
@Test
- public void testSimpleStrategyMappingAtControlPoints() {
- Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ public void testSimpleStrategyMappingAtControlPoints_IntConfig() {
+ Resources res = createResources(DISPLAY_LEVELS_INT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNotNull("BrightnessMappingStrategy should not be null", simple);
for (int i = 0; i < LUX_LEVELS.length; i++) {
final float expectedLevel = MathUtils.map(PowerManager.BRIGHTNESS_OFF + 1,
PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN,
- PowerManager.BRIGHTNESS_MAX, DISPLAY_LEVELS_BACKLIGHT[i]);
+ PowerManager.BRIGHTNESS_MAX, DISPLAY_LEVELS_INT[i]);
assertEquals(expectedLevel,
simple.getBrightness(LUX_LEVELS[i]), 0.0001f /*tolerance*/);
}
}
@Test
- public void testSimpleStrategyMappingBetweenControlPoints() {
- Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ public void testSimpleStrategyMappingBetweenControlPoints_IntConfig() {
+ Resources res = createResources(DISPLAY_LEVELS_INT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNotNull("BrightnessMappingStrategy should not be null", simple);
@@ -179,14 +193,42 @@
final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
final float backlight = simple.getBrightness(lux) * PowerManager.BRIGHTNESS_ON;
assertTrue("Desired brightness should be between adjacent control points.",
- backlight > DISPLAY_LEVELS_BACKLIGHT[i - 1]
- && backlight < DISPLAY_LEVELS_BACKLIGHT[i]);
+ backlight > DISPLAY_LEVELS_INT[i - 1]
+ && backlight < DISPLAY_LEVELS_INT[i]);
+ }
+ }
+
+ @Test
+ public void testSimpleStrategyMappingAtControlPoints_FloatConfig() {
+ Resources res = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY, EMPTY_FLOAT_ARRAY, LUX_LEVELS,
+ EMPTY_FLOAT_ARRAY, DISPLAY_LEVELS);
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
+ assertNotNull("BrightnessMappingStrategy should not be null", simple);
+ for (int i = 0; i < LUX_LEVELS.length; i++) {
+ assertEquals(DISPLAY_LEVELS[i], simple.getBrightness(LUX_LEVELS[i]),
+ /* tolerance= */ 0.0001f);
+ }
+ }
+
+ @Test
+ public void testSimpleStrategyMappingBetweenControlPoints_FloatConfig() {
+ Resources res = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY, EMPTY_FLOAT_ARRAY, LUX_LEVELS,
+ EMPTY_FLOAT_ARRAY, DISPLAY_LEVELS);
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
+ assertNotNull("BrightnessMappingStrategy should not be null", simple);
+ for (int i = 1; i < LUX_LEVELS.length; i++) {
+ final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
+ final float brightness = simple.getBrightness(lux);
+ assertTrue("Desired brightness should be between adjacent control points.",
+ brightness > DISPLAY_LEVELS[i - 1] && brightness < DISPLAY_LEVELS[i]);
}
}
@Test
public void testSimpleStrategyIgnoresNewConfiguration() {
- Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ Resources res = createResources(DISPLAY_LEVELS_INT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
@@ -201,14 +243,14 @@
@Test
public void testSimpleStrategyIgnoresNullConfiguration() {
- Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ Resources res = createResources(DISPLAY_LEVELS_INT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
strategy.setBrightnessConfiguration(null);
- final int n = DISPLAY_LEVELS_BACKLIGHT.length;
+ final int n = DISPLAY_LEVELS_INT.length;
final float expectedBrightness =
- (float) DISPLAY_LEVELS_BACKLIGHT[n - 1] / PowerManager.BRIGHTNESS_ON;
+ (float) DISPLAY_LEVELS_INT[n - 1] / PowerManager.BRIGHTNESS_ON;
assertEquals(expectedBrightness,
strategy.getBrightness(LUX_LEVELS[n - 1]), 0.0001f /*tolerance*/);
}
@@ -322,7 +364,7 @@
@Test
public void testDefaultStrategyIsPhysical() {
- Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ Resources res = createResources(DISPLAY_LEVELS_INT);
DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
@@ -363,13 +405,13 @@
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
- res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ res = createResources(DISPLAY_LEVELS_INT);
strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
// Extra backlight level
final int[] backlight = Arrays.copyOf(
- DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length + 1);
+ DISPLAY_LEVELS_INT, DISPLAY_LEVELS_INT.length + 1);
backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1;
res = createResources(backlight);
ddc = createDdc(DISPLAY_RANGE_NITS,
@@ -410,7 +452,7 @@
LUX_LEVELS, DISPLAY_LEVELS_NITS);
assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc));
ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
- res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ res = createResources(DISPLAY_LEVELS_INT);
assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc));
}
@@ -546,16 +588,24 @@
when(mockDdc.getBrightness()).thenReturn(backlightArray);
when(mockDdc.getAutoBrightnessBrighteningLevelsLux()).thenReturn(LUX_LEVELS);
when(mockDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(EMPTY_FLOAT_ARRAY);
+ when(mockDdc.getAutoBrightnessBrighteningLevels()).thenReturn(EMPTY_FLOAT_ARRAY);
return mockDdc;
}
private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray,
float[] luxLevelsFloat, float[] brightnessLevelsNits) {
+ return createDdc(nitsArray, backlightArray, luxLevelsFloat, brightnessLevelsNits,
+ EMPTY_FLOAT_ARRAY);
+ }
+
+ private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray,
+ float[] luxLevelsFloat, float[] brightnessLevelsNits, float[] brightnessLevels) {
DisplayDeviceConfig mockDdc = mock(DisplayDeviceConfig.class);
when(mockDdc.getNits()).thenReturn(nitsArray);
when(mockDdc.getBrightness()).thenReturn(backlightArray);
when(mockDdc.getAutoBrightnessBrighteningLevelsLux()).thenReturn(luxLevelsFloat);
when(mockDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(brightnessLevelsNits);
+ when(mockDdc.getAutoBrightnessBrighteningLevels()).thenReturn(brightnessLevels);
return mockDdc;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 0bcbeb9..31d7e88 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -47,8 +47,10 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.server.display.config.HdrBrightnessData;
import com.android.server.display.config.ThermalStatus;
+import com.android.server.display.feature.DisplayManagerFlags;
import org.junit.Before;
import org.junit.Test;
@@ -92,10 +94,14 @@
@Mock
private Resources mResources;
+ @Mock
+ private DisplayManagerFlags mFlags;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getResources()).thenReturn(mResources);
+ when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(true);
mockDeviceConfigs();
}
@@ -110,10 +116,6 @@
assertArrayEquals(mDisplayDeviceConfig.getBrightness(), BRIGHTNESS, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getNits(), NITS, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getBacklight(), BRIGHTNESS, ZERO_DELTA);
- assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
- float[]{0.0f, 50.0f, 80.0f}, ZERO_DELTA);
- assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
- float[]{45.32f, 75.43f}, ZERO_DELTA);
// Test thresholds
assertEquals(10, mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(),
@@ -606,7 +608,7 @@
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
float[]{2.0f, 200.0f, 600.0f}, ZERO_DELTA);
assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
- float[]{0.0f, 0.0f, 110.0f, 500.0f}, ZERO_DELTA);
+ float[]{0.0f, 110.0f, 500.0f}, ZERO_DELTA);
// Test thresholds
assertEquals(0, mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(), ZERO_DELTA);
@@ -671,6 +673,9 @@
assertEquals("test_light_sensor", mDisplayDeviceConfig.getAmbientLightSensor().type);
assertEquals("", mDisplayDeviceConfig.getAmbientLightSensor().name);
+
+ assertEquals(BrightnessSynchronizer.brightnessIntToFloat(35),
+ mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(), ZERO_DELTA);
}
@Test
@@ -716,6 +721,38 @@
assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowIncreaseIdle(), 0.04f, ZERO_DELTA);
}
+ @Test
+ public void testBrightnessCapForWearBedtimeMode() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
+ getValidProxSensor(), /* includeIdleMode= */ false));
+ assertEquals(0.1f, mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(), ZERO_DELTA);
+ }
+
+ @Test
+ public void testAutoBrightnessBrighteningLevels() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
+ getValidProxSensor(), /* includeIdleMode= */ false));
+
+ assertArrayEquals(new float[]{0.0f, 80},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), ZERO_DELTA);
+ assertArrayEquals(new float[]{0.2f, 0.3f},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(), SMALL_DELTA);
+ }
+
+ @Test
+ public void testAutoBrightnessBrighteningLevels_FeatureFlagOff() throws IOException {
+ when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(false);
+ setupDisplayDeviceConfigFromConfigResourceFile();
+ setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
+ getValidProxSensor(), /* includeIdleMode= */ false));
+
+ assertNull(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels());
+ assertArrayEquals(new float[]{0, 110, 500},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), ZERO_DELTA);
+ assertArrayEquals(new float[]{2, 200, 600},
+ mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), SMALL_DELTA);
+ }
+
private String getValidLuxThrottling() {
return "<luxThrottling>\n"
+ " <brightnessLimitMap>\n"
@@ -1037,8 +1074,8 @@
+ "<screenBrightnessRampDecreaseMaxIdleMillis>"
+ "5000"
+ "</screenBrightnessRampDecreaseMaxIdleMillis>\n";
-
}
+
private String getContent() {
return getContent(getValidLuxThrottling(), getValidProxSensor(),
/* includeIdleMode= */ true);
@@ -1089,16 +1126,18 @@
+ "<brighteningLightDebounceMillis>2000</brighteningLightDebounceMillis>\n"
+ "<darkeningLightDebounceMillis>1000</darkeningLightDebounceMillis>\n"
+ (includeIdleMode ? getRampSpeedsIdle() : "")
- + "<displayBrightnessMapping>\n"
- + "<displayBrightnessPoint>\n"
- + "<lux>50</lux>\n"
- + "<nits>45.32</nits>\n"
- + "</displayBrightnessPoint>\n"
- + "<displayBrightnessPoint>\n"
- + "<lux>80</lux>\n"
- + "<nits>75.43</nits>\n"
- + "</displayBrightnessPoint>\n"
- + "</displayBrightnessMapping>\n"
+ + "<luxToBrightnessMapping>\n"
+ + "<map>\n"
+ + "<point>\n"
+ + "<first>0</first>\n"
+ + "<second>0.2</second>\n"
+ + "</point>\n"
+ + "<point>\n"
+ + "<first>80</first>\n"
+ + "<second>0.3</second>\n"
+ + "</point>\n"
+ + "</map>\n"
+ + "</luxToBrightnessMapping>\n"
+ "</autoBrightness>\n"
+ getPowerThrottlingConfig()
+ "<highBrightnessMode enabled=\"true\">\n"
@@ -1355,6 +1394,9 @@
+ "<majorVersion>2</majorVersion>\n"
+ "<minorVersion>0</minorVersion>\n"
+ "</usiVersion>\n"
+ + "<screenBrightnessCapForWearBedtimeMode>"
+ + "0.1"
+ + "</screenBrightnessCapForWearBedtimeMode>"
+ "</displayConfiguration>\n";
}
@@ -1372,7 +1414,7 @@
private void setupDisplayDeviceConfigFromDisplayConfigFile(String content) throws IOException {
Path tempFile = Files.createTempFile("display_config", ".tmp");
Files.write(tempFile, content.getBytes(StandardCharsets.UTF_8));
- mDisplayDeviceConfig = new DisplayDeviceConfig(mContext);
+ mDisplayDeviceConfig = new DisplayDeviceConfig(mContext, mFlags);
mDisplayDeviceConfig.initFromFile(tempFile.toFile());
}
@@ -1381,25 +1423,15 @@
when(mResources.obtainTypedArray(
com.android.internal.R.array.config_screenBrightnessNits))
.thenReturn(screenBrightnessNits);
- TypedArray screenBrightnessBacklight = createFloatTypedArray(new
- float[]{0.0f, 120.0f, 255.0f});
- when(mResources.obtainTypedArray(
- com.android.internal.R.array.config_screenBrightnessBacklight))
- .thenReturn(screenBrightnessBacklight);
when(mResources.getIntArray(com.android.internal.R.array
.config_screenBrightnessBacklight)).thenReturn(new int[]{0, 120, 255});
- when(mResources.getIntArray(com.android.internal.R.array
- .config_autoBrightnessLevels)).thenReturn(new int[]{30, 80});
- when(mResources.getIntArray(com.android.internal.R.array
- .config_autoBrightnessDisplayValuesNits)).thenReturn(new int[]{25, 55});
-
TypedArray screenBrightnessLevelNits = createFloatTypedArray(new
float[]{2.0f, 200.0f, 600.0f});
when(mResources.obtainTypedArray(
com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
.thenReturn(screenBrightnessLevelNits);
- int[] screenBrightnessLevelLux = new int[]{0, 110, 500};
+ int[] screenBrightnessLevelLux = new int[]{110, 500};
when(mResources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLevels))
.thenReturn(screenBrightnessLevelLux);
@@ -1457,7 +1489,12 @@
R.integer.config_autoBrightnessDarkeningLightDebounce))
.thenReturn(4000);
- mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, true);
+ when(mResources.getInteger(
+ R.integer.config_screenBrightnessCapForWearBedtimeMode))
+ .thenReturn(35);
+
+ mDisplayDeviceConfig = DisplayDeviceConfig.create(mContext, /* useConfigXml= */ true,
+ mFlags);
}
private TypedArray createFloatTypedArray(float[] vals) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
index 4fd8f26..dc6abf1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
@@ -57,6 +57,9 @@
@Mock
private SurfaceControl.Transaction mMockTransaction;
+ @Mock
+ private DisplayAdapter mMockDisplayAdapter;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -67,34 +70,39 @@
@Test
public void testGetDisplaySurfaceDefaultSizeLocked_notRotated() {
- DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+ mMockDisplayAdapter);
assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
}
@Test
public void testGetDisplaySurfaceDefaultSizeLocked_rotation0() {
- DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+ mMockDisplayAdapter);
displayDevice.setProjectionLocked(mMockTransaction, ROTATION_0, new Rect(), new Rect());
assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
}
@Test
public void testGetDisplaySurfaceDefaultSizeLocked_rotation90() {
- DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+ mMockDisplayAdapter);
displayDevice.setProjectionLocked(mMockTransaction, ROTATION_90, new Rect(), new Rect());
assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(LANDSCAPE_SIZE);
}
@Test
public void testGetDisplaySurfaceDefaultSizeLocked_rotation180() {
- DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+ mMockDisplayAdapter);
displayDevice.setProjectionLocked(mMockTransaction, ROTATION_180, new Rect(), new Rect());
assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
}
@Test
public void testGetDisplaySurfaceDefaultSizeLocked_rotation270() {
- DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo,
+ mMockDisplayAdapter);
displayDevice.setProjectionLocked(mMockTransaction, ROTATION_270, new Rect(), new Rect());
assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(LANDSCAPE_SIZE);
}
@@ -102,8 +110,9 @@
private static class FakeDisplayDevice extends DisplayDevice {
private final DisplayDeviceInfo mDisplayDeviceInfo;
- FakeDisplayDevice(DisplayDeviceInfo displayDeviceInfo) {
- super(null, null, "", InstrumentationRegistry.getInstrumentation().getContext());
+ FakeDisplayDevice(DisplayDeviceInfo displayDeviceInfo, DisplayAdapter displayAdapter) {
+ super(displayAdapter, /* displayToken= */ null, /* uniqueId= */ "",
+ InstrumentationRegistry.getInstrumentation().getContext());
mDisplayDeviceInfo = displayDeviceInfo;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 55f56e9..02e3ef4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -212,7 +212,8 @@
new DisplayManagerService.Injector() {
@Override
VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot,
- Context context, Handler handler, DisplayAdapter.Listener listener) {
+ Context context, Handler handler, DisplayAdapter.Listener listener,
+ DisplayManagerFlags flags) {
return mMockVirtualDisplayAdapter;
}
@@ -251,7 +252,8 @@
@Override
VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+ Handler handler, DisplayAdapter.Listener displayAdapterListener,
+ DisplayManagerFlags flags) {
return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
new VirtualDisplayAdapter.SurfaceControlDisplayFactory() {
@Override
@@ -263,7 +265,7 @@
@Override
public void destroyDisplay(IBinder displayToken) {
}
- });
+ }, flags);
}
@Override
@@ -329,6 +331,7 @@
@Mock SensorManager mSensorManager;
@Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
@Mock PackageManagerInternal mMockPackageManagerInternal;
+ @Mock DisplayAdapter mMockDisplayAdapter;
@Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
@Mock DisplayManagerFlags mMockFlags;
@@ -788,7 +791,8 @@
new DisplayManagerService.Injector() {
@Override
VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot,
- Context context, Handler handler, DisplayAdapter.Listener listener) {
+ Context context, Handler handler, DisplayAdapter.Listener listener,
+ DisplayManagerFlags flags) {
return null; // return null for the adapter. This should cause a failure.
}
@@ -3194,7 +3198,7 @@
private DisplayDeviceInfo mDisplayDeviceInfo;
FakeDisplayDevice() {
- super(null, null, "", mContext);
+ super(mMockDisplayAdapter, /* displayToken= */ null, /* uniqueId= */ "", mContext);
}
public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 8270845..f36854b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -1455,8 +1455,8 @@
// mMockContext and values will be loaded from mMockResources.
@Override
public DisplayDeviceConfig createDisplayDeviceConfig(Context context,
- long physicalDisplayId, boolean isFirstDisplay) {
- return DisplayDeviceConfig.create(context, isFirstDisplay);
+ long physicalDisplayId, boolean isFirstDisplay, DisplayManagerFlags flags) {
+ return DisplayDeviceConfig.create(context, isFirstDisplay, flags);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 43d2b8f..28ec896 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -119,8 +119,8 @@
@Mock IThermalService mIThermalServiceMock;
@Spy DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy =
new DeviceStateToLayoutMap(mIdProducer, NON_EXISTING_FILE);
- @Mock
- DisplayManagerFlags mFlagsMock;
+ @Mock DisplayManagerFlags mFlagsMock;
+ @Mock DisplayAdapter mDisplayAdapterMock;
@Captor ArgumentCaptor<LogicalDisplay> mDisplayCaptor;
@Captor ArgumentCaptor<Integer> mDisplayEventCaptor;
@@ -1075,7 +1075,8 @@
private int mState;
TestDisplayDevice() {
- super(null, null, "test_display_" + sUniqueTestDisplayId++, mContextMock);
+ super(mDisplayAdapterMock, /* displayToken= */ null,
+ "test_display_" + sUniqueTestDisplayId++, mContextMock);
mInfo = new DisplayDeviceInfo();
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
index 9f91916..5676a38 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
@@ -32,9 +32,12 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -51,8 +54,12 @@
private TestInjector mInjector;
private TestLooper mTestLooper;
+ @Mock
+ private DisplayAdapter mDisplayAdapter;
+
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
mInjector = new TestInjector();
mTestLooper = new TestLooper();
Handler handler = new Handler(mTestLooper.getLooper());
@@ -62,8 +69,8 @@
@Test
public void testLoadBrightness() {
final String uniqueDisplayId = "test:123";
- final DisplayDevice testDisplayDevice = new DisplayDevice(
- null, null, uniqueDisplayId, null) {
+ final DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
@@ -100,7 +107,8 @@
public void testSetBrightness_brightnessTagWithNoUserId_updatesToBrightnessTagWithUserId() {
final String uniqueDisplayId = "test:123";
final DisplayDevice testDisplayDevice =
- new DisplayDevice(null, null, uniqueDisplayId, null) {
+ new DisplayDevice(mDisplayAdapter, /* displayToken= */ null, uniqueDisplayId,
+ /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
@@ -273,7 +281,8 @@
assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
userSerial));
- DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
@@ -319,7 +328,8 @@
assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
userSerial));
- DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return false;
@@ -386,7 +396,8 @@
@Test
public void testStoreAndRestoreResolution() {
final String uniqueDisplayId = "test:123";
- DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
@@ -422,7 +433,8 @@
@Test
public void testStoreAndRestoreRefreshRate() {
final String uniqueDisplayId = "test:123";
- DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
@@ -455,7 +467,8 @@
@Test
public void testBrightnessInitialisesWithInvalidFloat() {
final String uniqueDisplayId = "test:123";
- DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ DisplayDevice testDisplayDevice = new DisplayDevice(mDisplayAdapter,
+ /* displayToken= */ null, uniqueDisplayId, /* context= */ null) {
@Override
public boolean hasStableUniqueId() {
return true;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
index b7cbac5..5c50acb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
@@ -72,27 +72,14 @@
@Test
public void testFindHighestRefreshRateForDefaultDisplay() {
- when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
- assertEquals(120,
- RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
- /* delta= */ 0);
- }
-
- @Test
- public void testFindHighestRefreshRate() {
- int displayId = 13;
- when(mDisplayManagerMock.getDisplay(displayId)).thenReturn(mDisplayMock);
- assertEquals(120,
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, displayId),
- /* delta= */ 0);
- }
-
- @Test
- public void testFindHighestRefreshRate_DisplayIsNull() {
when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null);
assertEquals(DEFAULT_REFRESH_RATE,
RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
/* delta= */ 0);
+ when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
+ assertEquals(120,
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
+ /* delta= */ 0);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index 73e7ba0..c01b15c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -28,6 +28,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.testutils.TestHandler;
import org.junit.Before;
@@ -59,13 +60,17 @@
private VirtualDisplayAdapter mVirtualDisplayAdapter;
+ @Mock
+ private DisplayManagerFlags mFeatureFlags;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mHandler = new TestHandler(null);
mVirtualDisplayAdapter = new VirtualDisplayAdapter(new DisplayManagerService.SyncRoot(),
- mContextMock, mHandler, mMockListener, mMockSufaceControlDisplayFactory);
+ mContextMock, mHandler, mMockListener, mMockSufaceControlDisplayFactory,
+ mFeatureFlags);
when(mMockCallback.asBinder()).thenReturn(mMockBinder);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index ff2b1f4..9aa6136 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -262,7 +262,8 @@
Handler handler,
BrightnessClamperController.ClamperChangeListener clamperChangeListener,
BrightnessClamperController.DisplayDeviceData data,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags,
+ Context context) {
mCapturedChangeListener = clamperChangeListener;
return mClampers;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
new file mode 100644
index 0000000..3458b08
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.clamper;
+
+import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeClamper.BEDTIME_MODE_OFF;
+import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeClamper.BEDTIME_MODE_ON;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.provider.Settings;
+import android.testing.TestableContext;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.display.BrightnessSynchronizer;
+import com.android.server.testutils.TestHandler;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class BrightnessWearBedtimeModeClamperTest {
+
+ private static final float BRIGHTNESS_CAP = 0.3f;
+
+ @Mock
+ private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+
+ private final TestHandler mTestHandler = new TestHandler(null);
+ private final TestInjector mInjector = new TestInjector();
+ private BrightnessWearBedtimeModeClamper mClamper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mClamper = new BrightnessWearBedtimeModeClamper(mInjector, mTestHandler, mContext,
+ mMockClamperChangeListener, () -> BRIGHTNESS_CAP);
+ mTestHandler.flush();
+ }
+
+ @Test
+ public void testBrightnessCap() {
+ assertEquals(BRIGHTNESS_CAP, mClamper.getBrightnessCap(), BrightnessSynchronizer.EPSILON);
+ }
+
+ @Test
+ public void testBedtimeModeOn() {
+ setBedtimeModeEnabled(true);
+ assertTrue(mClamper.isActive());
+ verify(mMockClamperChangeListener).onChanged();
+ }
+
+ @Test
+ public void testBedtimeModeOff() {
+ setBedtimeModeEnabled(false);
+ assertFalse(mClamper.isActive());
+ verify(mMockClamperChangeListener).onChanged();
+ }
+
+ @Test
+ public void testType() {
+ assertEquals(BrightnessClamper.Type.BEDTIME_MODE, mClamper.getType());
+ }
+
+ @Test
+ public void testOnDisplayChanged() {
+ float newBrightnessCap = 0.61f;
+
+ mClamper.onDisplayChanged(() -> newBrightnessCap);
+ mTestHandler.flush();
+
+ assertEquals(newBrightnessCap, mClamper.getBrightnessCap(), BrightnessSynchronizer.EPSILON);
+ verify(mMockClamperChangeListener).onChanged();
+ }
+
+ private void setBedtimeModeEnabled(boolean enabled) {
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.Wearable.BEDTIME_MODE,
+ enabled ? BEDTIME_MODE_ON : BEDTIME_MODE_OFF);
+ mInjector.notifyBedtimeModeChanged();
+ mTestHandler.flush();
+ }
+
+ private static class TestInjector extends BrightnessWearBedtimeModeClamper.Injector {
+
+ private ContentObserver mObserver;
+
+ @Override
+ void registerBedtimeModeObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ mObserver = observer;
+ }
+
+ private void notifyBedtimeModeChanged() {
+ if (mObserver != null) {
+ mObserver.dispatchChange(/* selfChange= */ false,
+ Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE));
+ }
+ }
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt
new file mode 100644
index 0000000..3f72364
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.server.display.mode.DisplayModeDirector.VoteSummary
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+
+private const val BASE_REFRESH_RATE = 60f
+private const val OTHER_BASE_REFRESH_RATE = 90f
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BaseModeRefreshRateVoteTest {
+
+ private lateinit var baseModeVote: BaseModeRefreshRateVote
+
+ @Before
+ fun setUp() {
+ baseModeVote = BaseModeRefreshRateVote(BASE_REFRESH_RATE)
+ }
+
+ @Test
+ fun `updates summary with base mode refresh rate if not set`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+
+ baseModeVote.updateSummary(summary)
+
+ assertThat(summary.appRequestBaseModeRefreshRate).isEqualTo(BASE_REFRESH_RATE)
+ }
+
+ @Test
+ fun `keeps summary base mode refresh rate if set`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.appRequestBaseModeRefreshRate = OTHER_BASE_REFRESH_RATE
+
+ baseModeVote.updateSummary(summary)
+
+ assertThat(summary.appRequestBaseModeRefreshRate).isEqualTo(OTHER_BASE_REFRESH_RATE)
+ }
+
+ @Test
+ fun `keeps summary with base mode refresh rate if vote refresh rate is negative`() {
+ val invalidBaseModeVote = BaseModeRefreshRateVote(-10f)
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+
+ invalidBaseModeVote.updateSummary(summary)
+
+ assertThat(summary.appRequestBaseModeRefreshRate).isZero()
+ }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt
new file mode 100644
index 0000000..7f8da88
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.server.display.mode.DisplayModeDirector.VoteSummary
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.junit.MockitoJUnit
+
+
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CombinedVoteTest {
+ private lateinit var combinedVote: CombinedVote
+
+ @get:Rule
+ val mockitoRule = MockitoJUnit.rule()
+
+ private val mockVote1 = mock<Vote>()
+ private val mockVote2 = mock<Vote>()
+
+ @Before
+ fun setUp() {
+ combinedVote = CombinedVote(listOf(mockVote1, mockVote2))
+ }
+
+ @Test
+ fun `delegates update to children`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+
+ combinedVote.updateSummary(summary)
+
+ verify(mockVote1).updateSummary(summary)
+ verify(mockVote2).updateSummary(summary)
+ }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt
new file mode 100644
index 0000000..c624325
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode
+
+import androidx.test.filters.SmallTest
+import com.android.server.display.mode.DisplayModeDirector.VoteSummary
+import com.google.common.truth.Truth.assertThat
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(TestParameterInjector::class)
+class DisableRefreshRateSwitchingVoteTest {
+
+ @Test
+ fun `disabled refresh rate switching is not changed`(
+ @TestParameter voteDisableSwitching: Boolean
+ ) {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.disableRefreshRateSwitching = true
+ val vote = DisableRefreshRateSwitchingVote(voteDisableSwitching)
+
+ vote.updateSummary(summary)
+
+ assertThat(summary.disableRefreshRateSwitching).isTrue()
+ }
+
+ @Test
+ fun `disables refresh rate switching if requested`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ val vote = DisableRefreshRateSwitchingVote(true)
+
+ vote.updateSummary(summary)
+
+ assertThat(summary.disableRefreshRateSwitching).isTrue()
+ }
+
+ @Test
+ fun `does not disable refresh rate switching if not requested`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ val vote = DisableRefreshRateSwitchingVote(false)
+
+ vote.updateSummary(summary)
+
+ assertThat(summary.disableRefreshRateSwitching).isFalse()
+ }
+}
\ No newline at end of file
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 6798a2d..60a0c03 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -28,7 +28,6 @@
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.server.display.mode.Vote.INVALID_SIZE;
import static com.google.common.truth.Truth.assertThat;
@@ -291,7 +290,6 @@
};
private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY;
- private static final int DISPLAY_ID_2 = Display.DEFAULT_DISPLAY + 1;
private static final int MODE_ID = 1;
private static final float TRANSITION_POINT = 0.763f;
@@ -1193,7 +1191,9 @@
assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
assertThat(vote).isNotNull();
- assertThat(vote.disableRefreshRateSwitching).isTrue();
+ assertThat(vote).isInstanceOf(DisableRefreshRateSwitchingVote.class);
+ DisableRefreshRateSwitchingVote disableVote = (DisableRefreshRateSwitchingVote) vote;
+ assertThat(disableVote.mDisableRefreshRateSwitching).isTrue();
}
@Test
@@ -1272,7 +1272,9 @@
assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
assertThat(vote).isNotNull();
- assertThat(vote.disableRefreshRateSwitching).isTrue();
+ assertThat(vote).isInstanceOf(DisableRefreshRateSwitchingVote.class);
+ DisableRefreshRateSwitchingVote disableVote = (DisableRefreshRateSwitchingVote) vote;
+ assertThat(disableVote.mDisableRefreshRateSwitching).isTrue();
// We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
// parameter to the necessary threshold
@@ -1341,7 +1343,9 @@
assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/);
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
assertThat(vote).isNotNull();
- assertThat(vote.disableRefreshRateSwitching).isTrue();
+ assertThat(vote).isInstanceOf(DisableRefreshRateSwitchingVote.class);
+ DisableRefreshRateSwitchingVote disableVote = (DisableRefreshRateSwitchingVote) vote;
+ assertThat(disableVote.mDisableRefreshRateSwitching).isTrue();
}
@Test
@@ -1424,7 +1428,9 @@
assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
assertThat(vote).isNotNull();
- assertThat(vote.disableRefreshRateSwitching).isTrue();
+ assertThat(vote).isInstanceOf(DisableRefreshRateSwitchingVote.class);
+ DisableRefreshRateSwitchingVote disableVote = (DisableRefreshRateSwitchingVote) vote;
+ assertThat(disableVote.mDisableRefreshRateSwitching).isTrue();
// Set critical and check new refresh rate
Temperature temp = getSkinTemp(Temperature.THROTTLING_CRITICAL);
@@ -1436,7 +1442,9 @@
assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/);
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
assertThat(vote).isNotNull();
- assertThat(vote.disableRefreshRateSwitching).isTrue();
+ assertThat(vote).isInstanceOf(DisableRefreshRateSwitchingVote.class);
+ disableVote = (DisableRefreshRateSwitchingVote) vote;
+ assertThat(disableVote.mDisableRefreshRateSwitching).isTrue();
}
@Test
@@ -1519,7 +1527,9 @@
assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
assertThat(vote).isNotNull();
- assertThat(vote.disableRefreshRateSwitching).isTrue();
+ assertThat(vote).isInstanceOf(DisableRefreshRateSwitchingVote.class);
+ DisableRefreshRateSwitchingVote disableVote = (DisableRefreshRateSwitchingVote) vote;
+ assertThat(disableVote.mDisableRefreshRateSwitching).isTrue();
// Set critical and check new refresh rate
Temperature temp = getSkinTemp(Temperature.THROTTLING_CRITICAL);
@@ -1531,19 +1541,18 @@
assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/);
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
assertThat(vote).isNotNull();
- assertThat(vote.disableRefreshRateSwitching).isTrue();
+ assertThat(vote).isInstanceOf(DisableRefreshRateSwitchingVote.class);
+ disableVote = (DisableRefreshRateSwitchingVote) vote;
+ assertThat(disableVote.mDisableRefreshRateSwitching).isTrue();
}
@Test
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);
@@ -1554,14 +1563,10 @@
setPeakRefreshRate(Float.POSITIVE_INFINITY);
- Vote vote1 = director.getVote(DISPLAY_ID,
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- Vote vote2 = director.getVote(DISPLAY_ID_2,
- Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ 0,
- /* frameRateHigh= */ highestRefreshRate1);
- assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ 0,
- /* frameRateHigh= */ highestRefreshRate2);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ highestRefreshRate);
}
@Test
@@ -1579,54 +1584,19 @@
setPeakRefreshRate(peakRefreshRate);
- Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
- /* frameRateHigh= */ peakRefreshRate);
- }
-
- @Test
- public void testPeakRefreshRate_DisplayChanged() {
- when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
- .thenReturn(true);
- float highestRefreshRate = 130;
- doReturn(highestRefreshRate).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- DisplayModeDirector director =
- createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
-
- Sensor lightSensor = createLightSensor();
- SensorManager sensorManager = createMockSensorManager(lightSensor);
- director.start(sensorManager);
-
- setPeakRefreshRate(Float.POSITIVE_INFINITY);
-
- Vote vote = director.getVote(DISPLAY_ID,
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
- /* frameRateHigh= */ highestRefreshRate);
-
- // The highest refresh rate of the display changes
- highestRefreshRate = 140;
- doReturn(highestRefreshRate).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- director.getDisplayObserver().onDisplayChanged(DISPLAY_ID);
-
- vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
- /* frameRateHigh= */ highestRefreshRate);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ peakRefreshRate);
}
@Test
public void testMinRefreshRate_FlagEnabled() {
when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
.thenReturn(true);
- float highestRefreshRate1 = 130;
- float highestRefreshRate2 = 132;
- doReturn(highestRefreshRate1).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- doReturn(highestRefreshRate2).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID_2));
+ float highestRefreshRate = 130;
+ doReturn(highestRefreshRate).when(() ->
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -1637,12 +1607,9 @@
setMinRefreshRate(Float.POSITIVE_INFINITY);
- Vote vote1 = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- Vote vote2 = director.getVote(DISPLAY_ID_2,
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ highestRefreshRate1,
- /* frameRateHigh= */ Float.POSITIVE_INFINITY);
- assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ highestRefreshRate2,
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate,
/* frameRateHigh= */ Float.POSITIVE_INFINITY);
}
@@ -1661,44 +1628,13 @@
setMinRefreshRate(minRefreshRate);
- Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ minRefreshRate,
/* frameRateHigh= */ Float.POSITIVE_INFINITY);
}
@Test
- public void testMinRefreshRate_DisplayChanged() {
- when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
- .thenReturn(true);
- float highestRefreshRate = 130;
- doReturn(highestRefreshRate).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- DisplayModeDirector director =
- createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
-
- Sensor lightSensor = createLightSensor();
- SensorManager sensorManager = createMockSensorManager(lightSensor);
- director.start(sensorManager);
-
- setMinRefreshRate(Float.POSITIVE_INFINITY);
-
- Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate,
- /* frameRateHigh= */ Float.POSITIVE_INFINITY);
-
- // The highest refresh rate of the display changes
- highestRefreshRate = 140;
- doReturn(highestRefreshRate).when(() ->
- RefreshRateSettingsUtils.findHighestRefreshRate(mContext, DISPLAY_ID));
- director.getDisplayObserver().onDisplayChanged(DISPLAY_ID);
-
- vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate,
- /* frameRateHigh= */ Float.POSITIVE_INFINITY);
- }
-
- @Test
public void testSensorRegistration() {
// First, configure brightness zones or DMD won't register for sensor data.
final FakeDeviceConfig config = mInjector.getDeviceConfig();
@@ -1877,61 +1813,43 @@
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 0, 0);
- Vote appRequestRefreshRate =
- director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
- assertNotNull(appRequestRefreshRate);
- assertThat(appRequestRefreshRate.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestRefreshRate.refreshRateRanges.physical.max).isPositiveInfinity();
- assertThat(appRequestRefreshRate.refreshRateRanges.render.min).isZero();
- assertThat(appRequestRefreshRate.refreshRateRanges.render.max).isPositiveInfinity();
- assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse();
- assertThat(appRequestRefreshRate.appRequestBaseModeRefreshRate)
- .isWithin(FLOAT_TOLERANCE).of(60);
- assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE);
- assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE);
+ Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
+ assertNotNull(vote);
+ assertThat(vote).isInstanceOf(BaseModeRefreshRateVote.class);
+ BaseModeRefreshRateVote baseModeVote = (BaseModeRefreshRateVote) vote;
+ assertThat(baseModeVote.mAppRequestBaseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
- Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
- assertNotNull(appRequestSize);
- assertThat(appRequestSize.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestSize.refreshRateRanges.physical.max).isPositiveInfinity();
- assertThat(appRequestSize.refreshRateRanges.render.min).isZero();
- assertThat(appRequestSize.refreshRateRanges.render.max).isPositiveInfinity();
- assertThat(appRequestSize.disableRefreshRateSwitching).isFalse();
- assertThat(appRequestSize.appRequestBaseModeRefreshRate).isZero();
- assertThat(appRequestSize.height).isEqualTo(1000);
- assertThat(appRequestSize.width).isEqualTo(1000);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
+ assertNotNull(vote);
+ assertThat(vote).isInstanceOf(SizeVote.class);
+ SizeVote sizeVote = (SizeVote) vote;
+ assertThat(sizeVote.mHeight).isEqualTo(1000);
+ assertThat(sizeVote.mWidth).isEqualTo(1000);
+ assertThat(sizeVote.mMinHeight).isEqualTo(1000);
+ assertThat(sizeVote.mMinWidth).isEqualTo(1000);
- Vote appRequestRefreshRateRange =
- director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
- assertNull(appRequestRefreshRateRange);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
+ assertNull(vote);
director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 90, 0, 0);
- appRequestRefreshRate =
- director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
- assertNotNull(appRequestRefreshRate);
- assertThat(appRequestRefreshRate.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestRefreshRate.refreshRateRanges.physical.max).isPositiveInfinity();
- assertThat(appRequestRefreshRate.refreshRateRanges.render.min).isZero();
- assertThat(appRequestRefreshRate.refreshRateRanges.render.max).isPositiveInfinity();
- assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse();
- assertThat(appRequestRefreshRate.appRequestBaseModeRefreshRate)
- .isWithin(FLOAT_TOLERANCE).of(90);
- assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE);
- assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
+ assertNotNull(vote);
+ assertThat(vote).isInstanceOf(BaseModeRefreshRateVote.class);
+ baseModeVote = (BaseModeRefreshRateVote) vote;
+ assertThat(baseModeVote.mAppRequestBaseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
- appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
- assertNotNull(appRequestSize);
- assertThat(appRequestSize.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestSize.refreshRateRanges.physical.max).isPositiveInfinity();
- assertThat(appRequestSize.refreshRateRanges.render.min).isZero();
- assertThat(appRequestSize.refreshRateRanges.render.max).isPositiveInfinity();
- assertThat(appRequestSize.height).isEqualTo(1000);
- assertThat(appRequestSize.width).isEqualTo(1000);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
+ assertNotNull(vote);
+ assertThat(vote).isInstanceOf(SizeVote.class);
+ sizeVote = (SizeVote) vote;
+ assertThat(sizeVote.mHeight).isEqualTo(1000);
+ assertThat(sizeVote.mWidth).isEqualTo(1000);
+ assertThat(sizeVote.mMinHeight).isEqualTo(1000);
+ assertThat(sizeVote.mMinWidth).isEqualTo(1000);
- appRequestRefreshRateRange =
- director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
- assertNull(appRequestRefreshRateRange);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
+ assertNull(vote);
}
@Test
@@ -1945,17 +1863,12 @@
Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
assertNull(appRequestSize);
- Vote appRequestRefreshRateRange =
- director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
- assertNotNull(appRequestRefreshRateRange);
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isPositiveInfinity();
- assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
- .isWithin(FLOAT_TOLERANCE).of(60);
- assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max).isAtLeast(90);
- assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE);
- assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE);
+ Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
+ assertNotNull(vote);
+ assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+ RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
+ assertThat(renderVote.mMinRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(renderVote.mMaxRefreshRate).isAtLeast(90);
director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 90, 0);
appRequestRefreshRate =
@@ -1965,18 +1878,12 @@
appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
assertNull(appRequestSize);
- appRequestRefreshRateRange =
- director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
- assertNotNull(appRequestRefreshRateRange);
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isPositiveInfinity();
-
- assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
- .isWithin(FLOAT_TOLERANCE).of(90);
- assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max).isAtLeast(90);
- assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE);
- assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
+ assertNotNull(vote);
+ assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+ renderVote = (RefreshRateVote.RenderVote) vote;
+ assertThat(renderVote.mMinRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(renderVote.mMaxRefreshRate).isAtLeast(90);
}
@Test
@@ -1990,18 +1897,12 @@
Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
assertNull(appRequestSize);
- Vote appRequestRefreshRateRange =
- director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
- assertNotNull(appRequestRefreshRateRange);
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isPositiveInfinity();
-
- assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min).isZero();
- assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
- .isWithin(FLOAT_TOLERANCE).of(90);
- assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE);
- assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE);
+ Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
+ assertNotNull(vote);
+ assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+ RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
+ assertThat(renderVote.mMinRefreshRate).isZero();
+ assertThat(renderVote.mMaxRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 0, 60);
appRequestRefreshRate =
@@ -2011,18 +1912,12 @@
appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
assertNull(appRequestSize);
- appRequestRefreshRateRange =
- director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
- assertNotNull(appRequestRefreshRateRange);
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isPositiveInfinity();
-
- assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min).isZero();
- assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
- .isWithin(FLOAT_TOLERANCE).of(60);
- assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE);
- assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
+ assertNotNull(vote);
+ assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+ renderVote = (RefreshRateVote.RenderVote) vote;
+ assertThat(renderVote.mMinRefreshRate).isZero();
+ assertThat(renderVote.mMaxRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
}
@Test
@@ -2046,41 +1941,27 @@
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 90, 90);
- Vote appRequestRefreshRate =
- director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
- assertNotNull(appRequestRefreshRate);
- assertThat(appRequestRefreshRate.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestRefreshRate.refreshRateRanges.physical.max).isPositiveInfinity();
- assertThat(appRequestRefreshRate.refreshRateRanges.render.min).isZero();
- assertThat(appRequestRefreshRate.refreshRateRanges.render.max).isPositiveInfinity();
- assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse();
- assertThat(appRequestRefreshRate.appRequestBaseModeRefreshRate)
- .isWithin(FLOAT_TOLERANCE).of(60);
- assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE);
- assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE);
+ Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
+ assertNotNull(vote);
+ assertThat(vote).isInstanceOf(BaseModeRefreshRateVote.class);
+ BaseModeRefreshRateVote baseModeVote = (BaseModeRefreshRateVote) vote;
+ assertThat(baseModeVote.mAppRequestBaseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
- Vote appRequestSize =
- director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
- assertNotNull(appRequestSize);
- assertThat(appRequestSize.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestSize.refreshRateRanges.physical.max).isPositiveInfinity();
- assertThat(appRequestSize.refreshRateRanges.render.min).isZero();
- assertThat(appRequestSize.refreshRateRanges.render.max).isPositiveInfinity();
- assertThat(appRequestSize.height).isEqualTo(1000);
- assertThat(appRequestSize.width).isEqualTo(1000);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE);
+ assertNotNull(vote);
+ assertThat(vote).isInstanceOf(SizeVote.class);
+ SizeVote sizeVote = (SizeVote) vote;
+ assertThat(sizeVote.mHeight).isEqualTo(1000);
+ assertThat(sizeVote.mWidth).isEqualTo(1000);
+ assertThat(sizeVote.mMinHeight).isEqualTo(1000);
+ assertThat(sizeVote.mMinWidth).isEqualTo(1000);
- Vote appRequestRefreshRateRange =
- director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
- assertNotNull(appRequestRefreshRateRange);
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isPositiveInfinity();
- assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
- .isWithin(FLOAT_TOLERANCE).of(90);
- assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
- .isWithin(FLOAT_TOLERANCE).of(90);
- assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE);
- assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
+ assertNotNull(vote);
+ assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+ RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
+ assertThat(renderVote.mMinRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(renderVote.mMaxRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
}
@Test
@@ -3150,8 +3031,7 @@
captor.getValue().onAuthenticationPossible(DISPLAY_ID, true);
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE);
- assertThat(vote.refreshRateRanges.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
- assertThat(vote.refreshRateRanges.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
+ assertVoteForPhysicalRefreshRate(vote, 90);
}
@Test
@@ -3184,8 +3064,7 @@
captor.getValue().onRequestEnabled(DISPLAY_ID);
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_UDFPS);
- assertThat(vote.refreshRateRanges.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
- assertThat(vote.refreshRateRanges.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
+ assertVoteForPhysicalRefreshRate(vote, 90);
}
@Test
@@ -3257,16 +3136,21 @@
private void assertVoteForPhysicalRefreshRate(Vote vote, float refreshRate) {
assertThat(vote).isNotNull();
- final RefreshRateRange expectedRange = new RefreshRateRange(refreshRate, refreshRate);
- assertThat(vote.refreshRateRanges.physical).isEqualTo(expectedRange);
+ assertThat(vote).isInstanceOf(CombinedVote.class);
+ CombinedVote combinedVote = (CombinedVote) vote;
+ RefreshRateVote.PhysicalVote physicalVote =
+ (RefreshRateVote.PhysicalVote) combinedVote.mVotes.get(0);
+ assertThat(physicalVote.mMinRefreshRate).isWithin(FLOAT_TOLERANCE).of(refreshRate);
+ assertThat(physicalVote.mMaxRefreshRate).isWithin(FLOAT_TOLERANCE).of(refreshRate);
}
private void assertVoteForRenderFrameRateRange(
Vote vote, float frameRateLow, float frameRateHigh) {
assertThat(vote).isNotNull();
- final RefreshRateRange expectedRange =
- new RefreshRateRange(frameRateLow, frameRateHigh);
- assertThat(vote.refreshRateRanges.render).isEqualTo(expectedRange);
+ assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+ RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
+ assertThat(renderVote.mMinRefreshRate).isEqualTo(frameRateLow);
+ assertThat(renderVote.mMaxRefreshRate).isEqualTo(frameRateHigh);
}
public static class FakeDeviceConfig extends FakeDeviceConfigInterface {
@@ -3445,7 +3329,7 @@
public static class FakesInjector implements DisplayModeDirector.Injector {
private final FakeDeviceConfig mDeviceConfig;
private final DisplayInfo mDisplayInfo;
- private final Map<Integer, Display> mDisplays;
+ private final Display mDisplay;
private boolean mDisplayInfoValid = true;
private final DisplayManagerInternal mDisplayManagerInternal;
private final StatusBarManagerInternal mStatusBarManagerInternal;
@@ -3466,8 +3350,7 @@
mDisplayInfo.defaultModeId = MODE_ID;
mDisplayInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID,
800, 600, /* refreshRate= */ 60)};
- mDisplays = Map.of(DISPLAY_ID, createDisplay(DISPLAY_ID),
- DISPLAY_ID_2, createDisplay(DISPLAY_ID_2));
+ mDisplay = createDisplay(DISPLAY_ID);
mDisplayManagerInternal = displayManagerInternal;
mStatusBarManagerInternal = statusBarManagerInternal;
mSensorManagerInternal = sensorManagerInternal;
@@ -3498,12 +3381,12 @@
@Override
public Display getDisplay(int displayId) {
- return mDisplays.get(displayId);
+ return mDisplay;
}
@Override
public Display[] getDisplays() {
- return mDisplays.values().toArray(new Display[0]);
+ return new Display[] { mDisplay };
}
@Override
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode
+
+import androidx.test.filters.SmallTest
+import com.android.server.display.mode.DisplayModeDirector.VoteSummary
+import com.google.common.truth.Truth.assertThat
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val MIN_REFRESH_RATE = 60f
+private const val MAX_REFRESH_RATE = 90f
+
+@SmallTest
+@RunWith(TestParameterInjector::class)
+class PhysicalVoteTest {
+ private lateinit var physicalVote: RefreshRateVote.PhysicalVote
+
+ @Before
+ fun setUp() {
+ physicalVote = RefreshRateVote.PhysicalVote(MIN_REFRESH_RATE, MAX_REFRESH_RATE)
+ }
+
+ @Test
+ fun `updates minPhysicalRefreshRate if summary has less`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.minPhysicalRefreshRate = 45f
+
+ physicalVote.updateSummary(summary)
+
+ assertThat(summary.minPhysicalRefreshRate).isEqualTo(MIN_REFRESH_RATE)
+ }
+
+ @Test
+ fun `does not update minPhysicalRefreshRate if summary has more`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.minPhysicalRefreshRate = 75f
+
+ physicalVote.updateSummary(summary)
+
+ assertThat(summary.minPhysicalRefreshRate).isEqualTo(75f)
+ }
+
+ @Test
+ fun `updates maxPhysicalRefreshRate if summary has more`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.maxPhysicalRefreshRate = 120f
+
+ physicalVote.updateSummary(summary)
+
+ assertThat(summary.maxPhysicalRefreshRate).isEqualTo(MAX_REFRESH_RATE)
+ }
+
+ @Test
+ fun `does not update maxPhysicalRefreshRate if summary has less`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.maxPhysicalRefreshRate = 75f
+
+ physicalVote.updateSummary(summary)
+
+ assertThat(summary.maxPhysicalRefreshRate).isEqualTo(75f)
+ }
+
+ @Test
+ fun `updates maxRenderFrameRate if summary has more`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.maxRenderFrameRate = 120f
+
+ physicalVote.updateSummary(summary)
+
+ assertThat(summary.maxRenderFrameRate).isEqualTo(MAX_REFRESH_RATE)
+ }
+
+ @Test
+ fun `does not update maxRenderFrameRate if summary has less`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.maxRenderFrameRate = 75f
+
+ physicalVote.updateSummary(summary)
+
+ assertThat(summary.maxRenderFrameRate).isEqualTo(75f)
+ }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt
new file mode 100644
index 0000000..868a893
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode
+
+import androidx.test.filters.SmallTest
+import com.android.server.display.mode.DisplayModeDirector.VoteSummary
+import com.google.common.truth.Truth.assertThat
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val MIN_REFRESH_RATE = 60f
+private const val MAX_REFRESH_RATE = 90f
+
+@SmallTest
+@RunWith(TestParameterInjector::class)
+class RenderVoteTest {
+
+ private lateinit var renderVote: RefreshRateVote.RenderVote
+
+ @Before
+ fun setUp() {
+ renderVote = RefreshRateVote.RenderVote(MIN_REFRESH_RATE, MAX_REFRESH_RATE)
+ }
+
+ @Test
+ fun `updates minRenderFrameRate if summary has less`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.minRenderFrameRate = 45f
+
+ renderVote.updateSummary(summary)
+
+ assertThat(summary.minRenderFrameRate).isEqualTo(MIN_REFRESH_RATE)
+ }
+
+ @Test
+ fun `does not update minRenderFrameRate if summary has more`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.minRenderFrameRate = 75f
+
+ renderVote.updateSummary(summary)
+
+ assertThat(summary.minRenderFrameRate).isEqualTo(75f)
+ }
+
+ @Test
+ fun `updates maxRenderFrameRate if summary has more`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.maxRenderFrameRate = 120f
+
+ renderVote.updateSummary(summary)
+
+ assertThat(summary.maxRenderFrameRate).isEqualTo(MAX_REFRESH_RATE)
+ }
+
+ @Test
+ fun `does not update maxRenderFrameRate if summary has less`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.maxRenderFrameRate = 75f
+
+ renderVote.updateSummary(summary)
+
+ assertThat(summary.maxRenderFrameRate).isEqualTo(75f)
+ }
+
+ @Test
+ fun `updates minPhysicalRefreshRate if summary has less`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.minPhysicalRefreshRate = 45f
+
+ renderVote.updateSummary(summary)
+
+ assertThat(summary.minPhysicalRefreshRate).isEqualTo(MIN_REFRESH_RATE)
+ }
+
+ @Test
+ fun `does not update minPhysicalRefreshRate if summary has more`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.minPhysicalRefreshRate = 75f
+
+ renderVote.updateSummary(summary)
+
+ assertThat(summary.minPhysicalRefreshRate).isEqualTo(75f)
+ }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt
new file mode 100644
index 0000000..1c631b0
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.server.display.mode.DisplayModeDirector.VoteSummary
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+
+private const val WIDTH = 800
+private const val HEIGHT = 1600
+private const val MIN_WIDTH = 400
+private const val MIN_HEIGHT = 1200
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SizeVoteTest {
+ private lateinit var sizeVote: SizeVote
+
+ @Before
+ fun setUp() {
+ sizeVote = SizeVote(WIDTH, HEIGHT, MIN_WIDTH, MIN_HEIGHT)
+ }
+
+ @Test
+ fun `updates size if width and height not set and display resolution voting disabled`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ false)
+ summary.width = Vote.INVALID_SIZE
+ summary.height = Vote.INVALID_SIZE
+ summary.minWidth = 100
+ summary.minHeight = 200
+
+ sizeVote.updateSummary(summary)
+
+ assertThat(summary.width).isEqualTo(WIDTH)
+ assertThat(summary.height).isEqualTo(HEIGHT)
+ assertThat(summary.minWidth).isEqualTo(MIN_WIDTH)
+ assertThat(summary.minHeight).isEqualTo(MIN_HEIGHT)
+ }
+
+ @Test
+ fun `does not update size if width set and display resolution voting disabled`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ false)
+ summary.width = 150
+ summary.height = Vote.INVALID_SIZE
+ summary.minWidth = 100
+ summary.minHeight = 200
+
+ sizeVote.updateSummary(summary)
+
+ assertThat(summary.width).isEqualTo(150)
+ assertThat(summary.height).isEqualTo(Vote.INVALID_SIZE)
+ assertThat(summary.minWidth).isEqualTo(100)
+ assertThat(summary.minHeight).isEqualTo(200)
+ }
+
+ @Test
+ fun `does not update size if height set and display resolution voting disabled`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ false)
+ summary.width = Vote.INVALID_SIZE
+ summary.height = 250
+ summary.minWidth = 100
+ summary.minHeight = 200
+
+ sizeVote.updateSummary(summary)
+
+ assertThat(summary.width).isEqualTo(Vote.INVALID_SIZE)
+ assertThat(summary.height).isEqualTo(250)
+ assertThat(summary.minWidth).isEqualTo(100)
+ assertThat(summary.minHeight).isEqualTo(200)
+ }
+
+ @Test
+ fun `updates width if summary has more and display resolution voting enabled`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.width = 850
+
+ sizeVote.updateSummary(summary)
+
+ assertThat(summary.width).isEqualTo(WIDTH)
+ }
+
+ @Test
+ fun `does not update width if summary has less and display resolution voting enabled`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.width = 750
+
+ sizeVote.updateSummary(summary)
+
+ assertThat(summary.width).isEqualTo(750)
+ }
+
+ @Test
+ fun `updates height if summary has more and display resolution voting enabled`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.height = 1650
+
+ sizeVote.updateSummary(summary)
+
+ assertThat(summary.height).isEqualTo(HEIGHT)
+ }
+
+ @Test
+ fun `does not update height if summary has less and display resolution voting enabled`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.height = 1550
+
+ sizeVote.updateSummary(summary)
+
+ assertThat(summary.height).isEqualTo(1550)
+ }
+
+ @Test
+ fun `updates minWidth if summary has less and display resolution voting enabled`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.width = 150
+ summary.minWidth = 350
+
+ sizeVote.updateSummary(summary)
+
+ assertThat(summary.minWidth).isEqualTo(MIN_WIDTH)
+ }
+
+ @Test
+ fun `does not update minWidth if summary has more and display resolution voting enabled`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.width = 150
+ summary.minWidth = 450
+
+ sizeVote.updateSummary(summary)
+
+ assertThat(summary.minWidth).isEqualTo(450)
+ }
+
+ @Test
+ fun `updates minHeight if summary has less and display resolution voting enabled`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.width = 150
+ summary.minHeight = 1150
+
+ sizeVote.updateSummary(summary)
+
+ assertThat(summary.minHeight).isEqualTo(MIN_HEIGHT)
+ }
+
+ @Test
+ fun `does not update minHeight if summary has more and display resolution voting enabled`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.width = 150
+ summary.minHeight = 1250
+
+ sizeVote.updateSummary(summary)
+
+ assertThat(summary.minHeight).isEqualTo(1250)
+ }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
index 9ab6ee5..f677401 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
@@ -17,6 +17,8 @@
package com.android.server.display.mode;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -102,17 +104,21 @@
SparseArray<Vote> displayVotes = mStorage.getVotes(DISPLAY_ID);
assertEquals(1, displayVotes.size());
- Vote vote = displayVotes.get(
- Vote.PRIORITY_SKIN_TEMPERATURE);
- assertEquals(0, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
- assertEquals(60, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
+ Vote vote = displayVotes.get(Vote.PRIORITY_SKIN_TEMPERATURE);
+
+ assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+ RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
+ assertEquals(0, renderVote.mMinRefreshRate, FLOAT_TOLERANCE);
+ assertEquals(60, renderVote.mMaxRefreshRate, FLOAT_TOLERANCE);
SparseArray<Vote> otherDisplayVotes = mStorage.getVotes(DISPLAY_ID_OTHER);
assertEquals(1, otherDisplayVotes.size());
vote = otherDisplayVotes.get(Vote.PRIORITY_SKIN_TEMPERATURE);
- assertEquals(0, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
- assertEquals(60, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
+ assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+ renderVote = (RefreshRateVote.RenderVote) vote;
+ assertEquals(0, renderVote.mMinRefreshRate, FLOAT_TOLERANCE);
+ assertEquals(60, renderVote.mMaxRefreshRate, FLOAT_TOLERANCE);
}
@Test
@@ -167,8 +173,10 @@
SparseArray<Vote> displayVotes = mStorage.getVotes(DISPLAY_ID);
assertEquals(1, displayVotes.size());
Vote vote = displayVotes.get(Vote.PRIORITY_SKIN_TEMPERATURE);
- assertEquals(90, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
- assertEquals(120, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
+ assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+ RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
+ assertEquals(90, renderVote.mMinRefreshRate, FLOAT_TOLERANCE);
+ assertEquals(120, renderVote.mMaxRefreshRate, FLOAT_TOLERANCE);
assertEquals(0, mStorage.getVotes(DISPLAY_ID_OTHER).size());
}
@@ -188,8 +196,10 @@
SparseArray<Vote> displayVotes = mStorage.getVotes(DISPLAY_ID_ADDED);
Vote vote = displayVotes.get(Vote.PRIORITY_SKIN_TEMPERATURE);
- assertEquals(0, vote.refreshRateRanges.render.min, FLOAT_TOLERANCE);
- assertEquals(60, vote.refreshRateRanges.render.max, FLOAT_TOLERANCE);
+ assertThat(vote).isInstanceOf(RefreshRateVote.RenderVote.class);
+ RefreshRateVote.RenderVote renderVote = (RefreshRateVote.RenderVote) vote;
+ assertEquals(0, renderVote.mMinRefreshRate, FLOAT_TOLERANCE);
+ assertEquals(60, renderVote.mMaxRefreshRate, FLOAT_TOLERANCE);
}
@Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
new file mode 100644
index 0000000..cc88003
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.server.display.mode.DisplayModeDirector.VoteSummary
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SupportedModesVoteTest {
+ private val supportedModes = listOf(
+ SupportedModesVote.SupportedMode(60f, 90f ),
+ SupportedModesVote.SupportedMode(120f, 240f )
+ )
+
+ private val otherMode = SupportedModesVote.SupportedMode(120f, 120f )
+
+ private lateinit var supportedModesVote: SupportedModesVote
+
+ @Before
+ fun setUp() {
+ supportedModesVote = SupportedModesVote(supportedModes)
+ }
+
+ @Test
+ fun `adds supported modes if supportedModes in summary is null`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+
+ supportedModesVote.updateSummary(summary)
+
+ assertThat(summary.supportedModes).containsExactlyElementsIn(supportedModes)
+ }
+
+ @Test
+ fun `does not add supported modes if summary has empty list of modes`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.supportedModes = ArrayList()
+
+ supportedModesVote.updateSummary(summary)
+
+ assertThat(summary.supportedModes).isEmpty()
+ }
+
+ @Test
+ fun `filters out modes that does not match vote`() {
+ val summary = VoteSummary(/* isDisplayResolutionRangeVotingEnabled= */ true)
+ summary.supportedModes = ArrayList(listOf(otherMode, supportedModes[0]))
+
+ supportedModesVote.updateSummary(summary)
+
+ assertThat(summary.supportedModes).containsExactly(supportedModes[0])
+ }
+}
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index be29163..1a3a6a3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -46,6 +46,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
@@ -61,6 +62,7 @@
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.IActivityManager;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
@@ -128,6 +130,8 @@
@Mock
private ConnectivityManager mConnectivityManager;
@Mock
+ private ContentResolver mContentResolver;
+ @Mock
private IActivityManager mIActivityManager;
@Mock
private LocationManager mLocationManager;
@@ -332,7 +336,7 @@
anyString(), any(Executor.class),
any(DeviceConfig.OnPropertiesChangedListener.class)));
doAnswer((Answer<DeviceConfig.Properties>) invocationOnMock
- -> mock(DeviceConfig.Properties.class))
+ -> new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_DEVICE_IDLE).build())
.when(() -> DeviceConfig.getProperties(
anyString(), ArgumentMatchers.<String>any()));
when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
@@ -347,6 +351,7 @@
mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
mAnyMotionDetector = new AnyMotionDetectorForTest();
mInjector = new InjectorForTest(getContext());
+ doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
doReturn(mWearModeManagerInternal)
.when(() -> LocalServices.getService(WearModeManagerInternal.class));
@@ -366,7 +371,8 @@
mDeviceIdleController.setLightEnabledForTest(true);
// Get the same Constants object that mDeviceIdleController got.
- mConstants = mInjector.getConstants(mDeviceIdleController);
+ mConstants = mInjector.getConstants(mDeviceIdleController,
+ mInjector.getHandler(mDeviceIdleController), mContentResolver);
final ArgumentCaptor<TelephonyCallback> telephonyCallbackCaptor =
ArgumentCaptor.forClass(TelephonyCallback.class);
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
index 92d1118..4f672f8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
@@ -19,6 +19,7 @@
import static android.app.AppOpsManager.OP_SCHEDULE_EXACT_ALARM;
import static android.app.AppOpsManager.OP_USE_FULL_SCREEN_INTENT;
import static android.app.AppOpsManager._NUM_OP;
+import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -208,8 +209,8 @@
private void assertSameModes(AppOpsCheckingServiceImpl testService, int op1, int op2) {
for (int uid : testService.getUidsWithNonDefaultModes()) {
assertEquals(
- testService.getUidMode(uid, op1),
- testService.getUidMode(uid, op2)
+ testService.getUidMode(uid, PERSISTENT_DEVICE_ID_DEFAULT, op1),
+ testService.getUidMode(uid, PERSISTENT_DEVICE_ID_DEFAULT, op2)
);
}
for (UserPackage pkg : testService.getPackagesWithNonDefaultModes()) {
@@ -275,7 +276,9 @@
} else {
expectedMode = previousMode;
}
- int mode = testService.getUidMode(uid, OP_SCHEDULE_EXACT_ALARM);
+ int mode =
+ testService.getUidMode(
+ uid, PERSISTENT_DEVICE_ID_DEFAULT, OP_SCHEDULE_EXACT_ALARM);
assertEquals(expectedMode, mode);
}
}
@@ -284,7 +287,9 @@
int[] unrelatedUidsInFile = {10225, 10178};
for (int uid : unrelatedUidsInFile) {
- int mode = testService.getUidMode(uid, OP_SCHEDULE_EXACT_ALARM);
+ int mode =
+ testService.getUidMode(
+ uid, PERSISTENT_DEVICE_ID_DEFAULT, OP_SCHEDULE_EXACT_ALARM);
assertEquals(AppOpsManager.opToDefaultMode(OP_SCHEDULE_EXACT_ALARM), mode);
}
}
@@ -331,7 +336,9 @@
final int uid = UserHandle.getUid(userId, appId);
final int expectedMode = AppOpsManager.opToDefaultMode(OP_USE_FULL_SCREEN_INTENT);
synchronized (testService) {
- int mode = testService.getUidMode(uid, OP_USE_FULL_SCREEN_INTENT);
+ int mode =
+ testService.getUidMode(
+ uid, PERSISTENT_DEVICE_ID_DEFAULT, OP_USE_FULL_SCREEN_INTENT);
assertEquals(expectedMode, mode);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java
new file mode 100644
index 0000000..c5e6824
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.gnss;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.location.GnssCapabilities;
+import android.location.LocationManager;
+import android.location.LocationManagerInternal;
+import android.location.flags.Flags;
+import android.location.provider.ProviderRequest;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.modules.utils.testing.ExtendedMockitoRule;
+import com.android.server.LocalServices;
+import com.android.server.location.gnss.hal.FakeGnssHal;
+import com.android.server.location.gnss.hal.GnssNative;
+import com.android.server.location.injector.Injector;
+import com.android.server.location.injector.TestInjector;
+import com.android.server.timedetector.TimeDetectorInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.quality.Strictness;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+@Presubmit
+@androidx.test.filters.SmallTest
+@RunWith(AndroidJUnit4.class)
+public class GnssLocationProviderTest {
+
+ @Rule
+ public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+ .setStrictness(Strictness.WARN)
+ .mockStatic(Settings.Global.class)
+ .build();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ private @Mock Context mContext;
+ private @Mock LocationManagerInternal mLocationManagerInternal;
+ private @Mock LocationManager mLocationManager;
+ private @Mock TimeDetectorInternal mTimeDetectorInternal;
+ private @Mock GnssConfiguration mMockConfiguration;
+ private @Mock GnssMetrics mGnssMetrics;
+ private @Mock PowerManager mPowerManager;
+ private @Mock TelephonyManager mTelephonyManager;
+ private @Mock AppOpsManager mAppOpsManager;
+ private @Mock AlarmManager mAlarmManager;
+ private @Mock PowerManager.WakeLock mWakeLock;
+ private @Mock ContentResolver mContentResolver;
+ private @Mock UserManager mUserManager;
+ private @Mock UserHandle mUserHandle;
+ private Set<UserHandle> mUserHandleSet = new HashSet<>();
+
+ private GnssNative mGnssNative;
+
+ private GnssLocationProvider mTestProvider;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ doReturn("mypackage").when(mContext).getPackageName();
+ doReturn("attribution").when(mContext).getAttributionTag();
+ doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
+ doReturn(mPowerManager).when(mContext).getSystemService("power");
+ doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
+ doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
+ doReturn(mAlarmManager).when(mContext).getSystemService(Context.ALARM_SERVICE);
+ doReturn(mLocationManager).when(mContext).getSystemService(LocationManager.class);
+ doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
+ mUserHandleSet.add(mUserHandle);
+ doReturn(true).when(mLocationManager).isLocationEnabledForUser(eq(mUserHandle));
+ doReturn(mUserHandleSet).when(mUserManager).getVisibleUsers();
+ doReturn(mContentResolver).when(mContext).getContentResolver();
+ doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
+ LocalServices.addService(LocationManagerInternal.class, mLocationManagerInternal);
+ LocalServices.addService(TimeDetectorInternal.class, mTimeDetectorInternal);
+ FakeGnssHal fakeGnssHal = new FakeGnssHal();
+ GnssNative.setGnssHalForTest(fakeGnssHal);
+ Injector injector = new TestInjector(mContext);
+ mGnssNative = spy(Objects.requireNonNull(
+ GnssNative.create(injector, mMockConfiguration)));
+ doReturn(true).when(mGnssNative).init();
+ GnssCapabilities gnssCapabilities = new GnssCapabilities.Builder().setHasScheduling(
+ true).build();
+ doReturn(gnssCapabilities).when(mGnssNative).getCapabilities();
+
+ mTestProvider = new GnssLocationProvider(mContext, mGnssNative, mGnssMetrics);
+ mGnssNative.register();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(LocationManagerInternal.class);
+ LocalServices.removeServiceForTest(TimeDetectorInternal.class);
+ }
+
+ @Test
+ public void testStartNavigating() {
+ ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
+ 0).build();
+
+ mTestProvider.onSetRequest(providerRequest);
+ verify(mGnssNative).start();
+ }
+
+ @Test
+ public void testUpdateRequirements_sameRequest() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_GNSS_CALL_STOP_BEFORE_SET_POSITION_MODE);
+ ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
+ 0).build();
+
+ mTestProvider.onSetRequest(providerRequest);
+ verify(mGnssNative).start();
+
+ // set the same request
+ mTestProvider.onSetRequest(providerRequest);
+ verify(mGnssNative, never()).stop();
+ verify(mGnssNative, times(1)).start();
+ }
+
+ @Test
+ public void testUpdateRequirements_differentRequest() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_GNSS_CALL_STOP_BEFORE_SET_POSITION_MODE);
+ ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
+ 0).build();
+
+ mTestProvider.onSetRequest(providerRequest);
+ verify(mGnssNative).start();
+
+ // set a different request
+ providerRequest = new ProviderRequest.Builder().setIntervalMillis(2000).build();
+ mTestProvider.onSetRequest(providerRequest);
+ verify(mGnssNative).stop();
+ verify(mGnssNative, times(2)).start();
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
index 931b38d..f8fe97e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
@@ -22,12 +22,14 @@
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.content.pm.UserInfo
import android.os.Build
+import android.os.UserHandle
import android.os.UserHandle.USER_SYSTEM
import android.util.Log
import com.android.server.testutils.any
import com.android.server.testutils.spy
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertFalse
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -177,4 +179,13 @@
assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_INTERNAL_ERROR)
}
+
+ @Test
+ fun deletePackageLIFWithNonExistantPackage_isFalse() {
+ val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+ mock(BroadcastHelper::class.java))
+ val result = dph.deletePackageLIF("a.nonexistent.package", UserHandle.of(USER_SYSTEM), true,
+ intArrayOf(0), 0, PackageRemovedInfo(), true)
+ assertFalse(result)
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index be33b1b..46806f3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -55,6 +55,8 @@
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder
import com.android.internal.R
+import com.android.internal.pm.parsing.pkg.ParsedPackage
+import com.android.internal.pm.pkg.parsing.ParsingPackage
import com.android.server.LocalManagerRegistry
import com.android.server.LocalServices
import com.android.server.LockGuard
@@ -66,10 +68,8 @@
import com.android.server.pm.dex.DynamicCodeLogger
import com.android.server.pm.parsing.PackageParser2
import com.android.server.pm.parsing.pkg.PackageImpl
-import com.android.server.pm.parsing.pkg.ParsedPackage
import com.android.server.pm.permission.PermissionManagerServiceInternal
import com.android.server.pm.pkg.AndroidPackage
-import com.android.server.pm.pkg.parsing.ParsingPackage
import com.android.server.pm.pkg.parsing.ParsingPackageUtils
import com.android.server.pm.resolution.ComponentResolver
import com.android.server.pm.snapshot.PackageDataSnapshot
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
index b8f726b..e685c3f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
@@ -25,13 +25,13 @@
import android.os.UserHandle
import android.util.ArrayMap
import android.util.PackageUtils
+import com.android.internal.pm.parsing.pkg.ParsedPackage
import com.android.server.SystemConfig.SharedLibraryEntry
import com.android.server.compat.PlatformCompat
import com.android.server.extendedtestutils.wheneverStatic
import com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.parsing.pkg.PackageImpl
-import com.android.server.pm.parsing.pkg.ParsedPackage
import com.android.server.testutils.any
import com.android.server.testutils.eq
import com.android.server.testutils.mock
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
index 2003d04..ca7de7c 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
@@ -90,6 +90,10 @@
private AggregatedPowerStats prepareAggregatePowerStats() {
AggregatedPowerStats stats = new AggregatedPowerStats(mAggregatedPowerStatsConfig);
+
+ PowerStats ps = new PowerStats(mPowerComponentDescriptor);
+ stats.addPowerStats(ps, 0);
+
stats.addClockUpdate(1000, 456);
stats.setDuration(789);
@@ -100,7 +104,6 @@
stats.setUidState(APP_2, AggregatedPowerStatsConfig.STATE_PROCESS_STATE,
BatteryConsumer.PROCESS_STATE_FOREGROUND, 2000);
- PowerStats ps = new PowerStats(mPowerComponentDescriptor);
ps.stats[0] = 100;
ps.stats[1] = 987;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
index 663af5d..9c2834d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
@@ -215,7 +215,7 @@
public class TestBatteryStatsImpl extends BatteryStatsImpl {
public TestBatteryStatsImpl(Context context) {
- super(Clock.SYSTEM_CLOCK, null);
+ super(Clock.SYSTEM_CLOCK, null, null, null);
mPowerProfile = new PowerProfile(context, true /* forTest */);
SparseArray<int[]> cpusByPolicy = new SparseArray<>();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
index 55ffa1a..f9f32b2 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
@@ -37,6 +37,8 @@
import static org.mockito.Mockito.when;
import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Looper;
import android.os.UserHandle;
import android.util.SparseArray;
import android.util.SparseLongArray;
@@ -97,6 +99,7 @@
BatteryStatsImpl.UserInfoProvider mUserInfoProvider;
private MockClock mClocks;
+ private PowerStatsUidResolver mPowerStatsUidResolver;
private MockBatteryStatsImpl mBatteryStatsImpl;
private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
@@ -105,7 +108,9 @@
MockitoAnnotations.initMocks(this);
mClocks = new MockClock();
- mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks)
+ Handler handler = new Handler(Looper.getMainLooper());
+ mPowerStatsUidResolver = new PowerStatsUidResolver();
+ mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks, null, handler, mPowerStatsUidResolver)
.setTestCpuScalingPolicies()
.setKernelCpuUidUserSysTimeReader(mCpuUidUserSysTimeReader)
.setKernelCpuUidFreqTimeReader(mCpuUidFreqTimeReader)
@@ -374,7 +379,7 @@
// PRECONDITIONS
final int ownerUid = UserHandle.getUid(testUserId, FIRST_APPLICATION_UID + 42);
- mBatteryStatsImpl.addIsolatedUidLocked(isolatedUid, ownerUid);
+ mPowerStatsUidResolver.noteIsolatedUidAdded(isolatedUid, ownerUid);
final long[][] deltasUs = {
{9379, 3332409833484L}, {493247, 723234}, {3247819, 123348}
};
@@ -965,7 +970,7 @@
// PRECONDITIONS
final int ownerUid = UserHandle.getUid(testUserId, FIRST_APPLICATION_UID + 42);
- mBatteryStatsImpl.addIsolatedUidLocked(isolatedUid, ownerUid);
+ mPowerStatsUidResolver.noteIsolatedUidAdded(isolatedUid, ownerUid);
final long[][] deltasMs = {
{3, 12, 55, 100, 32},
{32483274, 232349349, 123, 2398, 0},
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
index 5ebc6ca..8d51592 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
@@ -39,14 +39,22 @@
import android.app.ActivityManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.UidTraffic;
+import android.content.Context;
+import android.os.BatteryConsumer;
+import android.os.BatteryManager;
import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
import android.os.BluetoothBatteryStats;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Parcel;
import android.os.WakeLockStats;
import android.os.WorkSource;
import android.util.SparseArray;
import android.view.Display;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -65,6 +73,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.File;
+import java.time.Instant;
import java.util.List;
@LargeTest
@@ -93,6 +103,11 @@
private final MockClock mMockClock = new MockClock();
private MockBatteryStatsImpl mBatteryStatsImpl;
+ private Handler mHandler;
+ private PowerStatsStore mPowerStatsStore;
+ private BatteryUsageStatsProvider mBatteryUsageStatsProvider;
+ @Mock
+ private PowerStatsExporter mPowerStatsExporter;
@Before
public void setUp() {
@@ -103,12 +118,23 @@
when(mKernelSingleUidTimeReader.singleUidCpuTimesAvailable()).thenReturn(true);
when(mKernelWakelockReader.readKernelWakelockStats(
any(KernelWakelockStats.class))).thenReturn(mKernelWakelockStats);
- mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock)
+ HandlerThread bgThread = new HandlerThread("bg thread");
+ bgThread.start();
+ mHandler = new Handler(bgThread.getLooper());
+ mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock, null, mHandler)
.setPowerProfile(mPowerProfile)
.setCpuScalingPolicies(mCpuScalingPolicies)
.setKernelCpuUidFreqTimeReader(mKernelUidCpuFreqTimeReader)
.setKernelSingleUidTimeReader(mKernelSingleUidTimeReader)
.setKernelWakelockReader(mKernelWakelockReader);
+
+ final Context context = InstrumentationRegistry.getContext();
+ File systemDir = context.getCacheDir();
+ mPowerStatsStore = new PowerStatsStore(systemDir, mHandler,
+ new AggregatedPowerStatsConfig());
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mPowerStatsExporter,
+ mPowerProfile, mBatteryStatsImpl.getCpuScalingPolicies(), mPowerStatsStore,
+ mMockClock);
}
@Test
@@ -754,4 +780,76 @@
parcel.recycle();
return info;
}
+
+ @Test
+ public void storeBatteryUsageStatsOnReset() {
+ mBatteryStatsImpl.forceRecordAllHistory();
+
+ mMockClock.currentTime = Instant.parse("2023-01-02T03:04:05.00Z").toEpochMilli();
+ mMockClock.realtime = 7654321;
+
+ synchronized (mBatteryStatsImpl) {
+ mBatteryStatsImpl.setOnBatteryLocked(mMockClock.realtime, mMockClock.uptime, true,
+ BatteryManager.BATTERY_STATUS_DISCHARGING, 50, 0);
+ // Will not save to PowerStatsStore because "saveBatteryUsageStatsOnReset" has not
+ // been called yet.
+ mBatteryStatsImpl.resetAllStatsAndHistoryLocked(
+ BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+ }
+
+ assertThat(mPowerStatsStore.getTableOfContents()).isEmpty();
+
+ mBatteryStatsImpl.saveBatteryUsageStatsOnReset(mBatteryUsageStatsProvider,
+ mPowerStatsStore);
+
+ synchronized (mBatteryStatsImpl) {
+ mBatteryStatsImpl.noteFlashlightOnLocked(42, mMockClock.realtime, mMockClock.uptime);
+ }
+
+ mMockClock.realtime += 60000;
+ mMockClock.currentTime += 60000;
+
+ synchronized (mBatteryStatsImpl) {
+ mBatteryStatsImpl.noteFlashlightOffLocked(42, mMockClock.realtime, mMockClock.uptime);
+ }
+
+ mMockClock.realtime += 60000;
+ mMockClock.currentTime += 60000;
+
+ // Battery stats reset should have the side-effect of saving accumulated battery usage stats
+ synchronized (mBatteryStatsImpl) {
+ mBatteryStatsImpl.resetAllStatsAndHistoryLocked(
+ BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+ }
+
+ // Await completion
+ ConditionVariable done = new ConditionVariable();
+ mHandler.post(done::open);
+ done.block();
+
+ List<PowerStatsSpan.Metadata> contents = mPowerStatsStore.getTableOfContents();
+ assertThat(contents).hasSize(1);
+
+ PowerStatsSpan.Metadata metadata = contents.get(0);
+
+ PowerStatsSpan span = mPowerStatsStore.loadPowerStatsSpan(metadata.getId(),
+ BatteryUsageStatsSection.TYPE);
+ assertThat(span).isNotNull();
+
+ List<PowerStatsSpan.TimeFrame> timeFrames = span.getMetadata().getTimeFrames();
+ assertThat(timeFrames).hasSize(1);
+ assertThat(timeFrames.get(0).startMonotonicTime).isEqualTo(7654321);
+ assertThat(timeFrames.get(0).duration).isEqualTo(120000);
+
+ List<PowerStatsSpan.Section> sections = span.getSections();
+ assertThat(sections).hasSize(1);
+
+ PowerStatsSpan.Section section = sections.get(0);
+ assertThat(section.getType()).isEqualTo(BatteryUsageStatsSection.TYPE);
+ BatteryUsageStats bus = ((BatteryUsageStatsSection) section).getBatteryUsageStats();
+ assertThat(bus.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+ .isEqualTo(60000);
+ }
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
index 7ef1a3f..24c67f8 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
@@ -35,6 +35,8 @@
import android.os.BatteryStats;
import android.os.BatteryStats.HistoryItem;
import android.os.BatteryStats.Uid.Sensor;
+import android.os.Handler;
+import android.os.Looper;
import android.os.Process;
import android.os.UserHandle;
import android.os.WorkSource;
@@ -155,7 +157,9 @@
@SmallTest
public void testNoteStartWakeLocked_isolatedUid() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
+ new Handler(Looper.getMainLooper()), uidResolver);
int pid = 10;
String name = "name";
@@ -165,7 +169,7 @@
isolatedWorkChain.addNode(ISOLATED_UID, name);
// Map ISOLATED_UID to UID.
- bi.addIsolatedUidLocked(ISOLATED_UID, UID);
+ uidResolver.noteIsolatedUidAdded(ISOLATED_UID, UID);
bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
@@ -195,7 +199,9 @@
@SmallTest
public void testNoteStartWakeLocked_isolatedUidRace() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+ PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
+ new Handler(Looper.getMainLooper()), uidResolver);
int pid = 10;
String name = "name";
@@ -205,7 +211,7 @@
isolatedWorkChain.addNode(ISOLATED_UID, name);
// Map ISOLATED_UID to UID.
- bi.addIsolatedUidLocked(ISOLATED_UID, UID);
+ uidResolver.noteIsolatedUidAdded(ISOLATED_UID, UID);
bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
@@ -216,7 +222,7 @@
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
clocks.realtime = clocks.uptime = 150;
- bi.maybeRemoveIsolatedUidLocked(ISOLATED_UID, clocks.realtime, clocks.uptime);
+ uidResolver.releaseIsolatedUid(ISOLATED_UID);
clocks.realtime = clocks.uptime = 220;
bi.noteStopWakeLocked(ISOLATED_UID, pid, isolatedWorkChain, name, historyName,
@@ -237,8 +243,9 @@
@SmallTest
public void testNoteLongPartialWakelockStart_isolatedUid() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
-
+ PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
+ new Handler(Looper.getMainLooper()), uidResolver);
bi.setRecordAllHistoryLocked(true);
bi.forceRecordAllHistory();
@@ -251,7 +258,7 @@
isolatedWorkChain.addNode(ISOLATED_UID, name);
// Map ISOLATED_UID to UID.
- bi.addIsolatedUidLocked(ISOLATED_UID, UID);
+ uidResolver.noteIsolatedUidAdded(ISOLATED_UID, UID);
bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
@@ -290,8 +297,9 @@
@SmallTest
public void testNoteLongPartialWakelockStart_isolatedUidRace() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
-
+ PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
+ new Handler(Looper.getMainLooper()), uidResolver);
bi.setRecordAllHistoryLocked(true);
bi.forceRecordAllHistory();
@@ -304,7 +312,7 @@
isolatedWorkChain.addNode(ISOLATED_UID, name);
// Map ISOLATED_UID to UID.
- bi.addIsolatedUidLocked(ISOLATED_UID, UID);
+ uidResolver.noteIsolatedUidAdded(ISOLATED_UID, UID);
bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
@@ -314,7 +322,7 @@
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
clocks.realtime = clocks.uptime = 150;
- bi.maybeRemoveIsolatedUidLocked(ISOLATED_UID, clocks.realtime, clocks.uptime);
+ uidResolver.releaseIsolatedUid(ISOLATED_UID);
clocks.realtime = clocks.uptime = 220;
bi.noteLongPartialWakelockFinish(name, historyName, ISOLATED_UID);
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 2bd8956..2e0ba00 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -28,9 +28,7 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
+import android.os.ConditionVariable;
import android.os.Parcel;
import android.os.Process;
import android.os.UidBatteryConsumer;
@@ -40,7 +38,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BatteryStatsHistoryIterator;
-import com.android.internal.os.MonotonicClock;
import com.android.internal.os.PowerProfile;
import org.junit.Rule;
@@ -72,10 +69,11 @@
BatteryStatsImpl batteryStats = prepareBatteryStats();
Context context = InstrumentationRegistry.getContext();
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, batteryStats);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null,
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
final BatteryUsageStats batteryUsageStats =
- provider.getBatteryUsageStats(BatteryUsageStatsQuery.DEFAULT);
+ provider.getBatteryUsageStats(batteryStats, BatteryUsageStatsQuery.DEFAULT);
final List<UidBatteryConsumer> uidBatteryConsumers =
batteryUsageStats.getUidBatteryConsumers();
@@ -108,10 +106,11 @@
BatteryStatsImpl batteryStats = prepareBatteryStats();
Context context = InstrumentationRegistry.getContext();
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, batteryStats);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null,
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
final BatteryUsageStats batteryUsageStats =
- provider.getBatteryUsageStats(
+ provider.getBatteryUsageStats(batteryStats,
new BatteryUsageStatsQuery.Builder()
.includePowerComponents(
new int[]{BatteryConsumer.POWER_COMPONENT_AUDIO})
@@ -213,10 +212,11 @@
}
Context context = InstrumentationRegistry.getContext();
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, batteryStats);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null,
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
final BatteryUsageStats batteryUsageStats =
- provider.getBatteryUsageStats(
+ provider.getBatteryUsageStats(batteryStats,
new BatteryUsageStatsQuery.Builder().includeBatteryHistory().build());
Parcel in = Parcel.obtain();
@@ -301,11 +301,11 @@
}
Context context = InstrumentationRegistry.getContext();
- BatteryUsageStatsProvider
- provider = new BatteryUsageStatsProvider(context, batteryStats);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null,
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
final BatteryUsageStats batteryUsageStats =
- provider.getBatteryUsageStats(
+ provider.getBatteryUsageStats(batteryStats,
new BatteryUsageStatsQuery.Builder().includeBatteryHistory().build());
Parcel parcel = Parcel.obtain();
@@ -361,27 +361,22 @@
@Test
public void shouldUpdateStats() {
- Context context = InstrumentationRegistry.getContext();
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
- mStatsRule.getBatteryStats());
-
final List<BatteryUsageStatsQuery> queries = List.of(
new BatteryUsageStatsQuery.Builder().setMaxStatsAgeMs(1000).build(),
new BatteryUsageStatsQuery.Builder().setMaxStatsAgeMs(2000).build()
);
- mStatsRule.setTime(10500, 0);
- assertThat(provider.shouldUpdateStats(queries, 10000)).isFalse();
+ assertThat(BatteryUsageStatsProvider.shouldUpdateStats(queries,
+ 10500, 10000)).isFalse();
- mStatsRule.setTime(11500, 0);
- assertThat(provider.shouldUpdateStats(queries, 10000)).isTrue();
+ assertThat(BatteryUsageStatsProvider.shouldUpdateStats(queries,
+ 11500, 10000)).isTrue();
}
@Test
public void testAggregateBatteryStats() {
Context context = InstrumentationRegistry.getContext();
BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
- MonotonicClock monotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
setTime(5 * MINUTE_IN_MS);
synchronized (batteryStats) {
@@ -390,14 +385,17 @@
PowerStatsStore powerStatsStore = new PowerStatsStore(
new File(context.getCacheDir(), "BatteryUsageStatsProviderTest"),
- new TestHandler(), null);
+ mStatsRule.getHandler(), null);
+ powerStatsStore.reset();
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
- batteryStats, powerStatsStore);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null,
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), powerStatsStore,
+ mMockClock);
- batteryStats.setBatteryResetListener(reason ->
- powerStatsStore.storeBatteryUsageStats(monotonicClock.monotonicTime(),
- provider.getBatteryUsageStats(BatteryUsageStatsQuery.DEFAULT)));
+ batteryStats.saveBatteryUsageStatsOnReset(provider, powerStatsStore);
+ synchronized (batteryStats) {
+ batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+ }
synchronized (batteryStats) {
batteryStats.noteFlashlightOnLocked(APP_UID,
@@ -450,11 +448,16 @@
}
setTime(95 * MINUTE_IN_MS);
+ // Await completion
+ ConditionVariable done = new ConditionVariable();
+ mStatsRule.getHandler().post(done::open);
+ done.block();
+
// Include the first and the second snapshot, but not the third or current
BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
.aggregateSnapshots(20 * MINUTE_IN_MS, 60 * MINUTE_IN_MS)
.build();
- final BatteryUsageStats stats = provider.getBatteryUsageStats(query);
+ final BatteryUsageStats stats = provider.getBatteryUsageStats(batteryStats, query);
assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
assertThat(stats.getStatsEndTimestamp()).isEqualTo(55 * MINUTE_IN_MS);
@@ -508,30 +511,19 @@
when(powerStatsStore.loadPowerStatsSpan(1, BatteryUsageStatsSection.TYPE))
.thenReturn(span1);
- BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
- batteryStats, powerStatsStore);
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, null,
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), powerStatsStore,
+ mMockClock);
BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
.aggregateSnapshots(0, 3000)
.build();
- final BatteryUsageStats stats = provider.getBatteryUsageStats(query);
+ final BatteryUsageStats stats = provider.getBatteryUsageStats(batteryStats, query);
assertThat(stats.getCustomPowerComponentNames())
.isEqualTo(batteryStats.getCustomEnergyConsumerNames());
assertThat(stats.getStatsDuration()).isEqualTo(1234);
}
- private static class TestHandler extends Handler {
- TestHandler() {
- super(Looper.getMainLooper());
- }
-
- @Override
- public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
- msg.getCallback().run();
- return true;
- }
- }
-
private static final Random sRandom = new Random();
/**
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index 0b10954..e61dd0b 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -28,6 +28,8 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.UidBatteryConsumer;
import android.os.UserBatteryConsumer;
import android.util.SparseArray;
@@ -57,6 +59,7 @@
private final PowerProfile mPowerProfile;
private final MockClock mMockClock = new MockClock();
private final MockBatteryStatsImpl mBatteryStats;
+ private final Handler mHandler;
private BatteryUsageStats mBatteryUsageStats;
private boolean mScreenOn;
@@ -73,10 +76,13 @@
}
public BatteryUsageStatsRule(long currentTime, File historyDir) {
+ HandlerThread bgThread = new HandlerThread("bg thread");
+ bgThread.start();
+ mHandler = new Handler(bgThread.getLooper());
mContext = InstrumentationRegistry.getContext();
mPowerProfile = spy(new PowerProfile(mContext, true /* forTest */));
mMockClock.currentTime = currentTime;
- mBatteryStats = new MockBatteryStatsImpl(mMockClock, historyDir);
+ mBatteryStats = new MockBatteryStatsImpl(mMockClock, historyDir, mHandler);
mBatteryStats.setPowerProfile(mPowerProfile);
mCpusByPolicy.put(0, new int[]{0, 1, 2, 3});
@@ -92,6 +98,10 @@
return mMockClock;
}
+ public Handler getHandler() {
+ return mHandler;
+ }
+
public BatteryUsageStatsRule setTestPowerProfile(@XmlRes int xmlId) {
mPowerProfile.forceInitForTesting(mContext, xmlId);
return this;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java
index 79084cc..8ca4ff6 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java
@@ -192,7 +192,7 @@
private static class MockPowerComponentAggregatedPowerStats extends
PowerComponentAggregatedPowerStats {
- private final CpuPowerStatsCollector.StatsArrayLayout mStatsLayout;
+ private final CpuPowerStatsCollector.CpuStatsArrayLayout mStatsLayout;
private final PowerStats.Descriptor mDescriptor;
private HashMap<String, long[]> mDeviceStats = new HashMap<>();
private HashMap<String, long[]> mUidStats = new HashMap<>();
@@ -203,10 +203,10 @@
MockPowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config,
boolean useEnergyConsumers) {
super(config);
- mStatsLayout = new CpuPowerStatsCollector.StatsArrayLayout();
+ mStatsLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout();
mStatsLayout.addDeviceSectionCpuTimeByScalingStep(3);
mStatsLayout.addDeviceSectionCpuTimeByCluster(2);
- mStatsLayout.addDeviceSectionUptime();
+ mStatsLayout.addDeviceSectionUsageDuration();
if (useEnergyConsumers) {
mStatsLayout.addDeviceSectionEnergyConsumers(2);
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
index bc211df..64d5414 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -56,6 +57,9 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class CpuPowerStatsCollectorTest {
+ private static final int ISOLATED_UID = 99123;
+ private static final int UID_1 = 42;
+ private static final int UID_2 = 99;
private Context mContext;
private final MockClock mMockClock = new MockClock();
private final HandlerThread mHandlerThread = new HandlerThread("test");
@@ -63,6 +67,8 @@
private PowerStats mCollectedStats;
private PowerProfile mPowerProfile;
@Mock
+ private PowerStatsUidResolver mUidResolver;
+ @Mock
private CpuPowerStatsCollector.KernelCpuStatsReader mMockKernelCpuStatsReader;
@Mock
private PowerStatsInternal mPowerStatsInternal;
@@ -76,6 +82,14 @@
mHandlerThread.start();
mHandler = mHandlerThread.getThreadHandler();
when(mMockKernelCpuStatsReader.nativeIsSupportedFeature()).thenReturn(true);
+ when(mUidResolver.mapUid(anyInt())).thenAnswer(invocation -> {
+ int uid = invocation.getArgument(0);
+ if (uid == ISOLATED_UID) {
+ return UID_2;
+ } else {
+ return uid;
+ }
+ });
}
@Test
@@ -156,14 +170,14 @@
assertThat(descriptor.name).isEqualTo("cpu");
assertThat(descriptor.statsArrayLength).isEqualTo(13);
assertThat(descriptor.uidStatsArrayLength).isEqualTo(5);
- CpuPowerStatsCollector.StatsArrayLayout layout =
- new CpuPowerStatsCollector.StatsArrayLayout();
+ CpuPowerStatsCollector.CpuStatsArrayLayout layout =
+ new CpuPowerStatsCollector.CpuStatsArrayLayout();
layout.fromExtras(descriptor.extras);
long[] deviceStats = new long[descriptor.statsArrayLength];
layout.setTimeByScalingStep(deviceStats, 2, 42);
layout.setConsumedEnergy(deviceStats, 1, 43);
- layout.setUptime(deviceStats, 44);
+ layout.setUsageDuration(deviceStats, 44);
layout.setDevicePowerEstimate(deviceStats, 45);
long[] uidStats = new long[descriptor.uidStatsArrayLength];
@@ -173,10 +187,10 @@
assertThat(layout.getCpuScalingStepCount()).isEqualTo(7);
assertThat(layout.getTimeByScalingStep(deviceStats, 2)).isEqualTo(42);
- assertThat(layout.getCpuClusterEnergyConsumerCount()).isEqualTo(2);
+ assertThat(layout.getEnergyConsumerCount()).isEqualTo(2);
assertThat(layout.getConsumedEnergy(deviceStats, 1)).isEqualTo(43);
- assertThat(layout.getUptime(deviceStats)).isEqualTo(44);
+ assertThat(layout.getUsageDuration(deviceStats)).isEqualTo(44);
assertThat(layout.getDevicePowerEstimate(deviceStats)).isEqualTo(45);
@@ -195,14 +209,15 @@
mockEnergyConsumers();
CpuPowerStatsCollector collector = createCollector(8, 0);
- CpuPowerStatsCollector.StatsArrayLayout layout =
- new CpuPowerStatsCollector.StatsArrayLayout();
+ CpuPowerStatsCollector.CpuStatsArrayLayout layout =
+ new CpuPowerStatsCollector.CpuStatsArrayLayout();
layout.fromExtras(collector.getPowerStatsDescriptor().extras);
mockKernelCpuStats(new long[]{1111, 2222, 3333},
new SparseArray<>() {{
- put(42, new long[]{100, 200});
- put(99, new long[]{300, 600});
+ put(UID_1, new long[]{100, 200});
+ put(UID_2, new long[]{100, 150});
+ put(ISOLATED_UID, new long[]{200, 450});
}}, 0, 1234);
mMockClock.uptime = 1000;
@@ -219,19 +234,19 @@
assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 0)).isEqualTo(0);
assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 1)).isEqualTo(0);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 0))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_1), 0))
.isEqualTo(100);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 1))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_1), 1))
.isEqualTo(200);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 0))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_2), 0))
.isEqualTo(300);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 1))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_2), 1))
.isEqualTo(600);
mockKernelCpuStats(new long[]{5555, 4444, 3333},
new SparseArray<>() {{
- put(42, new long[]{123, 234});
- put(99, new long[]{345, 678});
+ put(UID_1, new long[]{123, 234});
+ put(ISOLATED_UID, new long[]{245, 528});
}}, 1234, 3421);
mMockClock.uptime = 2000;
@@ -249,13 +264,13 @@
// 700 * 1000 / 3500
assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 1)).isEqualTo(200);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 0))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_1), 0))
.isEqualTo(23);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 1))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_1), 1))
.isEqualTo(34);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 0))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_2), 0))
.isEqualTo(45);
- assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 1))
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(UID_2), 1))
.isEqualTo(78);
}
@@ -282,9 +297,9 @@
private CpuPowerStatsCollector createCollector(int defaultCpuPowerBrackets,
int defaultCpuPowerBracketsPerEnergyConsumer) {
CpuPowerStatsCollector collector = new CpuPowerStatsCollector(mCpuScalingPolicies,
- mPowerProfile, mHandler, mMockKernelCpuStatsReader, () -> mPowerStatsInternal,
- () -> 3500, 60_000, mMockClock, defaultCpuPowerBrackets,
- defaultCpuPowerBracketsPerEnergyConsumer);
+ mPowerProfile, mHandler, mMockKernelCpuStatsReader, mUidResolver,
+ () -> mPowerStatsInternal, () -> 3500, 60_000, mMockClock,
+ defaultCpuPowerBrackets, defaultCpuPowerBracketsPerEnergyConsumer);
collector.addConsumer(stats -> mCollectedStats = stats);
collector.setEnabled(true);
return collector;
@@ -375,8 +390,8 @@
}
private static int[] getScalingStepToPowerBracketMap(CpuPowerStatsCollector collector) {
- CpuPowerStatsCollector.StatsArrayLayout layout =
- new CpuPowerStatsCollector.StatsArrayLayout();
+ CpuPowerStatsCollector.CpuStatsArrayLayout layout =
+ new CpuPowerStatsCollector.CpuStatsArrayLayout();
layout.fromExtras(collector.getPowerStatsDescriptor().extras);
return layout.getScalingStepToPowerBracketMap();
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index 4150972a..fb71ac8 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -61,16 +61,23 @@
}
MockBatteryStatsImpl(Clock clock, File historyDirectory) {
- super(clock, historyDirectory);
+ this(clock, historyDirectory, new Handler(Looper.getMainLooper()));
+ }
+
+ MockBatteryStatsImpl(Clock clock, File historyDirectory, Handler handler) {
+ this(clock, historyDirectory, handler, new PowerStatsUidResolver());
+ }
+
+ MockBatteryStatsImpl(Clock clock, File historyDirectory, Handler handler,
+ PowerStatsUidResolver powerStatsUidResolver) {
+ super(clock, historyDirectory, handler, powerStatsUidResolver);
initTimersAndCounters();
setMaxHistoryBuffer(128 * 1024);
setExternalStatsSyncLocked(mExternalStatsSync);
informThatAllExternalStatsAreFlushed();
- // A no-op handler.
- mHandler = new Handler(Looper.getMainLooper()) {
- };
+ mHandler = handler;
mCpuUidFreqTimeReader = mock(KernelCpuUidFreqTimeReader.class);
mKernelWakelockReader = null;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
index b52fc8a..6704987 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
@@ -76,9 +76,18 @@
@Test
public void stateUpdates() {
+ PowerStats.Descriptor descriptor =
+ new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
+ new PersistableBundle());
+ PowerStats powerStats = new PowerStats(descriptor);
+
mClock.currentTime = 1222156800000L; // An important date in world history
mHistory.forceRecordAllHistory();
+ powerStats.stats = new long[]{0};
+ powerStats.uidStats.put(TEST_UID, new long[]{0});
+ mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+
mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 10, /* plugged */ true);
mHistory.recordStateStartEvent(mClock.realtime, mClock.uptime,
BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
@@ -87,10 +96,6 @@
advance(1000);
- PowerStats.Descriptor descriptor =
- new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
- new PersistableBundle());
- PowerStats powerStats = new PowerStats(descriptor);
powerStats.stats = new long[]{10000};
powerStats.uidStats.put(TEST_UID, new long[]{1234});
mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
@@ -181,17 +186,21 @@
@Test
public void incompatiblePowerStats() {
+ PowerStats.Descriptor descriptor =
+ new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
+ new PersistableBundle());
+ PowerStats powerStats = new PowerStats(descriptor);
+
mHistory.forceRecordAllHistory();
+ powerStats.stats = new long[]{0};
+ powerStats.uidStats.put(TEST_UID, new long[]{0});
+ mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 10, /* plugged */ true);
mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, TEST_UID,
BatteryConsumer.PROCESS_STATE_FOREGROUND);
advance(1000);
- PowerStats.Descriptor descriptor =
- new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
- new PersistableBundle());
- PowerStats powerStats = new PowerStats(descriptor);
powerStats.stats = new long[]{10000};
powerStats.uidStats.put(TEST_UID, new long[]{1234});
mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
new file mode 100644
index 0000000..3c48262
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
@@ -0,0 +1,316 @@
+/*
+ * 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.power.stats;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.mock;
+
+import android.os.AggregateBatteryConsumer;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.PersistableBundle;
+import android.os.UidBatteryConsumer;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class PowerStatsExporterTest {
+
+ private static final int APP_UID1 = 42;
+ private static final int APP_UID2 = 84;
+ private static final double TOLERANCE = 0.01;
+
+ @Rule
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+ .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
+ .setCpuScalingPolicy(0, new int[]{0}, new int[]{100})
+ .setAveragePowerForCpuScalingPolicy(0, 360)
+ .setAveragePowerForCpuScalingStep(0, 0, 300)
+ .setCpuPowerBracketCount(1)
+ .setCpuPowerBracket(0, 0, 0);
+
+ private MockClock mClock = new MockClock();
+ private MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
+ private PowerStatsStore mPowerStatsStore;
+ private PowerStatsAggregator mPowerStatsAggregator;
+ private BatteryStatsHistory mHistory;
+ private CpuPowerStatsCollector.CpuStatsArrayLayout mCpuStatsArrayLayout;
+ private PowerStats.Descriptor mPowerStatsDescriptor;
+
+ @Before
+ public void setup() {
+ File storeDirectory = new File(getContext().getCacheDir(), getClass().getSimpleName());
+ clearDirectory(storeDirectory);
+
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
+ .trackDeviceStates(AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessor(
+ new CpuAggregatedPowerStatsProcessor(mStatsRule.getPowerProfile(),
+ mStatsRule.getCpuScalingPolicies()));
+
+ mPowerStatsStore = new PowerStatsStore(storeDirectory, new TestHandler(), config);
+ mHistory = new BatteryStatsHistory(Parcel.obtain(), storeDirectory, 0, 10000,
+ mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock,
+ mMonotonicClock, null);
+ mPowerStatsAggregator = new PowerStatsAggregator(config, mHistory);
+
+ mCpuStatsArrayLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout();
+ mCpuStatsArrayLayout.addDeviceSectionCpuTimeByScalingStep(1);
+ mCpuStatsArrayLayout.addDeviceSectionCpuTimeByCluster(1);
+ mCpuStatsArrayLayout.addDeviceSectionPowerEstimate();
+ mCpuStatsArrayLayout.addUidSectionCpuTimeByPowerBracket(new int[]{0});
+ mCpuStatsArrayLayout.addUidSectionPowerEstimate();
+ PersistableBundle extras = new PersistableBundle();
+ mCpuStatsArrayLayout.toExtras(extras);
+
+ mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
+ mCpuStatsArrayLayout.getDeviceStatsArrayLength(),
+ mCpuStatsArrayLayout.getUidStatsArrayLength(), extras);
+ }
+
+ @Test
+ public void breakdownByProcState_fullRange() throws Exception {
+ BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
+ new String[0], /* includePowerModels */ false,
+ /* includeProcessStateData */ true, /* powerThreshold */ 0);
+ exportAggregatedPowerStats(builder, 1000, 10000);
+
+ BatteryUsageStats actual = builder.build();
+ String message = "Actual BatteryUsageStats: " + actual;
+
+ assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
+ assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
+
+ assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_ANY, 13.5);
+ assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND, 7.47);
+ assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND, 6.03);
+
+ assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_ANY, 12.03);
+ assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 12.03);
+
+ actual.close();
+ }
+
+ @Test
+ public void breakdownByProcState_subRange() throws Exception {
+ BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
+ new String[0], /* includePowerModels */ false,
+ /* includeProcessStateData */ true, /* powerThreshold */ 0);
+ exportAggregatedPowerStats(builder, 3700, 6700);
+
+ BatteryUsageStats actual = builder.build();
+ String message = "Actual BatteryUsageStats: " + actual;
+
+ assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 15.4);
+ assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 15.4);
+
+ assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_ANY, 4.06);
+ assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND, 1.35);
+ assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND, 2.70);
+
+ assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_ANY, 11.33);
+ assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 11.33);
+
+ actual.close();
+ }
+
+ @Test
+ public void combinedProcessStates() throws Exception {
+ BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
+ new String[0], /* includePowerModels */ false,
+ /* includeProcessStateData */ false, /* powerThreshold */ 0);
+ exportAggregatedPowerStats(builder, 1000, 10000);
+
+ BatteryUsageStats actual = builder.build();
+ String message = "Actual BatteryUsageStats: " + actual;
+
+ assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
+ assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
+
+ assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_ANY, 13.5);
+ assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_ANY, 12.03);
+ UidBatteryConsumer uidScope = actual.getUidBatteryConsumers().stream()
+ .filter(us -> us.getUid() == APP_UID1).findFirst().orElse(null);
+ // There shouldn't be any per-procstate data
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> uidScope.getConsumedPower(new BatteryConsumer.Dimensions(
+ BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND)));
+
+
+ actual.close();
+ }
+
+ private void recordBatteryHistory() {
+ PowerStats powerStats = new PowerStats(mPowerStatsDescriptor);
+ long[] uidStats1 = new long[mCpuStatsArrayLayout.getUidStatsArrayLength()];
+ powerStats.uidStats.put(APP_UID1, uidStats1);
+ long[] uidStats2 = new long[mCpuStatsArrayLayout.getUidStatsArrayLength()];
+ powerStats.uidStats.put(APP_UID2, uidStats2);
+
+ mHistory.forceRecordAllHistory();
+
+ mHistory.startRecordingHistory(1000, 1000, false);
+ mHistory.recordPowerStats(1000, 1000, powerStats);
+ mHistory.recordBatteryState(1000, 1000, 70, /* plugged */ false);
+ mHistory.recordStateStartEvent(1000, 1000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
+ mHistory.recordProcessStateChange(1000, 1000, APP_UID1,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+ mHistory.recordProcessStateChange(1000, 1000, APP_UID2,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+ mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 11111);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 10000);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 1111);
+ mHistory.recordPowerStats(1000, 1000, powerStats);
+
+ mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 12345);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 9876);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 2469);
+ mHistory.recordPowerStats(3000, 3000, powerStats);
+
+ mPowerStatsAggregator.aggregatePowerStats(0, 3500, stats -> {
+ mPowerStatsStore.storeAggregatedPowerStats(stats);
+ });
+
+ mHistory.recordProcessStateChange(4000, 4000, APP_UID1,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND);
+
+ mHistory.recordStateStopEvent(4000, 4000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
+
+ mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 54321);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 14321);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 40000);
+ mHistory.recordPowerStats(6000, 6000, powerStats);
+
+ mPowerStatsAggregator.aggregatePowerStats(3500, 6500, stats -> {
+ mPowerStatsStore.storeAggregatedPowerStats(stats);
+ });
+
+ mHistory.recordStateStartEvent(7000, 7000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
+ mHistory.recordProcessStateChange(7000, 7000, APP_UID1,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+ mHistory.recordProcessStateChange(7000, 7000, APP_UID2,
+ BatteryConsumer.PROCESS_STATE_UNSPECIFIED);
+
+ mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 23456);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 23456);
+ mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 0);
+ mHistory.recordPowerStats(8000, 8000, powerStats);
+
+ assertThat(mPowerStatsStore.getTableOfContents()).hasSize(2);
+ }
+
+ private void exportAggregatedPowerStats(BatteryUsageStats.Builder builder,
+ int monotonicStartTime, int monotonicEndTime) {
+ recordBatteryHistory();
+ PowerStatsExporter exporter = new PowerStatsExporter(mPowerStatsStore,
+ mPowerStatsAggregator, /* batterySessionTimeSpanSlackMillis */ 0);
+ exporter.exportAggregatedPowerStats(builder, monotonicStartTime, monotonicEndTime);
+ }
+
+ private void assertDevicePowerEstimate(String message, BatteryUsageStats bus, int componentId,
+ double expected) {
+ AggregateBatteryConsumer consumer = bus.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+ assertWithMessage(message).that(consumer.getConsumedPower(componentId))
+ .isWithin(TOLERANCE).of(expected);
+ }
+
+ private void assertAllAppsPowerEstimate(String message, BatteryUsageStats bus, int componentId,
+ double expected) {
+ AggregateBatteryConsumer consumer = bus.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
+ assertWithMessage(message).that(consumer.getConsumedPower(componentId))
+ .isWithin(TOLERANCE).of(expected);
+ }
+
+ private void assertUidPowerEstimate(String message, BatteryUsageStats bus, int uid,
+ int componentId, int processState, double expected) {
+ List<UidBatteryConsumer> uidScopes = bus.getUidBatteryConsumers();
+ final UidBatteryConsumer uidScope = uidScopes.stream()
+ .filter(us -> us.getUid() == uid).findFirst().orElse(null);
+ assertWithMessage(message).that(uidScope).isNotNull();
+ assertWithMessage(message).that(uidScope.getConsumedPower(
+ new BatteryConsumer.Dimensions(componentId, processState)))
+ .isWithin(TOLERANCE).of(expected);
+ }
+
+ private void clearDirectory(File dir) {
+ if (dir.exists()) {
+ for (File child : dir.listFiles()) {
+ if (child.isDirectory()) {
+ clearDirectory(child);
+ }
+ child.delete();
+ }
+ }
+ }
+
+ private static class TestHandler extends Handler {
+ TestHandler() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ msg.getCallback().run();
+ return true;
+ }
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
index 0e58787..7257a94 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
@@ -27,9 +27,6 @@
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.os.BatteryConsumer;
-import android.os.BatteryManager;
-import android.os.BatteryUsageStats;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
@@ -59,13 +56,13 @@
private MockClock mClock = new MockClock();
private MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
private MockBatteryStatsImpl mBatteryStats;
- private BatteryUsageStatsProvider mBatteryUsageStatsProvider;
private PowerStatsScheduler mPowerStatsScheduler;
private PowerProfile mPowerProfile;
private PowerStatsAggregator mPowerStatsAggregator;
private AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
@Before
+ @SuppressWarnings("GuardedBy")
public void setup() {
final Context context = InstrumentationRegistry.getContext();
@@ -83,11 +80,10 @@
mPowerProfile = mock(PowerProfile.class);
when(mPowerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT)).thenReturn(1000000.0);
mBatteryStats = new MockBatteryStatsImpl(mClock).setPowerProfile(mPowerProfile);
- mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mBatteryStats);
mPowerStatsAggregator = mock(PowerStatsAggregator.class);
mPowerStatsScheduler = new PowerStatsScheduler(context, mPowerStatsAggregator,
TimeUnit.MINUTES.toMillis(30), TimeUnit.HOURS.toMillis(1), mPowerStatsStore, mClock,
- mMonotonicClock, mHandler, mBatteryStats, mBatteryUsageStatsProvider);
+ mMonotonicClock, mHandler, mBatteryStats);
}
@Test
@@ -176,70 +172,6 @@
}
@Test
- public void storeBatteryUsageStatsOnReset() {
- mBatteryStats.forceRecordAllHistory();
- synchronized (mBatteryStats) {
- mBatteryStats.setOnBatteryLocked(mClock.realtime, mClock.uptime, true,
- BatteryManager.BATTERY_STATUS_DISCHARGING, 50, 0);
- }
-
- mPowerStatsScheduler.start(/* schedulePeriodicPowerStatsCollection */false);
-
- assertThat(mPowerStatsStore.getTableOfContents()).isEmpty();
-
- mPowerStatsScheduler.start(true);
-
- synchronized (mBatteryStats) {
- mBatteryStats.noteFlashlightOnLocked(42, mClock.realtime, mClock.uptime);
- }
-
- mClock.realtime += 60000;
- mClock.currentTime += 60000;
-
- synchronized (mBatteryStats) {
- mBatteryStats.noteFlashlightOffLocked(42, mClock.realtime, mClock.uptime);
- }
-
- mClock.realtime += 60000;
- mClock.currentTime += 60000;
-
- // Battery stats reset should have the side-effect of saving accumulated battery usage stats
- synchronized (mBatteryStats) {
- mBatteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
- }
-
- // Await completion
- ConditionVariable done = new ConditionVariable();
- mHandler.post(done::open);
- done.block();
-
- List<PowerStatsSpan.Metadata> contents = mPowerStatsStore.getTableOfContents();
- assertThat(contents).hasSize(1);
-
- PowerStatsSpan.Metadata metadata = contents.get(0);
-
- PowerStatsSpan span = mPowerStatsStore.loadPowerStatsSpan(metadata.getId(),
- BatteryUsageStatsSection.TYPE);
- assertThat(span).isNotNull();
-
- List<PowerStatsSpan.TimeFrame> timeFrames = span.getMetadata().getTimeFrames();
- assertThat(timeFrames).hasSize(1);
- assertThat(timeFrames.get(0).startMonotonicTime).isEqualTo(7654321);
- assertThat(timeFrames.get(0).duration).isEqualTo(120000);
-
- List<PowerStatsSpan.Section> sections = span.getSections();
- assertThat(sections).hasSize(1);
-
- PowerStatsSpan.Section section = sections.get(0);
- assertThat(section.getType()).isEqualTo(BatteryUsageStatsSection.TYPE);
- BatteryUsageStats bus = ((BatteryUsageStatsSection) section).getBatteryUsageStats();
- assertThat(bus.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
- .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
- .isEqualTo(60000);
- }
-
- @Test
public void alignToWallClock() {
// Expect the aligned value to be adjusted by 1 min 30 sec - rounded to the next 15 min
assertThat(PowerStatsScheduler.alignToWallClock(123, TimeUnit.MINUTES.toMillis(15),
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsUidResolverTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsUidResolverTest.java
new file mode 100644
index 0000000..60b2541
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsUidResolverTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.power.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+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;
+
+@RunWith(AndroidJUnit4.class)
+public class PowerStatsUidResolverTest {
+
+ private final PowerStatsUidResolver mResolver = new PowerStatsUidResolver();
+ @Mock
+ PowerStatsUidResolver.Listener mListener;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void addAndRemoveIsolatedUid() {
+ mResolver.addListener(mListener);
+ mResolver.noteIsolatedUidAdded(42, 314);
+ verify(mListener).onIsolatedUidAdded(42, 314);
+ assertThat(mResolver.mapUid(42)).isEqualTo(314);
+
+ mResolver.noteIsolatedUidRemoved(42, 314);
+ verify(mListener).onBeforeIsolatedUidRemoved(42, 314);
+ verify(mListener).onAfterIsolatedUidRemoved(42, 314);
+ assertThat(mResolver.mapUid(42)).isEqualTo(42);
+
+ verifyNoMoreInteractions(mListener);
+ }
+
+ @Test
+ public void retainAndRemoveIsolatedUid() {
+ mResolver.addListener(mListener);
+ mResolver.noteIsolatedUidAdded(42, 314);
+ verify(mListener).onIsolatedUidAdded(42, 314);
+ assertThat(mResolver.mapUid(42)).isEqualTo(314);
+
+ mResolver.retainIsolatedUid(42);
+
+ mResolver.noteIsolatedUidRemoved(42, 314);
+ verify(mListener).onBeforeIsolatedUidRemoved(42, 314);
+ assertThat(mResolver.mapUid(42)).isEqualTo(314);
+ verifyNoMoreInteractions(mListener);
+
+ mResolver.releaseIsolatedUid(42);
+ verify(mListener).onAfterIsolatedUidRemoved(42, 314);
+ assertThat(mResolver.mapUid(42)).isEqualTo(42);
+
+ verifyNoMoreInteractions(mListener);
+ }
+
+ @Test
+ public void removeUidsInRange() {
+ mResolver.noteIsolatedUidAdded(1, 314);
+ mResolver.noteIsolatedUidAdded(2, 314);
+ mResolver.noteIsolatedUidAdded(3, 314);
+ mResolver.noteIsolatedUidAdded(4, 314);
+ mResolver.noteIsolatedUidAdded(6, 314);
+ mResolver.noteIsolatedUidAdded(8, 314);
+ mResolver.noteIsolatedUidAdded(10, 314);
+
+ mResolver.addListener(mListener);
+
+ mResolver.releaseUidsInRange(4, 4); // Single
+ verify(mListener).onAfterIsolatedUidRemoved(4, 314);
+ verifyNoMoreInteractions(mListener);
+
+ // Now: [1, 2, 3, 6, 8, 10]
+
+ mResolver.releaseUidsInRange(2, 3); // Inclusive
+ verify(mListener).onAfterIsolatedUidRemoved(2, 314);
+ verify(mListener).onAfterIsolatedUidRemoved(3, 314);
+ verifyNoMoreInteractions(mListener);
+
+ // Now: [1, 6, 8, 10]
+
+ mResolver.releaseUidsInRange(5, 9); // Exclusive
+ verify(mListener).onAfterIsolatedUidRemoved(6, 314);
+ verify(mListener).onAfterIsolatedUidRemoved(8, 314);
+ verifyNoMoreInteractions(mListener);
+
+ // Now: [1, 10]
+
+ mResolver.releaseUidsInRange(5, 9); // Empty
+ verifyNoMoreInteractions(mListener);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
index 1002fba..88ca029 100644
--- a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.spy;
import android.app.ActivityManagerInternal;
+import android.app.pinner.PinnedFileStat;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -47,22 +48,19 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.io.BufferedReader;
import java.io.CharArrayWriter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
-import java.util.Optional;
+import java.util.List;
import java.util.concurrent.TimeUnit;
@SmallTest
@@ -138,6 +136,13 @@
protected void publishBinderService(PinnerService service, Binder binderService) {
// Suppress this for testing, it's not needed and causes conflitcs.
}
+
+ @Override
+ protected PinnerService.PinnedFile pinFileInternal(String fileToPin,
+ int maxBytesToPin, boolean attemptPinIntrospection) {
+ return new PinnerService.PinnedFile(-1,
+ maxBytesToPin, fileToPin, maxBytesToPin);
+ }
};
}
@@ -202,20 +207,27 @@
return cw.toString();
}
- private int getPinnedSize(PinnerService pinnerService) throws Exception {
- return getPinnedSizeImpl(pinnerService, "Total size: ");
+ private long getPinnedSize(PinnerService pinnerService) {
+ long totalBytesPinned = 0;
+ for (PinnedFileStat stat : pinnerService.getPinnerStats()) {
+ totalBytesPinned += stat.getBytesPinned();
+ }
+ return totalBytesPinned;
}
- private int getPinnedAnonSize(PinnerService pinnerService) throws Exception {
- return getPinnedSizeImpl(pinnerService, "Pinned anon region: ");
+ private int getPinnedAnonSize(PinnerService pinnerService) {
+ List<PinnedFileStat> anonStats = pinnerService.getPinnerStats().stream()
+ .filter(pf -> pf.getGroupName().equals(PinnerService.ANON_REGION_STAT_NAME))
+ .toList();
+ int totalAnon = 0;
+ for (PinnedFileStat anonStat : anonStats) {
+ totalAnon += anonStat.getBytesPinned();
+ }
+ return totalAnon;
}
- private int getPinnedSizeImpl(PinnerService pinnerService, String sizeToken) throws Exception {
- String dumpOutput = getPinnerServiceDump(pinnerService);
- BufferedReader bufReader = new BufferedReader(new StringReader(dumpOutput));
- Optional<Integer> size = bufReader.lines().filter(s -> s.contains(sizeToken))
- .map(s -> Integer.valueOf(s.substring(sizeToken.length()))).findAny();
- return size.orElse(-1);
+ private long getTotalPinnedFiles(PinnerService pinnerService) {
+ return pinnerService.getPinnerStats().stream().count();
}
private void setDeviceConfigPinnedAnonSize(long size) {
@@ -227,7 +239,6 @@
}
@Test
- @Ignore("b/309853498, pinning home app can fail with ENOMEM")
public void testPinHomeApp() throws Exception {
// Enable HOME app pinning
mContext.getOrCreateTestableResources()
@@ -245,15 +256,13 @@
ArrayMap<Integer, Object> pinnedApps = getPinnedApps(pinnerService);
assertThat(pinnedApps.get(KEY_HOME)).isNotNull();
- // Check if dump() reports total pinned bytes
- int totalPinnedSizeBytes = getPinnedSize(pinnerService);
- assertThat(totalPinnedSizeBytes).isGreaterThan(0);
+ assertThat(getPinnedSize(pinnerService)).isGreaterThan(0);
+ assertThat(getTotalPinnedFiles(pinnerService)).isGreaterThan(0);
unpinAll(pinnerService);
}
@Test
- @Ignore("b/309853498, pinning home app can fail with ENOMEM")
public void testPinHomeAppOnBootCompleted() throws Exception {
// Enable HOME app pinning
mContext.getOrCreateTestableResources()
@@ -271,9 +280,7 @@
ArrayMap<Integer, Object> pinnedApps = getPinnedApps(pinnerService);
assertThat(pinnedApps.get(KEY_HOME)).isNotNull();
- // Check if dump() reports total pinned bytes
- int totalPinnedSizeBytes = getPinnedSize(pinnerService);
- assertThat(totalPinnedSizeBytes).isGreaterThan(0);
+ assertThat(getPinnedSize(pinnerService)).isGreaterThan(0);
unpinAll(pinnerService);
}
@@ -294,12 +301,24 @@
ArrayMap<Integer, Object> pinnedApps = getPinnedApps(pinnerService);
assertThat(pinnedApps).isEmpty();
- // Check if dump() reports total pinned bytes
- int totalPinnedSizeBytes = getPinnedSize(pinnerService);
+ long totalPinnedSizeBytes = getPinnedSize(pinnerService);
assertThat(totalPinnedSizeBytes).isEqualTo(0);
int pinnedAnonSizeBytes = getPinnedAnonSize(pinnerService);
- assertThat(pinnedAnonSizeBytes).isEqualTo(-1);
+ assertThat(pinnedAnonSizeBytes).isEqualTo(0);
+
+ unpinAll(pinnerService);
+ }
+
+ @Test
+ public void testPinFile() throws Exception {
+ PinnerService pinnerService = new PinnerService(mContext, mInjector);
+ pinnerService.onStart();
+
+ pinnerService.pinFile("test_file", 4096, null, "my_group");
+
+ assertThat(getPinnedSize(pinnerService)).isGreaterThan(0);
+ assertThat(getTotalPinnedFiles(pinnerService)).isGreaterThan(0);
unpinAll(pinnerService);
}
@@ -341,11 +360,8 @@
waitForPinnerService(pinnerService);
// An empty anon region should clear the associated status entry.
pinnedAnonSizeBytes = getPinnedAnonSize(pinnerService);
- assertThat(pinnedAnonSizeBytes).isEqualTo(-1);
+ assertThat(pinnedAnonSizeBytes).isEqualTo(0);
unpinAll(pinnerService);
}
-
- // TODO: Add test to check that the pages we expect to be pinned are actually pinned
-
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 82efdd3..800350a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -85,9 +85,9 @@
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
+import com.android.server.accessibility.magnification.MagnificationConnectionManager;
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationProcessor;
-import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -156,7 +156,7 @@
@Mock private UserManagerInternal mMockUserManagerInternal;
@Mock private IBinder mMockBinder;
@Mock private IAccessibilityServiceClient mMockServiceClient;
- @Mock private WindowMagnificationManager mMockWindowMagnificationMgr;
+ @Mock private MagnificationConnectionManager mMockMagnificationConnectionManager;
@Mock private MagnificationController mMockMagnificationController;
@Mock private FullScreenMagnificationController mMockFullScreenMagnificationController;
@Mock private ProxyManager mProxyManager;
@@ -180,8 +180,8 @@
UserManagerInternal.class, mMockUserManagerInternal);
mInputFilter = Mockito.mock(FakeInputFilter.class);
- when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
- mMockWindowMagnificationMgr);
+ when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn(
+ mMockMagnificationConnectionManager);
when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
mMockFullScreenMagnificationController);
when(mMockMagnificationController.supportWindowMagnification()).thenReturn(true);
@@ -530,7 +530,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(true);
+ verify(mMockMagnificationConnectionManager).requestConnection(true);
}
@SmallTest
@@ -547,7 +547,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(true);
+ verify(mMockMagnificationConnectionManager).requestConnection(true);
}
@SmallTest
@@ -565,7 +565,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(false);
+ verify(mMockMagnificationConnectionManager).requestConnection(false);
}
@SmallTest
@@ -583,7 +583,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(true);
+ verify(mMockMagnificationConnectionManager).requestConnection(true);
}
@SmallTest
@@ -602,7 +602,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(false);
+ verify(mMockMagnificationConnectionManager).requestConnection(false);
}
@SmallTest
@@ -616,7 +616,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr).requestConnection(true);
+ verify(mMockMagnificationConnectionManager).requestConnection(true);
}
@SmallTest
@@ -630,7 +630,8 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr, atLeastOnce()).removeMagnificationButton(anyInt());
+ verify(mMockMagnificationConnectionManager, atLeastOnce())
+ .removeMagnificationButton(anyInt());
}
@SmallTest
@@ -644,7 +645,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr, never()).removeMagnificationButton(anyInt());
+ verify(mMockMagnificationConnectionManager, never()).removeMagnificationButton(anyInt());
}
@SmallTest
@@ -659,7 +660,8 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr, atLeastOnce()).removeMagnificationButton(anyInt());
+ verify(mMockMagnificationConnectionManager, atLeastOnce())
+ .removeMagnificationButton(anyInt());
}
@SmallTest
@@ -674,7 +676,7 @@
// Invokes client change to trigger onUserStateChanged.
mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
- verify(mMockWindowMagnificationMgr, never()).removeMagnificationButton(anyInt());
+ verify(mMockMagnificationConnectionManager, never()).removeMagnificationButton(anyInt());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
index a02807f..7829fcc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -39,9 +39,9 @@
import android.graphics.Region;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
+import com.android.server.accessibility.magnification.MagnificationConnectionManager;
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationProcessor;
-import com.android.server.accessibility.magnification.WindowMagnificationManager;
import org.junit.Before;
import org.junit.Test;
@@ -66,21 +66,21 @@
@Mock
private FullScreenMagnificationController mMockFullScreenMagnificationController;
@Mock
- private WindowMagnificationManager mMockWindowMagnificationManager;
+ private MagnificationConnectionManager mMockMagnificationConnectionManager;
FullScreenMagnificationControllerStub mFullScreenMagnificationControllerStub;
- WindowMagnificationManagerStub mWindowMagnificationManagerStub;
+ MagnificationManagerStub mMagnificationManagerStub;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mFullScreenMagnificationControllerStub = new FullScreenMagnificationControllerStub(
mMockFullScreenMagnificationController);
- mWindowMagnificationManagerStub = new WindowMagnificationManagerStub(
- mMockWindowMagnificationManager);
+ mMagnificationManagerStub = new MagnificationManagerStub(
+ mMockMagnificationConnectionManager);
when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
mMockFullScreenMagnificationController);
- when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
- mMockWindowMagnificationManager);
+ when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn(
+ mMockMagnificationConnectionManager);
mMagnificationProcessor = new MagnificationProcessor(mMockMagnificationController);
}
@@ -194,7 +194,7 @@
doAnswer((invocation) -> {
((Region) invocation.getArguments()[1]).set(region);
return null;
- }).when(mMockWindowMagnificationManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
+ }).when(mMockMagnificationConnectionManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
any());
final Region result = new Region();
@@ -286,7 +286,7 @@
mMagnificationProcessor.resetCurrentMagnification(TEST_DISPLAY, /* animate= */false);
- verify(mMockWindowMagnificationManager).disableWindowMagnification(TEST_DISPLAY, false,
+ verify(mMockMagnificationConnectionManager).disableWindowMagnification(TEST_DISPLAY, false,
null);
}
@@ -296,7 +296,7 @@
mMagnificationProcessor.resetAllIfNeeded(connectionId);
verify(mMockFullScreenMagnificationController).resetAllIfNeeded(eq(connectionId));
- verify(mMockWindowMagnificationManager).resetAllIfNeeded(eq(connectionId));
+ verify(mMockMagnificationConnectionManager).resetAllIfNeeded(eq(connectionId));
}
@Test
@@ -450,7 +450,7 @@
.setActivated(false).build();
mMagnificationProcessor.setMagnificationConfig(TEST_DISPLAY, config, false, SERVICE_ID);
- verify(mMockWindowMagnificationManager)
+ verify(mMockMagnificationConnectionManager)
.disableWindowMagnification(eq(TEST_DISPLAY), anyBoolean());
}
@@ -481,11 +481,11 @@
mFullScreenMagnificationControllerStub.resetAndStubMethods();
mMockFullScreenMagnificationController.setScaleAndCenter(displayId, config.getScale(),
config.getCenterX(), config.getCenterY(), false, SERVICE_ID);
- mWindowMagnificationManagerStub.deactivateIfNeed();
+ mMagnificationManagerStub.deactivateIfNeed();
} else if (config.getMode() == MAGNIFICATION_MODE_WINDOW) {
- mWindowMagnificationManagerStub.resetAndStubMethods();
- mMockWindowMagnificationManager.enableWindowMagnification(displayId, config.getScale(),
- config.getCenterX(), config.getCenterY());
+ mMagnificationManagerStub.resetAndStubMethods();
+ mMockMagnificationConnectionManager.enableWindowMagnification(
+ displayId, config.getScale(), config.getCenterX(), config.getCenterY());
mFullScreenMagnificationControllerStub.deactivateIfNeed();
}
}
@@ -568,26 +568,26 @@
}
}
- private static class WindowMagnificationManagerStub {
- private final WindowMagnificationManager mWindowMagnificationManager;
+ private static class MagnificationManagerStub {
+ private final MagnificationConnectionManager mMagnificationConnectionManager;
private float mScale = 1.0f;
private float mCenterX = 0;
private float mCenterY = 0;
private boolean mIsEnabled = false;
- WindowMagnificationManagerStub(
- WindowMagnificationManager windowMagnificationManager) {
- mWindowMagnificationManager = windowMagnificationManager;
+ MagnificationManagerStub(
+ MagnificationConnectionManager magnificationConnectionManager) {
+ mMagnificationConnectionManager = magnificationConnectionManager;
}
private void stubMethods() {
- doAnswer(invocation -> mScale).when(mWindowMagnificationManager).getScale(
+ doAnswer(invocation -> mScale).when(mMagnificationConnectionManager).getScale(
TEST_DISPLAY);
- doAnswer(invocation -> mCenterX).when(mWindowMagnificationManager).getCenterX(
+ doAnswer(invocation -> mCenterX).when(mMagnificationConnectionManager).getCenterX(
TEST_DISPLAY);
- doAnswer(invocation -> mCenterY).when(mWindowMagnificationManager).getCenterY(
+ doAnswer(invocation -> mCenterY).when(mMagnificationConnectionManager).getCenterY(
TEST_DISPLAY);
- doAnswer(invocation -> mIsEnabled).when(mWindowMagnificationManager)
+ doAnswer(invocation -> mIsEnabled).when(mMagnificationConnectionManager)
.isWindowMagnifierEnabled(TEST_DISPLAY);
Answer enableWindowMagnificationStubAnswer = invocation -> {
@@ -598,10 +598,10 @@
return true;
};
doAnswer(enableWindowMagnificationStubAnswer).when(
- mWindowMagnificationManager).enableWindowMagnification(eq(TEST_DISPLAY),
+ mMagnificationConnectionManager).enableWindowMagnification(eq(TEST_DISPLAY),
anyFloat(), anyFloat(), anyFloat());
doAnswer(enableWindowMagnificationStubAnswer).when(
- mWindowMagnificationManager).enableWindowMagnification(eq(TEST_DISPLAY),
+ mMagnificationConnectionManager).enableWindowMagnification(eq(TEST_DISPLAY),
anyFloat(), anyFloat(), anyFloat(), any(), anyInt());
Answer disableWindowMagnificationStubAnswer = invocation -> {
@@ -609,15 +609,15 @@
return true;
};
doAnswer(disableWindowMagnificationStubAnswer).when(
- mWindowMagnificationManager).disableWindowMagnification(eq(TEST_DISPLAY),
+ mMagnificationConnectionManager).disableWindowMagnification(eq(TEST_DISPLAY),
anyBoolean());
doAnswer(disableWindowMagnificationStubAnswer).when(
- mWindowMagnificationManager).disableWindowMagnification(eq(TEST_DISPLAY),
+ mMagnificationConnectionManager).disableWindowMagnification(eq(TEST_DISPLAY),
anyBoolean(), any());
}
public void resetAndStubMethods() {
- Mockito.reset(mWindowMagnificationManager);
+ Mockito.reset(mMagnificationConnectionManager);
stubMethods();
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index fd2cf6d..3b39160 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -540,18 +540,23 @@
twoFingerTap();
assertIn(STATE_ACTIVATED);
+ verify(mMockMagnificationLogger, never()).logMagnificationTripleTap(anyBoolean());
+ verify(mMockMagnificationLogger).logMagnificationTwoFingerTripleTap(true);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testTwoFingerTripleTap_StateIsActivated_shouldInIdle() {
goFromStateIdleTo(STATE_ACTIVATED);
+ reset(mMockMagnificationLogger);
twoFingerTap();
twoFingerTap();
twoFingerTap();
assertIn(STATE_IDLE);
+ verify(mMockMagnificationLogger, never()).logMagnificationTripleTap(anyBoolean());
+ verify(mMockMagnificationLogger).logMagnificationTwoFingerTripleTap(false);
}
@Test
@@ -564,6 +569,8 @@
twoFingerTapAndHold();
assertIn(STATE_NON_ACTIVATED_ZOOMED_TMP);
+ verify(mMockMagnificationLogger, never()).logMagnificationTripleTap(anyBoolean());
+ verify(mMockMagnificationLogger).logMagnificationTwoFingerTripleTap(true);
}
@Test
@@ -576,6 +583,8 @@
twoFingerSwipeAndHold();
assertIn(STATE_NON_ACTIVATED_ZOOMED_TMP);
+ verify(mMockMagnificationLogger, never()).logMagnificationTripleTap(anyBoolean());
+ verify(mMockMagnificationLogger).logMagnificationTwoFingerTripleTap(true);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
new file mode 100644
index 0000000..3843e25
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
@@ -0,0 +1,854 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.magnification;
+
+import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY;
+import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY_2;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import static java.lang.Float.NaN;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+import android.view.accessibility.MagnificationAnimationCallback;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.FlakyTest;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityTraceManager;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+
+/**
+ * Tests for WindowMagnificationManager.
+ */
+public class MagnificationConnectionManagerTest {
+
+ private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
+ private static final int SERVICE_ID = 1;
+
+ private MockWindowMagnificationConnection mMockConnection;
+ @Mock
+ private Context mContext;
+ @Mock
+ private AccessibilityTraceManager mMockTrace;
+ @Mock
+ private StatusBarManagerInternal mMockStatusBarManagerInternal;
+ @Mock
+ private MagnificationAnimationCallback mAnimationCallback;
+ @Mock
+ private MagnificationConnectionManager.Callback mMockCallback;
+ private MockContentResolver mResolver;
+ private MagnificationConnectionManager mMagnificationConnectionManager;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+ LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
+ mResolver = new MockContentResolver();
+ mMockConnection = new MockWindowMagnificationConnection();
+ mMagnificationConnectionManager = new MagnificationConnectionManager(mContext, new Object(),
+ mMockCallback, mMockTrace, new MagnificationScaleProvider(mContext));
+
+ when(mContext.getContentResolver()).thenReturn(mResolver);
+ stubSetConnection(false);
+
+ mResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ Settings.Secure.putFloatForUser(mResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 2.5f,
+ CURRENT_USER_ID);
+ }
+
+ private void stubSetConnection(boolean needDelay) {
+ doAnswer((InvocationOnMock invocation) -> {
+ final boolean connect = (Boolean) invocation.getArguments()[0];
+ // Simulates setConnection() called by another process.
+ if (needDelay) {
+ final Context context = ApplicationProvider.getApplicationContext();
+ context.getMainThreadHandler().postDelayed(
+ () -> {
+ mMagnificationConnectionManager.setConnection(
+ connect ? mMockConnection.getConnection() : null);
+ }, 10);
+ } else {
+ mMagnificationConnectionManager.setConnection(
+ connect ? mMockConnection.getConnection() : null);
+ }
+ return true;
+ }).when(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(anyBoolean());
+ }
+
+ @Test
+ public void setConnection_connectionIsNull_wrapperIsNullAndLinkToDeath() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ assertTrue(mMagnificationConnectionManager.isConnected());
+ verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
+ }
+
+ @Test
+ public void setConnection_connectionIsNull_setMirrorWindowCallbackAndHasWrapper()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ assertTrue(mMagnificationConnectionManager.isConnected());
+ verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
+ verify(mMockConnection.getConnection()).setConnectionCallback(
+ any(IWindowMagnificationConnectionCallback.class));
+ }
+
+ @Test
+ public void binderDied_hasConnection_wrapperIsNullAndUnlinkToDeath() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ mMockConnection.getDeathRecipient().binderDied();
+
+ assertFalse(mMagnificationConnectionManager.isConnected());
+ verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(),
+ 0);
+ }
+
+ /**
+ * This test simulates {@link MagnificationConnectionManager#setConnection} is called by thread
+ * A and then the former connection is called by thread B. In this situation we should keep the
+ * new connection.
+ */
+ @Test
+ public void setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ MockWindowMagnificationConnection secondConnection =
+ new MockWindowMagnificationConnection();
+
+ mMagnificationConnectionManager.setConnection(secondConnection.getConnection());
+ mMockConnection.getDeathRecipient().binderDied();
+
+ assertTrue(mMagnificationConnectionManager.isConnected());
+ verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(), 0);
+ verify(secondConnection.asBinder(), never()).unlinkToDeath(
+ secondConnection.getDeathRecipient(), 0);
+ }
+
+ @Test
+ public void setNullConnection_hasConnection_wrapperIsNull() throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ mMagnificationConnectionManager.setConnection(null);
+
+ assertFalse(mMagnificationConnectionManager.isConnected());
+ verify(mMockConnection.getConnection()).setConnectionCallback(null);
+ }
+
+ @Test
+ public void enableWithAnimation_hasConnection_enableWindowMagnification()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f);
+
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
+ eq(200f), eq(300f), eq(0f), eq(0f), notNull());
+ }
+
+ @Test
+ public void enableWithCallback_hasConnection_enableWindowMagnification()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f,
+ mAnimationCallback, SERVICE_ID);
+
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
+ eq(200f), eq(300f), eq(0f), eq(0f),
+ any(IRemoteMagnificationAnimationCallback.class));
+ verify(mAnimationCallback).onResult(true);
+ }
+
+ @Test
+ public void disable_hasConnectionAndEnabled_disableWindowMagnification()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+
+ verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
+ notNull());
+ }
+
+ @Test
+ public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false,
+ mAnimationCallback);
+
+ verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
+ any(IRemoteMagnificationAnimationCallback.class));
+ verify(mAnimationCallback).onResult(true);
+ }
+
+ @Test
+ public void isWindowMagnifierEnabled_hasConnectionAndEnabled_returnExpectedValue() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
+
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ }
+
+ @Test
+ public void getPersistedScale() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ assertEquals(mMagnificationConnectionManager.getPersistedScale(TEST_DISPLAY), 2.5f);
+ }
+
+ @Test
+ public void persistScale_setValue_expectedValueInProvider() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
+ mMagnificationConnectionManager.setScale(TEST_DISPLAY, 2.5f);
+
+ mMagnificationConnectionManager.persistScale(TEST_DISPLAY);
+
+ assertEquals(Settings.Secure.getFloatForUser(mResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f,
+ CURRENT_USER_ID), 2.5f);
+ }
+
+ @Test
+ public void persistScale_setValueWhenScaleIsOne_nothingChanged() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ final float persistedScale =
+ mMagnificationConnectionManager.getPersistedScale(TEST_DISPLAY);
+
+ mMagnificationConnectionManager.setScale(TEST_DISPLAY, 1.0f);
+ mMagnificationConnectionManager.persistScale(TEST_DISPLAY);
+
+ assertEquals(Settings.Secure.getFloatForUser(mResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f,
+ CURRENT_USER_ID), persistedScale);
+ }
+
+ @Test
+ public void scaleSetterGetter_enabledOnTestDisplay_expectedValue() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
+
+ mMagnificationConnectionManager.setScale(TEST_DISPLAY, 2.5f);
+
+ assertEquals(mMagnificationConnectionManager.getScale(TEST_DISPLAY), 2.5f);
+ }
+
+ @Test
+ public void scaleSetterGetter_scaleIsOutOfRang_getNormalizeValue() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
+
+ mMagnificationConnectionManager.setScale(TEST_DISPLAY, 10.0f);
+
+ assertEquals(mMagnificationConnectionManager.getScale(TEST_DISPLAY),
+ MagnificationScaleProvider.MAX_SCALE);
+ }
+
+ @FlakyTest(bugId = 297879435)
+ @Test
+ public void logTrackingTypingFocus_processScroll_logDuration() {
+ MagnificationConnectionManager spyMagnificationConnectionManager = spy(
+ mMagnificationConnectionManager);
+ spyMagnificationConnectionManager.enableWindowMagnification(
+ TEST_DISPLAY, 3.0f, 50f, 50f);
+ spyMagnificationConnectionManager.onImeWindowVisibilityChanged(
+ TEST_DISPLAY, /* shown */ true);
+
+ spyMagnificationConnectionManager.processScroll(TEST_DISPLAY, 10f, 10f);
+
+ verify(spyMagnificationConnectionManager).logTrackingTypingFocus(anyLong());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingDisabledByOnDrag_withoutMovingMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ final Region outRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ }
+
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingDisabledByScroll_withoutMovingMagnifier()
+ throws RemoteException {
+ final float distanceX = 10f;
+ final float distanceY = 10f;
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ final Region outRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mMagnificationConnectionManager.processScroll(TEST_DISPLAY, distanceX, distanceY);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_requestRectangleInBound_withoutMovingMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ final Region outRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.inset(-10, -10);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ }
+ @Test
+ public void onRectangleOnScreenRequested_imeVisibilityDefaultInvisible_withoutMovingMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ final Region outRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingEnabledByDefault_movingMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ final Region outRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
+ eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ any(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_imeInvisible_withoutMovingMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ final Region outRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, false);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection(), never())
+ .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingEnabledByDragAndReset_movingMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ final Region outRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
+ final Rect requestedRect = outRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
+ eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ any(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnifier() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ final Region beforeRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+ final Rect requestedRect = beforeRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mMagnificationConnectionManager.setMagnificationFollowTypingEnabled(false);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ final Region afterRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
+ assertEquals(afterRegion, beforeRegion);
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingDisabled_withoutMovingMagnifier() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
+ final Region beforeRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+ final Rect requestedRect = beforeRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ final Region afterRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
+ assertEquals(afterRegion, beforeRegion);
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_trackingDisabledAndEnabledMagnifier_movingMagnifier()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
+ mMagnificationConnectionManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
+ mMagnificationConnectionManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
+ final Region beforeRegion = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
+ final Rect requestedRect = beforeRegion.getBounds();
+ requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+ // Enabling a window magnifier again will turn on the tracking typing focus functionality.
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, NaN, NaN, NaN);
+
+ mMagnificationConnectionManager.onRectangleOnScreenRequested(TEST_DISPLAY,
+ requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
+
+ verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
+ eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
+ any(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ @Test
+ public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
+
+ mMagnificationConnectionManager.moveWindowMagnification(TEST_DISPLAY, 200, 300);
+ verify(mMockConnection.getConnection()).moveWindowMagnifier(TEST_DISPLAY, 200, 300);
+ }
+
+ @Test
+ public void showMagnificationButton_hasConnection_invokeConnectionMethod()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ mMagnificationConnectionManager.showMagnificationButton(TEST_DISPLAY,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+ verify(mMockConnection.getConnection()).showMagnificationButton(TEST_DISPLAY,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+
+ mMagnificationConnectionManager.removeMagnificationButton(TEST_DISPLAY);
+ verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
+ }
+
+ @Test
+ public void removeMagnificationSettingsPanel_hasConnection_invokeConnectionMethod()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ mMagnificationConnectionManager.removeMagnificationSettingsPanel(TEST_DISPLAY);
+ verify(mMockConnection.getConnection()).removeMagnificationSettingsPanel(TEST_DISPLAY);
+ }
+
+ @Test
+ public void onUserMagnificationScaleChanged_hasConnection_invokeConnectionMethod()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+
+ final float testScale = 3f;
+ mMagnificationConnectionManager.onUserMagnificationScaleChanged(
+ CURRENT_USER_ID, TEST_DISPLAY, testScale);
+ verify(mMockConnection.getConnection()).onUserMagnificationScaleChanged(
+ eq(CURRENT_USER_ID), eq(TEST_DISPLAY), eq(testScale));
+ }
+
+ @Test
+ public void pointersInWindow_magnifierEnabled_returnCorrectValue() throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+ mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(TEST_DISPLAY,
+ new Rect(0, 0, 500, 500));
+ PointF[] pointersLocation = new PointF[2];
+ pointersLocation[0] = new PointF(600, 700);
+ pointersLocation[1] = new PointF(300, 400);
+ MotionEvent event = generatePointersDownEvent(pointersLocation);
+
+ assertEquals(mMagnificationConnectionManager.pointersInWindow(TEST_DISPLAY, event), 1);
+ }
+
+ @Test
+ public void onPerformScaleAction_magnifierEnabled_notifyAction() throws RemoteException {
+ final float newScale = 4.0f;
+ final boolean updatePersistence = true;
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+
+ mMockConnection.getConnectionCallback().onPerformScaleAction(
+ TEST_DISPLAY, newScale, updatePersistence);
+
+ verify(mMockCallback).onPerformScaleAction(
+ eq(TEST_DISPLAY), eq(newScale), eq(updatePersistence));
+ }
+
+ @Test
+ public void onAccessibilityActionPerformed_magnifierEnabled_notifyAction()
+ throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+
+ mMockConnection.getConnectionCallback().onAccessibilityActionPerformed(TEST_DISPLAY);
+
+ verify(mMockCallback).onAccessibilityActionPerformed(eq(TEST_DISPLAY));
+ }
+
+ @Test
+ public void binderDied_windowMagnifierIsEnabled_resetState() throws RemoteException {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+
+ mMockConnection.getDeathRecipient().binderDied();
+
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ }
+
+ @Test
+ public void
+ requestConnectionToNull_disableAllMagnifiersAndRequestWindowMagnificationConnection()
+ throws RemoteException {
+ assertTrue(mMagnificationConnectionManager.requestConnection(true));
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+
+ assertTrue(mMagnificationConnectionManager.requestConnection(false));
+
+ verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
+ verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(false);
+ }
+
+ @Test
+ public void requestConnection_requestWindowMagnificationConnection() throws RemoteException {
+ assertTrue(mMagnificationConnectionManager.requestConnection(true));
+ verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(true);
+ }
+
+ @Test
+ public void isConnected_requestConnection_expectedValue() throws RemoteException {
+ mMagnificationConnectionManager.requestConnection(true);
+ assertTrue(mMagnificationConnectionManager.isConnected());
+
+ mMagnificationConnectionManager.requestConnection(false);
+ assertFalse(mMagnificationConnectionManager.isConnected());
+ }
+
+ @Test
+ public void requestConnection_registerAndUnregisterBroadcastReceiver() {
+ assertTrue(mMagnificationConnectionManager.requestConnection(true));
+ verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
+
+ assertTrue(mMagnificationConnectionManager.requestConnection(false));
+ verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
+ }
+
+ @Test
+ public void requestConnectionToNull_expectedGetterResults() {
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1);
+
+ mMagnificationConnectionManager.requestConnection(false);
+
+ assertEquals(1f, mMagnificationConnectionManager.getScale(TEST_DISPLAY), 0);
+ assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY)));
+ assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY)));
+ final Region bounds = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
+ assertTrue(bounds.isEmpty());
+ }
+
+ @Test
+ public void enableWindowMagnification_connecting_invokeConnectionMethodAfterConnected()
+ throws RemoteException {
+ stubSetConnection(true);
+ mMagnificationConnectionManager.requestConnection(true);
+
+ assertTrue(mMagnificationConnectionManager.enableWindowMagnification(
+ TEST_DISPLAY, 3f, 1, 1));
+
+ // Invoke enableWindowMagnification if the connection is connected.
+ verify(mMockConnection.getConnection()).enableWindowMagnification(
+ eq(TEST_DISPLAY), eq(3f),
+ eq(1f), eq(1f), eq(0f), eq(0f), notNull());
+ }
+
+ @Test
+ public void resetAllMagnification_enabledBySameId_windowMagnifiersDisabled() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, null,
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
+ 100f, 200f, null,
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+
+ mMagnificationConnectionManager.resetAllIfNeeded(SERVICE_ID);
+
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+ }
+
+ @Test
+ public void resetAllMagnification_enabledByDifferentId_windowMagnifierDisabled() {
+ final int serviceId2 = SERVICE_ID + 1;
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, null,
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
+ 100f, 200f, null,
+ MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER, serviceId2);
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+
+ mMagnificationConnectionManager.resetAllIfNeeded(SERVICE_ID);
+
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+ }
+
+ @Test
+ public void onScreenOff_windowMagnifierIsEnabled_removeButtonAndDisableWindowMagnification()
+ throws RemoteException {
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
+
+ mMagnificationConnectionManager.mScreenStateReceiver.onReceive(mContext,
+ new Intent(Intent.ACTION_SCREEN_OFF));
+
+ verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
+ verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ }
+
+ @Test
+ public void centerGetter_enabledOnTestDisplay_expectedValues() {
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
+
+ assertEquals(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 100f);
+ assertEquals(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 200f);
+ }
+
+ @Test
+ public void centerGetter_enabledOnTestDisplayWindowAtCenter_expectedValues()
+ throws RemoteException {
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
+
+ assertEquals(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 100f);
+ assertEquals(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 200f);
+
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
+ eq(100f), eq(200f), eq(0f), eq(0f), notNull());
+ }
+
+ @Test
+ public void centerGetter_enabledOnTestDisplayWindowAtLeftTop_expectedValues()
+ throws RemoteException {
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, MagnificationConnectionManager.WINDOW_POSITION_AT_TOP_LEFT);
+
+ assertEquals(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 100f);
+ assertEquals(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 200f);
+
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
+ eq(100f), eq(200f), eq(-1f), eq(-1f), notNull());
+ }
+
+ @Test
+ public void magnifierGetters_disabled_expectedValues() {
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, MagnificationConnectionManager.WINDOW_POSITION_AT_CENTER);
+
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+
+ assertEquals(1f, mMagnificationConnectionManager.getScale(TEST_DISPLAY), 0);
+ assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterX(TEST_DISPLAY)));
+ assertTrue(Float.isNaN(mMagnificationConnectionManager.getCenterY(TEST_DISPLAY)));
+ final Region bounds = new Region();
+ mMagnificationConnectionManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
+ assertTrue(bounds.isEmpty());
+ }
+
+ @Test
+ public void onDisplayRemoved_enabledOnTestDisplay_disabled() {
+ mMagnificationConnectionManager.requestConnection(true);
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
+
+ mMagnificationConnectionManager.onDisplayRemoved(TEST_DISPLAY);
+
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ }
+
+ @Test
+ public void onWindowMagnificationActivationState_magnifierEnabled_notifyActivatedState() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+
+ verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, true);
+ }
+
+ @Test
+ public void onWindowMagnificationActivationState_magnifierDisabled_notifyDeactivatedState() {
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+
+ verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, false);
+
+ Mockito.reset(mMockCallback);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
+
+ verify(mMockCallback, never()).onWindowMagnificationActivationState(eq(TEST_DISPLAY),
+ anyBoolean());
+ }
+
+ private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
+ final int len = pointersLocation.length;
+
+ final MotionEvent.PointerProperties[] pp = new MotionEvent.PointerProperties[len];
+ for (int i = 0; i < len; i++) {
+ MotionEvent.PointerProperties pointerProperty = new MotionEvent.PointerProperties();
+ pointerProperty.id = i;
+ pointerProperty.toolType = MotionEvent.TOOL_TYPE_FINGER;
+ pp[i] = pointerProperty;
+ }
+
+ final MotionEvent.PointerCoords[] pc = new MotionEvent.PointerCoords[len];
+ for (int i = 0; i < len; i++) {
+ MotionEvent.PointerCoords pointerCoord = new MotionEvent.PointerCoords();
+ pointerCoord.x = pointersLocation[i].x;
+ pointerCoord.y = pointersLocation[i].y;
+ pc[i] = pointerCoord;
+ }
+
+ return MotionEvent.obtain(
+ /* downTime */ SystemClock.uptimeMillis(),
+ /* eventTime */ SystemClock.uptimeMillis(),
+ /* action */ MotionEvent.ACTION_POINTER_DOWN,
+ /* pointerCount */ pc.length,
+ /* pointerProperties */ pp,
+ /* pointerCoords */ pc,
+ /* metaState */ 0,
+ /* buttonState */ 0,
+ /* xPrecision */ 1.0f,
+ /* yPrecision */ 1.0f,
+ /* deviceId */ 0,
+ /* edgeFlags */ 0,
+ /* source */ InputDevice.SOURCE_TOUCHSCREEN,
+ /* flags */ 0);
+ }
+
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
index cfd0289..8f85f11 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionWrapperTest.java
@@ -39,7 +39,7 @@
/**
* Tests for MagnificationConnectionWrapper. We don't test {@code
* MagnificationConnectionWrapper#linkToDeath(IBinder.DeathRecipient)} since it's tested in
- * {@link WindowMagnificationManagerTest}.
+ * {@link MagnificationConnectionManagerTest}.
*/
public class MagnificationConnectionWrapperTest {
@@ -73,9 +73,9 @@
}
@Test
- public void setScale() throws RemoteException {
- mConnectionWrapper.setScale(TEST_DISPLAY, 3.0f);
- verify(mConnection).setScale(TEST_DISPLAY, 3.0f);
+ public void setScaleForWindowMagnification() throws RemoteException {
+ mConnectionWrapper.setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
+ verify(mConnection).setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index d4c6fad..e8cdf35 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -131,7 +131,7 @@
private ArgumentCaptor<MagnificationAnimationCallback> mCallbackArgumentCaptor;
private MockWindowMagnificationConnection mMockConnection;
- private WindowMagnificationManager mWindowMagnificationManager;
+ private MagnificationConnectionManager mMagnificationConnectionManager;
private MockContentResolver mMockResolver;
private MagnificationController mMagnificationController;
private final WindowMagnificationMgrCallbackDelegate
@@ -205,13 +205,14 @@
));
mScreenMagnificationController.register(TEST_DISPLAY);
- mWindowMagnificationManager = spy(new WindowMagnificationManager(mContext, globalLock,
- mWindowMagnificationCallbackDelegate, mTraceManager, mScaleProvider));
+ mMagnificationConnectionManager = spy(
+ new MagnificationConnectionManager(mContext, globalLock,
+ mWindowMagnificationCallbackDelegate, mTraceManager, mScaleProvider));
mMockConnection = new MockWindowMagnificationConnection(true);
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mMagnificationController = spy(new MagnificationController(mService, globalLock, mContext,
- mScreenMagnificationController, mWindowMagnificationManager, mScaleProvider,
+ mScreenMagnificationController, mMagnificationConnectionManager, mScaleProvider,
ConcurrentUtils.DIRECT_EXECUTOR));
mMagnificationController.setMagnificationCapabilities(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
@@ -254,8 +255,10 @@
mCallbackArgumentCaptor.getValue().onResult(true);
mMockConnection.invokeCallbacks();
verify(mTransitionCallBack).onResult(TEST_DISPLAY, true);
- assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
- assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_X,
+ mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y,
+ mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 0);
}
@Test
@@ -297,8 +300,10 @@
mMockConnection.invokeCallbacks();
verify(mTransitionCallBack).onResult(TEST_DISPLAY, true);
- assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
- assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_X,
+ mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y,
+ mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 0);
}
@Test
@@ -316,7 +321,7 @@
// The first time is triggered when window mode is activated.
// The second time is triggered when activating the window mode again.
// The third time is triggered when the transition is completed.
- verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
}
@@ -330,7 +335,7 @@
mTransitionCallBack);
mMockConnection.invokeCallbacks();
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY,
DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
true, MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -350,7 +355,7 @@
mTransitionCallBack);
mMockConnection.invokeCallbacks();
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE,
magnificationBounds.exactCenterX(), magnificationBounds.exactCenterY(), true,
MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -386,11 +391,11 @@
mTransitionCallBack);
// Enable window magnification while animating.
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
Float.NaN, Float.NaN, null, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
verify(mScreenMagnificationController, never()).setScaleAndCenter(TEST_DISPLAY,
DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
true, MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -408,8 +413,10 @@
verify(mScreenMagnificationController).reset(eq(TEST_DISPLAY), eq(false));
mMockConnection.invokeCallbacks();
- assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
- assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_X,
+ mMagnificationConnectionManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y,
+ mMagnificationConnectionManager.getCenterY(TEST_DISPLAY), 0);
}
@Test
@@ -438,7 +445,7 @@
animate, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY),
eq(DEFAULT_SCALE), eq(MAGNIFIED_CENTER_X), eq(MAGNIFIED_CENTER_Y),
any(MagnificationAnimationCallback.class), eq(TEST_SERVICE_ID));
@@ -564,7 +571,7 @@
mMagnificationController.onDisplayRemoved(TEST_DISPLAY);
verify(mScreenMagnificationController).onDisplayRemoved(TEST_DISPLAY);
- verify(mWindowMagnificationManager).onDisplayRemoved(TEST_DISPLAY);
+ verify(mMagnificationConnectionManager).onDisplayRemoved(TEST_DISPLAY);
verify(mScaleProvider).onDisplayRemoved(TEST_DISPLAY);
}
@@ -573,7 +580,7 @@
mMagnificationController.updateUserIdIfNeeded(SECOND_USER_ID);
verify(mScreenMagnificationController).resetAllIfNeeded(false);
- verify(mWindowMagnificationManager).disableAllWindowMagnifiers();
+ verify(mMagnificationConnectionManager).disableAllWindowMagnifiers();
verify(mScaleProvider).onUserChanged(SECOND_USER_ID);
}
@@ -584,7 +591,7 @@
mMagnificationController.onRequestMagnificationSpec(TEST_DISPLAY, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
}
@Test
@@ -594,11 +601,11 @@
// The first time is trigger when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
- verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -625,8 +632,8 @@
mMagnificationController.onPerformScaleAction(TEST_DISPLAY, newScale, updatePersistence);
- verify(mWindowMagnificationManager).setScale(eq(TEST_DISPLAY), eq(newScale));
- verify(mWindowMagnificationManager, never()).persistScale(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).setScale(eq(TEST_DISPLAY), eq(newScale));
+ verify(mMagnificationConnectionManager, never()).persistScale(eq(TEST_DISPLAY));
}
@Test
@@ -669,7 +676,7 @@
assertEquals(config.getCenterY(), actualConfig.getCenterY(), 0);
assertEquals(config.getScale(), actualConfig.getScale(), 0);
- verify(mWindowMagnificationManager).onUserMagnificationScaleChanged(
+ verify(mMagnificationConnectionManager).onUserMagnificationScaleChanged(
/* userId= */ anyInt(), eq(TEST_DISPLAY), eq(config.getScale()));
}
@@ -677,11 +684,11 @@
public void onSourceBoundChanged_windowEnabled_notifyMagnificationChanged()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- reset(mWindowMagnificationManager);
+ reset(mMagnificationConnectionManager);
mMagnificationController.onSourceBoundsChanged(TEST_DISPLAY, TEST_RECT);
- verify(mWindowMagnificationManager).onUserMagnificationScaleChanged(
+ verify(mMagnificationConnectionManager).onUserMagnificationScaleChanged(
/* userId= */ anyInt(), eq(TEST_DISPLAY), eq(DEFAULT_SCALE));
}
@@ -780,11 +787,11 @@
// The first time is triggered when window mode is activated.
// The second time is triggered when accessibility action performed.
- verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -799,10 +806,11 @@
// The first time is triggered when window mode is activated.
// The second time is triggered when accessibility action performed.
- verify(mWindowMagnificationManager, times(2)).removeMagnificationButton(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager, times(2))
+ .removeMagnificationButton(eq(TEST_DISPLAY));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -816,7 +824,7 @@
public void deactivateWindowMagnification_windowActivated_triggerCallbackAndLogUsage()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, /* clear= */ true);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, /* clear= */ true);
verify(mMagnificationController).onWindowMagnificationActivationState(
eq(TEST_DISPLAY), eq(false));
@@ -828,7 +836,7 @@
public void setPreferenceMagnificationFollowTypingEnabled_setPrefDisabled_disableAll() {
mMagnificationController.setMagnificationFollowTypingEnabled(false);
- verify(mWindowMagnificationManager).setMagnificationFollowTypingEnabled(eq(false));
+ verify(mMagnificationConnectionManager).setMagnificationFollowTypingEnabled(eq(false));
verify(mScreenMagnificationController).setMagnificationFollowTypingEnabled(eq(false));
}
@@ -850,7 +858,7 @@
verify(mScreenMagnificationController).onRectangleOnScreenRequested(eq(TEST_DISPLAY),
eq(TEST_RECT.left), eq(TEST_RECT.top), eq(TEST_RECT.right), eq(TEST_RECT.bottom));
- verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+ verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -867,7 +875,7 @@
verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(anyInt(),
anyInt(), anyInt(), anyInt(), anyInt());
- verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+ verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -880,7 +888,7 @@
verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(
eq(TEST_DISPLAY), anyInt(), anyInt(), anyInt(), anyInt());
- verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+ verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -895,7 +903,7 @@
verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(
eq(TEST_DISPLAY), anyInt(), anyInt(), anyInt(), anyInt());
- verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+ verify(mMagnificationConnectionManager, never()).onRectangleOnScreenRequested(anyInt(),
anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -970,7 +978,8 @@
mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
- verify(mWindowMagnificationManager).disableWindowMagnification(eq(TEST_DISPLAY), eq(false));
+ verify(mMagnificationConnectionManager)
+ .disableWindowMagnification(eq(TEST_DISPLAY), eq(false));
}
@Test
@@ -983,11 +992,11 @@
// The first time is triggered when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
// The third time is triggered when user interaction changed.
- verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1001,11 +1010,11 @@
// The first time is triggered when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
// The third time is triggered when user interaction changed.
- verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1018,11 +1027,11 @@
// The first time is triggered when the window mode is activated.
// The second time is triggered when user interaction changed.
- verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1035,11 +1044,11 @@
// The first time is triggered when the window mode is activated.
// The second time is triggered when user interaction changed.
- verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1053,11 +1062,11 @@
mMagnificationController.onTouchInteractionStart(TEST_DISPLAY, MODE_FULLSCREEN);
mMagnificationController.onTouchInteractionEnd(TEST_DISPLAY, MODE_FULLSCREEN);
- verify(mWindowMagnificationManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// The first time is triggered when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
- verify(mWindowMagnificationManager, times(2)).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, times(2)).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1071,9 +1080,9 @@
mMagnificationController.onTouchInteractionStart(TEST_DISPLAY, MODE_FULLSCREEN);
mMagnificationController.onTouchInteractionEnd(TEST_DISPLAY, MODE_FULLSCREEN);
- verify(mWindowMagnificationManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, never()).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
- verify(mWindowMagnificationManager, times(2)).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, times(2)).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1082,11 +1091,11 @@
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1100,11 +1109,11 @@
// The first time is triggered when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
// The third time is triggered when fullscreen mode activation state is updated.
- verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// Never call removeMagnificationSettingsPanel if it is allowed to show the settings panel
// in current capability and mode, and the magnification is activated.
- verify(mWindowMagnificationManager, never()).removeMagnificationSettingsPanel(
+ verify(mMagnificationConnectionManager, never()).removeMagnificationSettingsPanel(
eq(TEST_DISPLAY));
}
@@ -1113,10 +1122,10 @@
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
- verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationButton(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1126,8 +1135,8 @@
setMagnificationEnabled(MODE_FULLSCREEN);
mScreenMagnificationController.reset(TEST_DISPLAY, /* animate= */ true);
- verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationButton(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1142,10 +1151,10 @@
// The first time is triggered when fullscreen mode is activated.
// The second time is triggered when magnification spec is changed.
// The third time is triggered when the disable-magnification callback is triggered.
- verify(mWindowMagnificationManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(3)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
// It is triggered when the disable-magnification callback is triggered.
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1163,10 +1172,10 @@
// The first time is triggered when window mode is activated.
// The second time is triggered when the disable-magnification callback is triggered.
- verify(mWindowMagnificationManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
+ verify(mMagnificationConnectionManager, times(2)).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
// It is triggered when the disable-magnification callback is triggered.
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1174,9 +1183,9 @@
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+ mMagnificationConnectionManager.disableWindowMagnification(TEST_DISPLAY, false);
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1185,7 +1194,7 @@
setMagnificationEnabled(MODE_FULLSCREEN);
mScreenMagnificationController.reset(TEST_DISPLAY, /* animate= */ true);
- verify(mWindowMagnificationManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
+ verify(mMagnificationConnectionManager).removeMagnificationSettingsPanel(eq(TEST_DISPLAY));
}
@Test
@@ -1260,17 +1269,17 @@
private void activateMagnifier(int displayId, int mode, float centerX, float centerY)
throws RemoteException {
- final boolean windowMagnifying = mWindowMagnificationManager.isWindowMagnifierEnabled(
+ final boolean windowMagnifying = mMagnificationConnectionManager.isWindowMagnifierEnabled(
displayId);
if (windowMagnifying) {
- mWindowMagnificationManager.disableWindowMagnification(displayId, false);
+ mMagnificationConnectionManager.disableWindowMagnification(displayId, false);
mMockConnection.invokeCallbacks();
}
if (mode == MODE_FULLSCREEN) {
mScreenMagnificationController.setScaleAndCenter(displayId, DEFAULT_SCALE, centerX,
centerY, true, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
} else {
- mWindowMagnificationManager.enableWindowMagnification(displayId, DEFAULT_SCALE,
+ mMagnificationConnectionManager.enableWindowMagnification(displayId, DEFAULT_SCALE,
centerX, centerY, null, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
}
@@ -1304,10 +1313,10 @@
}
private static class WindowMagnificationMgrCallbackDelegate implements
- WindowMagnificationManager.Callback {
- private WindowMagnificationManager.Callback mCallback;
+ MagnificationConnectionManager.Callback {
+ private MagnificationConnectionManager.Callback mCallback;
- public void setDelegate(WindowMagnificationManager.Callback callback) {
+ public void setDelegate(MagnificationConnectionManager.Callback callback) {
mCallback = callback;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index 612a091..c4be51f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -92,7 +92,7 @@
public final TestableContext mContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getContext());
- private WindowMagnificationManager mWindowMagnificationManager;
+ private MagnificationConnectionManager mMagnificationConnectionManager;
private MockWindowMagnificationConnection mMockConnection;
private SpyWindowMagnificationGestureHandler mWindowMagnificationGestureHandler;
private WindowMagnificationGestureHandler mMockWindowMagnificationGestureHandler;
@@ -104,23 +104,23 @@
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
- mWindowMagnificationManager = new WindowMagnificationManager(mContext, new Object(),
- mock(WindowMagnificationManager.Callback.class), mMockTrace,
+ mMagnificationConnectionManager = new MagnificationConnectionManager(mContext, new Object(),
+ mock(MagnificationConnectionManager.Callback.class), mMockTrace,
new MagnificationScaleProvider(mContext));
mMockConnection = new MockWindowMagnificationConnection();
mWindowMagnificationGestureHandler = new SpyWindowMagnificationGestureHandler(
- mContext, mWindowMagnificationManager, mMockTrace, mMockCallback,
+ mContext, mMagnificationConnectionManager, mMockTrace, mMockCallback,
/** detectSingleFingerTripleTap= */ true, /** detectTwoFingerTripleTap= */ true,
/** detectShortcutTrigger= */ true, DISPLAY_0);
mMockWindowMagnificationGestureHandler =
mWindowMagnificationGestureHandler.getMockGestureHandler();
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationConnectionManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationGestureHandler.setNext(strictMock(EventStreamTransformation.class));
}
@After
public void tearDown() {
- mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, true);
+ mMagnificationConnectionManager.disableWindowMagnification(DISPLAY_0, true);
}
@Test
@@ -378,7 +378,7 @@
}
break;
case STATE_SHOW_MAGNIFIER_SHORTCUT: {
- mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, false);
+ mMagnificationConnectionManager.disableWindowMagnification(DISPLAY_0, false);
}
break;
case STATE_TWO_FINGERS_DOWN: {
@@ -423,7 +423,7 @@
}
private boolean isWindowMagnifierEnabled(int displayId) {
- return mWindowMagnificationManager.isWindowMagnifierEnabled(displayId);
+ return mMagnificationConnectionManager.isWindowMagnifierEnabled(displayId);
}
private static String stateToString(int state) {
@@ -495,13 +495,14 @@
private final WindowMagnificationGestureHandler mMockWindowMagnificationGestureHandler;
SpyWindowMagnificationGestureHandler(@UiContext Context context,
- WindowMagnificationManager windowMagnificationMgr,
+ MagnificationConnectionManager magnificationConnectionManager,
AccessibilityTraceManager trace,
Callback callback,
boolean detectSingleFingerTripleTap, boolean detectTwoFingerTripleTap,
boolean detectShortcutTrigger, int displayId) {
- super(context, windowMagnificationMgr, trace, callback, detectSingleFingerTripleTap,
- detectTwoFingerTripleTap, detectShortcutTrigger, displayId);
+ super(context, magnificationConnectionManager, trace, callback,
+ detectSingleFingerTripleTap, detectTwoFingerTripleTap,
+ detectShortcutTrigger, displayId);
mMockWindowMagnificationGestureHandler = mock(WindowMagnificationGestureHandler.class);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
deleted file mode 100644
index 24ad976..0000000
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ /dev/null
@@ -1,847 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.accessibility.magnification;
-
-import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY;
-import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY_2;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-
-import static java.lang.Float.NaN;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.test.mock.MockContentResolver;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-import android.view.accessibility.IWindowMagnificationConnectionCallback;
-import android.view.accessibility.MagnificationAnimationCallback;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.FlakyTest;
-
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.LocalServices;
-import com.android.server.accessibility.AccessibilityTraceManager;
-import com.android.server.statusbar.StatusBarManagerInternal;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-
-/**
- * Tests for WindowMagnificationManager.
- */
-public class WindowMagnificationManagerTest {
-
- private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
- private static final int SERVICE_ID = 1;
-
- private MockWindowMagnificationConnection mMockConnection;
- @Mock
- private Context mContext;
- @Mock
- private AccessibilityTraceManager mMockTrace;
- @Mock
- private StatusBarManagerInternal mMockStatusBarManagerInternal;
- @Mock
- private MagnificationAnimationCallback mAnimationCallback;
- @Mock
- private WindowMagnificationManager.Callback mMockCallback;
- private MockContentResolver mResolver;
- private WindowMagnificationManager mWindowMagnificationManager;
-
- @Before
- public void setUp() throws RemoteException {
- MockitoAnnotations.initMocks(this);
- LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
- LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
- mResolver = new MockContentResolver();
- mMockConnection = new MockWindowMagnificationConnection();
- mWindowMagnificationManager = new WindowMagnificationManager(mContext, new Object(),
- mMockCallback, mMockTrace, new MagnificationScaleProvider(mContext));
-
- when(mContext.getContentResolver()).thenReturn(mResolver);
- stubSetConnection(false);
-
- mResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- Settings.Secure.putFloatForUser(mResolver,
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 2.5f,
- CURRENT_USER_ID);
- }
-
- private void stubSetConnection(boolean needDelay) {
- doAnswer((InvocationOnMock invocation) -> {
- final boolean connect = (Boolean) invocation.getArguments()[0];
- // Simulates setConnection() called by another process.
- if (needDelay) {
- final Context context = ApplicationProvider.getApplicationContext();
- context.getMainThreadHandler().postDelayed(
- () -> {
- mWindowMagnificationManager.setConnection(
- connect ? mMockConnection.getConnection() : null);
- }, 10);
- } else {
- mWindowMagnificationManager.setConnection(
- connect ? mMockConnection.getConnection() : null);
- }
- return true;
- }).when(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(anyBoolean());
- }
-
- @Test
- public void setConnection_connectionIsNull_wrapperIsNullAndLinkToDeath() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
- verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
- }
-
- @Test
- public void setConnection_connectionIsNull_setMirrorWindowCallbackAndHasWrapper()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
- verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
- verify(mMockConnection.getConnection()).setConnectionCallback(
- any(IWindowMagnificationConnectionCallback.class));
- }
-
- @Test
- public void binderDied_hasConnection_wrapperIsNullAndUnlinkToDeath() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- mMockConnection.getDeathRecipient().binderDied();
-
- assertNull(mWindowMagnificationManager.mConnectionWrapper);
- verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(),
- 0);
- }
-
- /**
- * This test simulates {@link WindowMagnificationManager#setConnection} is called by thread A
- * and then the former connection is called by thread B. In this situation we should keep the
- * new connection.
- */
- @Test
- public void setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- MockWindowMagnificationConnection secondConnection =
- new MockWindowMagnificationConnection();
-
- mWindowMagnificationManager.setConnection(secondConnection.getConnection());
- mMockConnection.getDeathRecipient().binderDied();
-
- assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
- verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(), 0);
- verify(secondConnection.asBinder(), never()).unlinkToDeath(
- secondConnection.getDeathRecipient(), 0);
- }
-
- @Test
- public void setNullConnection_hasConnection_wrapperIsNull() throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- mWindowMagnificationManager.setConnection(null);
-
- assertNull(mWindowMagnificationManager.mConnectionWrapper);
- verify(mMockConnection.getConnection()).setConnectionCallback(null);
- }
-
- @Test
- public void enableWithAnimation_hasConnection_enableWindowMagnification()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f);
-
- verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
- eq(200f), eq(300f), eq(0f), eq(0f), notNull());
- }
-
- @Test
- public void enableWithCallback_hasConnection_enableWindowMagnification()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f,
- mAnimationCallback, SERVICE_ID);
-
- verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
- eq(200f), eq(300f), eq(0f), eq(0f),
- any(IRemoteMagnificationAnimationCallback.class));
- verify(mAnimationCallback).onResult(true);
- }
-
- @Test
- public void disable_hasConnectionAndEnabled_disableWindowMagnification()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
-
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
-
- verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
- notNull());
- }
-
- @Test
- public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
-
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false,
- mAnimationCallback);
-
- verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
- any(IRemoteMagnificationAnimationCallback.class));
- verify(mAnimationCallback).onResult(true);
- }
-
- @Test
- public void isWindowMagnifierEnabled_hasConnectionAndEnabled_returnExpectedValue() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
-
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
-
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- }
-
- @Test
- public void getPersistedScale() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- assertEquals(mWindowMagnificationManager.getPersistedScale(TEST_DISPLAY), 2.5f);
- }
-
- @Test
- public void persistScale_setValue_expectedValueInProvider() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
- mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f);
-
- mWindowMagnificationManager.persistScale(TEST_DISPLAY);
-
- assertEquals(Settings.Secure.getFloatForUser(mResolver,
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f,
- CURRENT_USER_ID), 2.5f);
- }
-
- @Test
- public void persistScale_setValueWhenScaleIsOne_nothingChanged() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- final float persistedScale = mWindowMagnificationManager.getPersistedScale(TEST_DISPLAY);
-
- mWindowMagnificationManager.setScale(TEST_DISPLAY, 1.0f);
- mWindowMagnificationManager.persistScale(TEST_DISPLAY);
-
- assertEquals(Settings.Secure.getFloatForUser(mResolver,
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0f,
- CURRENT_USER_ID), persistedScale);
- }
-
- @Test
- public void scaleSetterGetter_enabledOnTestDisplay_expectedValue() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
-
- mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f);
-
- assertEquals(mWindowMagnificationManager.getScale(TEST_DISPLAY), 2.5f);
- }
-
- @Test
- public void scaleSetterGetter_scaleIsOutOfRang_getNormalizeValue() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
-
- mWindowMagnificationManager.setScale(TEST_DISPLAY, 10.0f);
-
- assertEquals(mWindowMagnificationManager.getScale(TEST_DISPLAY),
- MagnificationScaleProvider.MAX_SCALE);
- }
-
- @FlakyTest(bugId = 297879435)
- @Test
- public void logTrackingTypingFocus_processScroll_logDuration() {
- WindowMagnificationManager spyWindowMagnificationManager = spy(mWindowMagnificationManager);
- spyWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- spyWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, /* shown */ true);
-
- spyWindowMagnificationManager.processScroll(TEST_DISPLAY, 10f, 10f);
-
- verify(spyWindowMagnificationManager).logTrackingTypingFocus(anyLong());
- }
-
- @Test
- public void onRectangleOnScreenRequested_trackingDisabledByOnDrag_withoutMovingMagnifier()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection(), never())
- .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
- }
-
-
- @Test
- public void onRectangleOnScreenRequested_trackingDisabledByScroll_withoutMovingMagnifier()
- throws RemoteException {
- final float distanceX = 10f;
- final float distanceY = 10f;
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.processScroll(TEST_DISPLAY, distanceX, distanceY);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection(), never())
- .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
- }
-
- @Test
- public void onRectangleOnScreenRequested_requestRectangleInBound_withoutMovingMagnifier()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.inset(-10, -10);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection(), never())
- .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
- }
- @Test
- public void onRectangleOnScreenRequested_imeVisibilityDefaultInvisible_withoutMovingMagnifier()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection(), never())
- .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
- }
-
- @Test
- public void onRectangleOnScreenRequested_trackingEnabledByDefault_movingMagnifier()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
- eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
- any(IRemoteMagnificationAnimationCallback.class));
- }
-
- @Test
- public void onRectangleOnScreenRequested_imeInvisible_withoutMovingMagnifier()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, false);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection(), never())
- .moveWindowMagnifierToPosition(anyInt(), anyFloat(), anyFloat(), any());
- }
-
- @Test
- public void onRectangleOnScreenRequested_trackingEnabledByDragAndReset_movingMagnifier()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- mMockConnection.getConnectionCallback().onMove(TEST_DISPLAY);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region outRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, outRegion);
- final Rect requestedRect = outRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
- eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
- any(IRemoteMagnificationAnimationCallback.class));
- }
-
- @Test
- public void onRectangleOnScreenRequested_followTypingIsDisabled_withoutMovingMagnifier() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- final Region beforeRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
- final Rect requestedRect = beforeRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.setMagnificationFollowTypingEnabled(false);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- final Region afterRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
- assertEquals(afterRegion, beforeRegion);
- }
-
- @Test
- public void onRectangleOnScreenRequested_trackingDisabled_withoutMovingMagnifier() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- mWindowMagnificationManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
- final Region beforeRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
- final Rect requestedRect = beforeRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- final Region afterRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, afterRegion);
- assertEquals(afterRegion, beforeRegion);
- }
-
- @Test
- public void onRectangleOnScreenRequested_trackingDisabledAndEnabledMagnifier_movingMagnifier()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, 50f, 50f);
- mWindowMagnificationManager.onImeWindowVisibilityChanged(TEST_DISPLAY, true);
- mWindowMagnificationManager.setTrackingTypingFocusEnabled(TEST_DISPLAY, false);
- final Region beforeRegion = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, beforeRegion);
- final Rect requestedRect = beforeRegion.getBounds();
- requestedRect.offsetTo(requestedRect.right + 10, requestedRect.bottom + 10);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
- // Enabling a window magnifier again will turn on the tracking typing focus functionality.
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, NaN, NaN, NaN);
-
- mWindowMagnificationManager.onRectangleOnScreenRequested(TEST_DISPLAY,
- requestedRect.left, requestedRect.top, requestedRect.right, requestedRect.bottom);
-
- verify(mMockConnection.getConnection()).moveWindowMagnifierToPosition(eq(TEST_DISPLAY),
- eq(requestedRect.exactCenterX()), eq(requestedRect.exactCenterY()),
- any(IRemoteMagnificationAnimationCallback.class));
- }
-
- @Test
- public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
-
- mWindowMagnificationManager.moveWindowMagnification(TEST_DISPLAY, 200, 300);
- verify(mMockConnection.getConnection()).moveWindowMagnifier(TEST_DISPLAY, 200, 300);
- }
-
- @Test
- public void showMagnificationButton_hasConnection_invokeConnectionMethod()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- mWindowMagnificationManager.showMagnificationButton(TEST_DISPLAY,
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
- verify(mMockConnection.getConnection()).showMagnificationButton(TEST_DISPLAY,
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-
- mWindowMagnificationManager.removeMagnificationButton(TEST_DISPLAY);
- verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
- }
-
- @Test
- public void removeMagnificationSettingsPanel_hasConnection_invokeConnectionMethod()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- mWindowMagnificationManager.removeMagnificationSettingsPanel(TEST_DISPLAY);
- verify(mMockConnection.getConnection()).removeMagnificationSettingsPanel(TEST_DISPLAY);
- }
-
- @Test
- public void onUserMagnificationScaleChanged_hasConnection_invokeConnectionMethod()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-
- final float testScale = 3f;
- mWindowMagnificationManager.onUserMagnificationScaleChanged(
- CURRENT_USER_ID, TEST_DISPLAY, testScale);
- verify(mMockConnection.getConnection()).onUserMagnificationScaleChanged(
- eq(CURRENT_USER_ID), eq(TEST_DISPLAY), eq(testScale));
- }
-
- @Test
- public void pointersInWindow_magnifierEnabled_returnCorrectValue() throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
- mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(TEST_DISPLAY,
- new Rect(0, 0, 500, 500));
- PointF[] pointersLocation = new PointF[2];
- pointersLocation[0] = new PointF(600, 700);
- pointersLocation[1] = new PointF(300, 400);
- MotionEvent event = generatePointersDownEvent(pointersLocation);
-
- assertEquals(mWindowMagnificationManager.pointersInWindow(TEST_DISPLAY, event), 1);
- }
-
- @Test
- public void onPerformScaleAction_magnifierEnabled_notifyAction() throws RemoteException {
- final float newScale = 4.0f;
- final boolean updatePersistence = true;
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
-
- mMockConnection.getConnectionCallback().onPerformScaleAction(
- TEST_DISPLAY, newScale, updatePersistence);
-
- verify(mMockCallback).onPerformScaleAction(
- eq(TEST_DISPLAY), eq(newScale), eq(updatePersistence));
- }
-
- @Test
- public void onAccessibilityActionPerformed_magnifierEnabled_notifyAction()
- throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
-
- mMockConnection.getConnectionCallback().onAccessibilityActionPerformed(TEST_DISPLAY);
-
- verify(mMockCallback).onAccessibilityActionPerformed(eq(TEST_DISPLAY));
- }
-
- @Test
- public void binderDied_windowMagnifierIsEnabled_resetState() throws RemoteException {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
-
- mMockConnection.getDeathRecipient().binderDied();
-
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- }
-
- @Test
- public void
- requestConnectionToNull_disableAllMagnifiersAndRequestWindowMagnificationConnection()
- throws RemoteException {
- assertTrue(mWindowMagnificationManager.requestConnection(true));
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
-
- assertTrue(mWindowMagnificationManager.requestConnection(false));
-
- verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
- verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(false);
- }
-
- @Test
- public void requestConnection_requestWindowMagnificationConnection() throws RemoteException {
- assertTrue(mWindowMagnificationManager.requestConnection(true));
- verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(true);
- }
-
- @Test
- public void isConnected_requestConnection_expectedValue() throws RemoteException {
- mWindowMagnificationManager.requestConnection(true);
- assertTrue(mWindowMagnificationManager.isConnected());
-
- mWindowMagnificationManager.requestConnection(false);
- assertFalse(mWindowMagnificationManager.isConnected());
- }
-
- @Test
- public void requestConnection_registerAndUnregisterBroadcastReceiver() {
- assertTrue(mWindowMagnificationManager.requestConnection(true));
- verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
-
- assertTrue(mWindowMagnificationManager.requestConnection(false));
- verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
- }
-
- @Test
- public void requestConnectionToNull_expectedGetterResults() {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1);
-
- mWindowMagnificationManager.requestConnection(false);
-
- assertEquals(1f, mWindowMagnificationManager.getScale(TEST_DISPLAY), 0);
- assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterX(TEST_DISPLAY)));
- assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterY(TEST_DISPLAY)));
- final Region bounds = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
- assertTrue(bounds.isEmpty());
- }
-
- @Test
- public void enableWindowMagnification_connecting_invokeConnectionMethodAfterConnected()
- throws RemoteException {
- stubSetConnection(true);
- mWindowMagnificationManager.requestConnection(true);
-
- assertTrue(mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1));
-
- // Invoke enableWindowMagnification if the connection is connected.
- verify(mMockConnection.getConnection()).enableWindowMagnification(
- eq(TEST_DISPLAY), eq(3f),
- eq(1f), eq(1f), eq(0f), eq(0f), notNull());
- }
-
- @Test
- public void resetAllMagnification_enabledBySameId_windowMagnifiersDisabled() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
- 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
- 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
-
- mWindowMagnificationManager.resetAllIfNeeded(SERVICE_ID);
-
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
- }
-
- @Test
- public void resetAllMagnification_enabledByDifferentId_windowMagnifierDisabled() {
- final int serviceId2 = SERVICE_ID + 1;
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
- 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
- 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, serviceId2);
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
-
- mWindowMagnificationManager.resetAllIfNeeded(SERVICE_ID);
-
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
- }
-
- @Test
- public void onScreenOff_windowMagnifierIsEnabled_removeButtonAndDisableWindowMagnification()
- throws RemoteException {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
-
- mWindowMagnificationManager.mScreenStateReceiver.onReceive(mContext,
- new Intent(Intent.ACTION_SCREEN_OFF));
-
- verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
- verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- }
-
- @Test
- public void centerGetter_enabledOnTestDisplay_expectedValues() {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
-
- assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
- assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
- }
-
- @Test
- public void centerGetter_enabledOnTestDisplayWindowAtCenter_expectedValues()
- throws RemoteException {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
- 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
-
- assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
- assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
-
- verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
- eq(100f), eq(200f), eq(0f), eq(0f), notNull());
- }
-
- @Test
- public void centerGetter_enabledOnTestDisplayWindowAtLeftTop_expectedValues()
- throws RemoteException {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
- 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT);
-
- assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
- assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
-
- verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(3f),
- eq(100f), eq(200f), eq(-1f), eq(-1f), notNull());
- }
-
- @Test
- public void magnifierGetters_disabled_expectedValues() {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
- 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
-
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
-
- assertEquals(1f, mWindowMagnificationManager.getScale(TEST_DISPLAY), 0);
- assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterX(TEST_DISPLAY)));
- assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterY(TEST_DISPLAY)));
- final Region bounds = new Region();
- mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
- assertTrue(bounds.isEmpty());
- }
-
- @Test
- public void onDisplayRemoved_enabledOnTestDisplay_disabled() {
- mWindowMagnificationManager.requestConnection(true);
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
-
- mWindowMagnificationManager.onDisplayRemoved(TEST_DISPLAY);
-
- assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- }
-
- @Test
- public void onWindowMagnificationActivationState_magnifierEnabled_notifyActivatedState() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
-
- verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, true);
- }
-
- @Test
- public void onWindowMagnificationActivationState_magnifierDisabled_notifyDeactivatedState() {
- mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
-
- verify(mMockCallback).onWindowMagnificationActivationState(TEST_DISPLAY, false);
-
- Mockito.reset(mMockCallback);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
-
- verify(mMockCallback, never()).onWindowMagnificationActivationState(eq(TEST_DISPLAY),
- anyBoolean());
- }
-
- private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
- final int len = pointersLocation.length;
-
- final MotionEvent.PointerProperties[] pp = new MotionEvent.PointerProperties[len];
- for (int i = 0; i < len; i++) {
- MotionEvent.PointerProperties pointerProperty = new MotionEvent.PointerProperties();
- pointerProperty.id = i;
- pointerProperty.toolType = MotionEvent.TOOL_TYPE_FINGER;
- pp[i] = pointerProperty;
- }
-
- final MotionEvent.PointerCoords[] pc = new MotionEvent.PointerCoords[len];
- for (int i = 0; i < len; i++) {
- MotionEvent.PointerCoords pointerCoord = new MotionEvent.PointerCoords();
- pointerCoord.x = pointersLocation[i].x;
- pointerCoord.y = pointersLocation[i].y;
- pc[i] = pointerCoord;
- }
-
- return MotionEvent.obtain(
- /* downTime */ SystemClock.uptimeMillis(),
- /* eventTime */ SystemClock.uptimeMillis(),
- /* action */ MotionEvent.ACTION_POINTER_DOWN,
- /* pointerCount */ pc.length,
- /* pointerProperties */ pp,
- /* pointerCoords */ pc,
- /* metaState */ 0,
- /* buttonState */ 0,
- /* xPrecision */ 1.0f,
- /* yPrecision */ 1.0f,
- /* deviceId */ 0,
- /* edgeFlags */ 0,
- /* source */ InputDevice.SOURCE_TOUCHSCREEN,
- /* flags */ 0);
- }
-
-
-}
diff --git a/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
index 749b07d..42c5205 100644
--- a/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/LoudnessCodecHelperTest.java
@@ -19,6 +19,16 @@
import static android.media.AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID;
import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_4;
import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_D;
+import static android.media.MediaFormat.KEY_AAC_DRC_EFFECT_TYPE;
+import static android.media.MediaFormat.KEY_AAC_DRC_HEAVY_COMPRESSION;
+import static android.media.MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL;
+
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_LARGE;
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_MEDIUM;
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_SMALL;
+import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_UNKNOWN;
+
+import static junit.framework.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -34,11 +44,15 @@
import android.media.LoudnessCodecInfo;
import android.media.PlayerBase;
import android.os.IBinder;
+import android.os.PersistableBundle;
import android.platform.test.annotations.Presubmit;
import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.server.audio.LoudnessCodecHelper.DeviceSplRange;
+import com.android.server.audio.LoudnessCodecHelper.LoudnessCodecInputProperties;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -200,6 +214,108 @@
any());
}
+ @Test
+ public void checkParcelableBundle_forMpeg4CodecInputProperties() {
+ PersistableBundle loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+ SPL_RANGE_SMALL).createLoudnessParameters();
+ assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+ SPL_RANGE_SMALL).createLoudnessParameters();
+ assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+ SPL_RANGE_MEDIUM).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+ SPL_RANGE_MEDIUM).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+ SPL_RANGE_LARGE).createLoudnessParameters();
+ assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+ SPL_RANGE_LARGE).createLoudnessParameters();
+ assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
+ SPL_RANGE_UNKNOWN).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
+ SPL_RANGE_UNKNOWN).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
+ }
+
+ @Test
+ public void checkParcelableBundle_forMpegDCodecInputProperties() {
+ PersistableBundle loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+ SPL_RANGE_SMALL).createLoudnessParameters();
+ assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(3, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+ SPL_RANGE_SMALL).createLoudnessParameters();
+ assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(3, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+ SPL_RANGE_MEDIUM).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+ SPL_RANGE_MEDIUM).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+ SPL_RANGE_LARGE).createLoudnessParameters();
+ assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+ SPL_RANGE_LARGE).createLoudnessParameters();
+ assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
+ SPL_RANGE_UNKNOWN).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+
+ loudnessParameters = createInputProperties(
+ CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
+ SPL_RANGE_UNKNOWN).createLoudnessParameters();
+ assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
+ assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
+ }
+
private List<AudioPlaybackConfiguration> getApcListForPiids(int... piids) {
final ArrayList<AudioPlaybackConfiguration> apcList = new ArrayList<>();
@@ -220,6 +336,12 @@
return apcList;
}
+ 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(int mediaCodecHash, boolean isDownmixing,
int metadataType) {
LoudnessCodecInfo info = new LoudnessCodecInfo();
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index b5ba322..9213601a 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -413,18 +413,24 @@
public void getDeviceIdForDisplayId_invalidDisplayId_returnsDefault() {
assertThat(mVdm.getDeviceIdForDisplayId(Display.INVALID_DISPLAY))
.isEqualTo(DEVICE_ID_DEFAULT);
+ assertThat(mLocalService.getDeviceIdForDisplayId(Display.INVALID_DISPLAY))
+ .isEqualTo(DEVICE_ID_DEFAULT);
}
@Test
public void getDeviceIdForDisplayId_defaultDisplayId_returnsDefault() {
assertThat(mVdm.getDeviceIdForDisplayId(Display.DEFAULT_DISPLAY))
.isEqualTo(DEVICE_ID_DEFAULT);
+ assertThat(mLocalService.getDeviceIdForDisplayId(Display.DEFAULT_DISPLAY))
+ .isEqualTo(DEVICE_ID_DEFAULT);
}
@Test
public void getDeviceIdForDisplayId_nonExistentDisplayId_returnsDefault() {
assertThat(mVdm.getDeviceIdForDisplayId(NON_EXISTENT_DISPLAY_ID))
.isEqualTo(DEVICE_ID_DEFAULT);
+ assertThat(mLocalService.getDeviceIdForDisplayId(NON_EXISTENT_DISPLAY_ID))
+ .isEqualTo(DEVICE_ID_DEFAULT);
}
@Test
@@ -433,6 +439,8 @@
assertThat(mVdm.getDeviceIdForDisplayId(DISPLAY_ID_1))
.isEqualTo(mDeviceImpl.getDeviceId());
+ assertThat(mLocalService.getDeviceIdForDisplayId(DISPLAY_ID_1))
+ .isEqualTo(mDeviceImpl.getDeviceId());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
index 01922e0..edfe1b4 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
@@ -40,6 +40,7 @@
import android.testing.TestableLooper;
import android.view.Surface;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -77,6 +78,11 @@
when(mVirtualCameraServiceMock.registerCamera(any(), any())).thenReturn(true);
}
+ @After
+ public void tearDown() throws Exception {
+ mVirtualCameraController.close();
+ }
+
@Test
public void registerCamera_registersCamera() throws Exception {
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
@@ -95,6 +101,8 @@
public void unregisterCamera_unregistersCamera() throws Exception {
VirtualCameraConfig config = createVirtualCameraConfig(
CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_1);
+ mVirtualCameraController.registerCamera(config);
+
mVirtualCameraController.unregisterCamera(config);
verify(mVirtualCameraServiceMock).unregisterCamera(any());
@@ -107,9 +115,10 @@
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT, CAMERA_DISPLAY_NAME_RES_ID_2));
+ mVirtualCameraController.close();
+
ArgumentCaptor<VirtualCameraConfiguration> configurationCaptor =
ArgumentCaptor.forClass(VirtualCameraConfiguration.class);
- mVirtualCameraController.close();
verify(mVirtualCameraServiceMock, times(2)).registerCamera(any(),
configurationCaptor.capture());
List<VirtualCameraConfiguration> virtualCameraConfigurations =
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
index 3a3ab84..dd687fd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -27,9 +27,9 @@
import android.os.Build;
import android.platform.test.annotations.Presubmit;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.PackageState;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index 57b1225..d70a4fd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -60,7 +60,8 @@
.setShowInLauncher(21)
.setStartWithParent(false)
.setShowInSettings(45)
- .setHideInSettingsInQuietMode(false)
+ .setShowInSharingSurfaces(78)
+ .setShowInQuietMode(12)
.setInheritDevicePolicy(67)
.setUseParentsContacts(false)
.setCrossProfileIntentFilterAccessControl(10)
@@ -74,7 +75,8 @@
final UserProperties actualProps = new UserProperties(defaultProps);
actualProps.setShowInLauncher(14);
actualProps.setShowInSettings(32);
- actualProps.setHideInSettingsInQuietMode(true);
+ actualProps.setShowInSharingSurfaces(46);
+ actualProps.setShowInQuietMode(27);
actualProps.setInheritDevicePolicy(51);
actualProps.setUseParentsContacts(true);
actualProps.setCrossProfileIntentFilterAccessControl(20);
@@ -236,8 +238,10 @@
assertThat(expected.getShowInLauncher()).isEqualTo(actual.getShowInLauncher());
assertThat(expected.getStartWithParent()).isEqualTo(actual.getStartWithParent());
assertThat(expected.getShowInSettings()).isEqualTo(actual.getShowInSettings());
- assertThat(expected.getHideInSettingsInQuietMode())
- .isEqualTo(actual.getHideInSettingsInQuietMode());
+ assertThat(expected.getShowInSharingSurfaces()).isEqualTo(
+ actual.getShowInSharingSurfaces());
+ assertThat(expected.getShowInQuietMode())
+ .isEqualTo(actual.getShowInQuietMode());
assertThat(expected.getInheritDevicePolicy()).isEqualTo(actual.getInheritDevicePolicy());
assertThat(expected.getUseParentsContacts()).isEqualTo(actual.getUseParentsContacts());
assertThat(expected.getCrossProfileIntentFilterAccessControl())
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index 48eb5c6..77f6939 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -29,6 +29,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
@@ -91,7 +92,8 @@
.setCredentialShareableWithParent(false)
.setAuthAlwaysRequiredToDisableQuietMode(true)
.setShowInSettings(900)
- .setHideInSettingsInQuietMode(true)
+ .setShowInSharingSurfaces(20)
+ .setShowInQuietMode(30)
.setInheritDevicePolicy(340)
.setDeleteAppWithParent(true)
.setAlwaysVisible(true);
@@ -107,9 +109,9 @@
.setIconBadge(28)
.setBadgePlain(29)
.setBadgeNoBackground(30)
- .setLabel(31)
.setMaxAllowedPerParent(32)
.setStatusBarIcon(33)
+ .setLabels(34, 35, 36)
.setDefaultRestrictions(restrictions)
.setDefaultSystemSettings(systemSettings)
.setDefaultSecureSettings(secureSettings)
@@ -124,9 +126,11 @@
assertEquals(28, type.getIconBadge());
assertEquals(29, type.getBadgePlain());
assertEquals(30, type.getBadgeNoBackground());
- assertEquals(31, type.getLabel());
assertEquals(32, type.getMaxAllowedPerParent());
assertEquals(33, type.getStatusBarIcon());
+ assertEquals(34, type.getLabel(0));
+ assertEquals(35, type.getLabel(1));
+ assertEquals(36, type.getLabel(2));
assertTrue(UserRestrictionsUtils.areEqual(restrictions, type.getDefaultRestrictions()));
assertNotSame(restrictions, type.getDefaultRestrictions());
@@ -164,7 +168,9 @@
assertTrue(type.getDefaultUserPropertiesReference()
.isAuthAlwaysRequiredToDisableQuietMode());
assertEquals(900, type.getDefaultUserPropertiesReference().getShowInSettings());
- assertTrue(type.getDefaultUserPropertiesReference().getHideInSettingsInQuietMode());
+ assertEquals(20, type.getDefaultUserPropertiesReference().getShowInSharingSurfaces());
+ assertEquals(30,
+ type.getDefaultUserPropertiesReference().getShowInQuietMode());
assertEquals(340, type.getDefaultUserPropertiesReference()
.getInheritDevicePolicy());
assertTrue(type.getDefaultUserPropertiesReference().getDeleteAppWithParent());
@@ -203,7 +209,7 @@
assertEquals(Resources.ID_NULL, type.getStatusBarIcon());
assertEquals(Resources.ID_NULL, type.getBadgeLabel(0));
assertEquals(Resources.ID_NULL, type.getBadgeColor(0));
- assertEquals(Resources.ID_NULL, type.getLabel());
+ assertEquals(Resources.ID_NULL, type.getLabel(0));
assertTrue(type.getDefaultRestrictions().isEmpty());
assertTrue(type.getDefaultSystemSettings().isEmpty());
assertTrue(type.getDefaultSecureSettings().isEmpty());
@@ -222,7 +228,9 @@
assertFalse(props.isCredentialShareableWithParent());
assertFalse(props.getDeleteAppWithParent());
assertFalse(props.getAlwaysVisible());
- assertFalse(props.getHideInSettingsInQuietMode());
+ assertEquals(UserProperties.SHOW_IN_LAUNCHER_SEPARATE, props.getShowInSharingSurfaces());
+ assertEquals(UserProperties.SHOW_IN_QUIET_MODE_PAUSED,
+ props.getShowInQuietMode());
assertFalse(type.hasBadge());
}
@@ -311,8 +319,9 @@
.setCredentialShareableWithParent(true)
.setAuthAlwaysRequiredToDisableQuietMode(false)
.setShowInSettings(20)
- .setHideInSettingsInQuietMode(false)
.setInheritDevicePolicy(21)
+ .setShowInSharingSurfaces(22)
+ .setShowInQuietMode(24)
.setDeleteAppWithParent(true)
.setAlwaysVisible(false);
@@ -354,9 +363,11 @@
assertFalse(aospType.getDefaultUserPropertiesReference()
.isAuthAlwaysRequiredToDisableQuietMode());
assertEquals(20, aospType.getDefaultUserPropertiesReference().getShowInSettings());
- assertFalse(aospType.getDefaultUserPropertiesReference().getHideInSettingsInQuietMode());
assertEquals(21, aospType.getDefaultUserPropertiesReference()
.getInheritDevicePolicy());
+ assertEquals(22, aospType.getDefaultUserPropertiesReference().getShowInSharingSurfaces());
+ assertEquals(24,
+ aospType.getDefaultUserPropertiesReference().getShowInQuietMode());
assertTrue(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
assertFalse(aospType.getDefaultUserPropertiesReference().getAlwaysVisible());
@@ -403,7 +414,10 @@
assertTrue(aospType.getDefaultUserPropertiesReference()
.isAuthAlwaysRequiredToDisableQuietMode());
assertEquals(23, aospType.getDefaultUserPropertiesReference().getShowInSettings());
- assertTrue(aospType.getDefaultUserPropertiesReference().getHideInSettingsInQuietMode());
+ assertEquals(22,
+ aospType.getDefaultUserPropertiesReference().getShowInSharingSurfaces());
+ assertEquals(24,
+ aospType.getDefaultUserPropertiesReference().getShowInQuietMode());
assertEquals(450, aospType.getDefaultUserPropertiesReference()
.getInheritDevicePolicy());
assertFalse(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 2b6d8ed..c7d80ed 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -343,6 +343,8 @@
assertThat(mainUserId).isEqualTo(parentProfileInfo.id);
removeUser(userInfo.id);
assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
+ assertThat(mUserManager.getProfileLabel()).isEqualTo(
+ Resources.getSystem().getString(userTypeDetails.getLabel(0)));
}
@MediumTest
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
index a8e3c7e..8464969 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -53,10 +53,10 @@
import androidx.test.runner.AndroidJUnit4;
import androidx.test.uiautomator.UiDevice;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import org.junit.After;
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index b2843d8..1bfd43f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -37,10 +37,10 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.servicestests.R;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.PackageManagerException;
import com.android.server.pm.parsing.TestPackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import org.junit.Assert;
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
index ad9f920..0f87202 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -29,10 +29,10 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.server.pm.parsing.pkg.PackageImpl;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.parsing.ParsingPackage;
import dalvik.system.DelegateLastClassLoader;
import dalvik.system.DexClassLoader;
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index 6ae2658..cd29c80 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -24,11 +24,13 @@
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
+import android.app.usage.Flags;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.res.Configuration;
+import android.os.PersistableBundle;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.AtomicFile;
import android.util.LongSparseArray;
@@ -183,6 +185,17 @@
case Event.LOCUS_ID_SET:
event.mLocusId = "locus" + (i % 7); //"random" locus
break;
+ case Event.USER_INTERACTION:
+ if (Flags.userInteractionTypeApi()) {
+ // "random" user interaction extras.
+ PersistableBundle extras = new PersistableBundle();
+ extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY,
+ "fake.namespace.category" + (i % 13));
+ extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION,
+ "fakeaction" + (i % 13));
+ event.mExtras = extras;
+ }
+ break;
}
mIntervalStats.addEvent(event);
@@ -295,6 +308,18 @@
assertEquals(e1.mLocusIdToken, e2.mLocusIdToken,
"Usage event " + debugId);
break;
+ case Event.USER_INTERACTION:
+ if (Flags.userInteractionTypeApi()) {
+ PersistableBundle extras1 = e1.getExtras();
+ PersistableBundle extras2 = e2.getExtras();
+ assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY),
+ extras2.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY),
+ "Usage event " + debugId);
+ assertEquals(extras1.getString(UsageStatsManager.EXTRA_EVENT_ACTION),
+ extras2.getString(UsageStatsManager.EXTRA_EVENT_ACTION),
+ "Usage event " + debugId);
+ }
+ break;
}
// fallthrough
case 4: // test fields added in version 4
diff --git a/services/tests/servicestests/src/com/android/server/utils/UserSettingDeviceConfigMediatorTest.java b/services/tests/servicestests/src/com/android/server/utils/UserSettingDeviceConfigMediatorTest.java
new file mode 100644
index 0000000..377e4c3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/utils/UserSettingDeviceConfigMediatorTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import android.provider.DeviceConfig;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for {@link UserSettingDeviceConfigMediator}
+ */
+@RunWith(AndroidJUnit4.class)
+public class UserSettingDeviceConfigMediatorTest {
+ @Test
+ public void testDeviceConfigOnly() {
+ UserSettingDeviceConfigMediator mediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
+
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder("test")
+ .setInt("int", 1)
+ .setFloat("float", .5f)
+ .setBoolean("boolean", true)
+ .setLong("long", 123456789)
+ .setString("string", "abc123")
+ .build();
+
+ mediator.setDeviceConfigProperties(properties);
+
+ assertEquals(1, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("invalidKey", 123));
+ assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+ assertEquals(true, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("invalidKey", true));
+ assertEquals(123456789, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+ assertEquals("abc123", mediator.getString("string", "xyz987"));
+ assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+
+ // Clear the properties
+ mediator.setDeviceConfigProperties(null);
+
+ assertEquals(123, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("invalidKey", 123));
+ assertEquals(.8f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+ assertEquals(false, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("invalidKey", true));
+ assertEquals(987654321, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+ assertEquals("xyz987", mediator.getString("string", "xyz987"));
+ assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+ }
+
+ @Test
+ public void testSettingsOnly() {
+ UserSettingDeviceConfigMediator mediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
+
+ String settings = "int=1,float=.5f,boolean=true,long=123456789,string=abc123";
+
+ mediator.setSettingsString(settings);
+
+ assertEquals(1, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("invalidKey", 123));
+ assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+ assertEquals(true, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("invalidKey", true));
+ assertEquals(123456789, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+ assertEquals("abc123", mediator.getString("string", "xyz987"));
+ assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+
+ // Clear the settings
+ mediator.setSettingsString(null);
+
+ assertEquals(123, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("invalidKey", 123));
+ assertEquals(.8f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("invalidKey", .8f), 0.001);
+ assertEquals(false, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("invalidKey", true));
+ assertEquals(987654321, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getInt("invalidKey", 987654321));
+ assertEquals("xyz987", mediator.getString("string", "xyz987"));
+ assertEquals("xyz987", mediator.getString("invalidKey", "xyz987"));
+ }
+
+ @Test
+ public void testSettingsOverridesAll() {
+ UserSettingDeviceConfigMediator mediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesAllMediator(',');
+
+ String settings = "int=1,float=.5f,boolean=true,long=123456789,string=abc123,"
+ + "intOnlyInSettings=9,floatOnlyInSettings=.25f,booleanOnlyInSettings=true,"
+ + "longOnlyInSettings=53771465,stringOnlyInSettings=settingsString";
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder("test")
+ .setInt("int", 10)
+ .setInt("intOnlyInDeviceConfig", 9001)
+ .setFloat("float", .7f)
+ .setFloat("floatOnlyInDeviceConfig", .9f)
+ .setBoolean("boolean", false)
+ .setBoolean("booleanOnlyInDeviceConfig", true)
+ .setLong("long", 60000001)
+ .setLong("longOnlyInDeviceConfig", 7357)
+ .setString("string", "xyz987")
+ .setString("stringOnlyInDeviceConfig", "deviceConfigString")
+ .build();
+
+ mediator.setSettingsString(settings);
+ mediator.setDeviceConfigProperties(properties);
+
+ // Since settings overrides all, anything in DeviceConfig should be ignored,
+ // even if settings doesn't have a value for it.
+ assertEquals(1, mediator.getInt("int", 123));
+ assertEquals(9, mediator.getInt("intOnlyInSettings", 123));
+ assertEquals(123, mediator.getInt("intOnlyInDeviceConfig", 123));
+ assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.25f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+ assertEquals(true, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("booleanOnlyInSettings", false));
+ assertEquals(false, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+ assertEquals(123456789, mediator.getLong("long", 987654321));
+ assertEquals(53771465, mediator.getLong("longOnlyInSettings", 987654321));
+ assertEquals(987654321, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+ assertEquals("abc123", mediator.getString("string", "default"));
+ assertEquals("settingsString", mediator.getString("stringOnlyInSettings", "default"));
+ assertEquals("default", mediator.getString("stringOnlyInDeviceConfig", "default"));
+
+ // Nothing in settings, do DeviceConfig can be used.
+ mediator.setSettingsString("");
+
+ assertEquals(10, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("intOnlyInSettings", 123));
+ assertEquals(9001, mediator.getInt("intOnlyInDeviceConfig", 123));
+ assertEquals(.7f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+ assertEquals(.9f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+ assertEquals(false, mediator.getBoolean("boolean", false));
+ assertEquals(false, mediator.getBoolean("booleanOnlyInSettings", false));
+ assertEquals(true, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+ assertEquals(60000001, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getLong("longOnlyInSettings", 987654321));
+ assertEquals(7357, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+ assertEquals("xyz987", mediator.getString("string", "default"));
+ assertEquals("default", mediator.getString("stringOnlyInSettings", "default"));
+ assertEquals("deviceConfigString",
+ mediator.getString("stringOnlyInDeviceConfig", "default"));
+
+ // Nothing in settings, do DeviceConfig can be used.
+ mediator.setSettingsString(null);
+
+ assertEquals(10, mediator.getInt("int", 123));
+ assertEquals(123, mediator.getInt("intOnlyInSettings", 123));
+ assertEquals(9001, mediator.getInt("intOnlyInDeviceConfig", 123));
+ assertEquals(.7f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.8f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+ assertEquals(.9f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+ assertEquals(false, mediator.getBoolean("boolean", false));
+ assertEquals(false, mediator.getBoolean("booleanOnlyInSettings", false));
+ assertEquals(true, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+ assertEquals(60000001, mediator.getLong("long", 987654321));
+ assertEquals(987654321, mediator.getLong("longOnlyInSettings", 987654321));
+ assertEquals(7357, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+ assertEquals("xyz987", mediator.getString("string", "default"));
+ assertEquals("default", mediator.getString("stringOnlyInSettings", "default"));
+ assertEquals("deviceConfigString",
+ mediator.getString("stringOnlyInDeviceConfig", "default"));
+ }
+
+ @Test
+ public void testSettingsOverridesIndividual() {
+ UserSettingDeviceConfigMediator mediator =
+ new UserSettingDeviceConfigMediator.SettingsOverridesIndividualMediator(',');
+
+ String settings = "int=1,float=.5f,boolean=true,long=123456789,string=abc123,"
+ + "intOnlyInSettings=9,floatOnlyInSettings=.25f,booleanOnlyInSettings=true,"
+ + "longOnlyInSettings=53771465,stringOnlyInSettings=settingsString";
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder("test")
+ .setInt("int", 10)
+ .setInt("intOnlyInDeviceConfig", 9001)
+ .setFloat("float", .7f)
+ .setFloat("floatOnlyInDeviceConfig", .9f)
+ .setBoolean("boolean", false)
+ .setBoolean("booleanOnlyInDeviceConfig", true)
+ .setLong("long", 60000001)
+ .setLong("longOnlyInDeviceConfig", 7357)
+ .setString("string", "xyz987")
+ .setString("stringOnlyInDeviceConfig", "deviceConfigString")
+ .build();
+
+ mediator.setSettingsString(settings);
+ mediator.setDeviceConfigProperties(properties);
+
+ // Since settings overrides individual, anything in DeviceConfig that doesn't exist in
+ // settings should be used.
+ assertEquals(1, mediator.getInt("int", 123));
+ assertEquals(9, mediator.getInt("intOnlyInSettings", 123));
+ assertEquals(9001, mediator.getInt("intOnlyInDeviceConfig", 123));
+ assertEquals(.5f, mediator.getFloat("float", .8f), 0.001);
+ assertEquals(.25f, mediator.getFloat("floatOnlyInSettings", .8f), 0.001);
+ assertEquals(.9f, mediator.getFloat("floatOnlyInDeviceConfig", .8f), 0.001);
+ assertEquals(true, mediator.getBoolean("boolean", false));
+ assertEquals(true, mediator.getBoolean("booleanOnlyInSettings", false));
+ assertEquals(true, mediator.getBoolean("booleanOnlyInDeviceConfig", false));
+ assertEquals(123456789, mediator.getLong("long", 987654321));
+ assertEquals(53771465, mediator.getLong("longOnlyInSettings", 987654321));
+ assertEquals(7357, mediator.getLong("longOnlyInDeviceConfig", 987654321));
+ assertEquals("abc123", mediator.getString("string", "default"));
+ assertEquals("settingsString", mediator.getString("stringOnlyInSettings", "default"));
+ assertEquals("deviceConfigString",
+ mediator.getString("stringOnlyInDeviceConfig", "default"));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index ebe45a6..32082e3 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.webkit;
+import static android.webkit.Flags.updateServiceV2;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -25,6 +27,10 @@
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;
import android.util.Base64;
import android.webkit.UserPackage;
@@ -35,6 +41,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
@@ -55,11 +62,14 @@
public class WebViewUpdateServiceTest {
private final static String TAG = WebViewUpdateServiceTest.class.getSimpleName();
- private WebViewUpdateServiceImpl mWebViewUpdateServiceImpl;
+ private WebViewUpdateServiceInterface mWebViewUpdateServiceImpl;
private TestSystemImpl mTestSystemImpl;
private static final String WEBVIEW_LIBRARY_FLAG = "com.android.webview.WebViewLibrary";
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
/**
* Creates a new instance.
*/
@@ -92,8 +102,13 @@
TestSystemImpl testing = new TestSystemImpl(packages, numRelros, isDebuggable,
multiProcessDefault);
mTestSystemImpl = Mockito.spy(testing);
- mWebViewUpdateServiceImpl =
- new WebViewUpdateServiceImpl(null /*Context*/, mTestSystemImpl);
+ if (updateServiceV2()) {
+ mWebViewUpdateServiceImpl =
+ new WebViewUpdateServiceImpl2(null /*Context*/, mTestSystemImpl);
+ } else {
+ mWebViewUpdateServiceImpl =
+ new WebViewUpdateServiceImpl(null /*Context*/, mTestSystemImpl);
+ }
}
private void setEnabledAndValidPackageInfos(WebViewProviderInfo[] providers) {
@@ -1310,11 +1325,13 @@
}
@Test
+ @RequiresFlagsDisabled("android.webkit.update_service_v2")
public void testMultiProcessEnabledByDefault() {
testMultiProcessByDefault(true /* enabledByDefault */);
}
@Test
+ @RequiresFlagsDisabled("android.webkit.update_service_v2")
public void testMultiProcessDisabledByDefault() {
testMultiProcessByDefault(false /* enabledByDefault */);
}
@@ -1344,6 +1361,7 @@
}
@Test
+ @RequiresFlagsDisabled("android.webkit.update_service_v2")
public void testMultiProcessEnabledByDefaultWithSettingsValue() {
testMultiProcessByDefaultWithSettingsValue(
true /* enabledByDefault */, Integer.MIN_VALUE, false /* expectEnabled */);
@@ -1356,6 +1374,7 @@
}
@Test
+ @RequiresFlagsDisabled("android.webkit.update_service_v2")
public void testMultiProcessDisabledByDefaultWithSettingsValue() {
testMultiProcessByDefaultWithSettingsValue(
false /* enabledByDefault */, Integer.MIN_VALUE, false /* expectEnabled */);
@@ -1431,4 +1450,21 @@
checkPreparationPhasesForPackage(currentSdkPackage.packageName,
1 /* first preparation phase */);
}
+
+ @Test
+ @RequiresFlagsEnabled("android.webkit.update_service_v2")
+ public void testDefaultWebViewPackageIsTheFirstAvailableByDefault() {
+ String nonDefaultPackage = "nonDefaultPackage";
+ String defaultPackage1 = "defaultPackage1";
+ String defaultPackage2 = "defaultPackage2";
+ WebViewProviderInfo[] packages =
+ new WebViewProviderInfo[] {
+ new WebViewProviderInfo(nonDefaultPackage, "", false, false, null),
+ new WebViewProviderInfo(defaultPackage1, "", true, false, null),
+ new WebViewProviderInfo(defaultPackage2, "", true, false, null)
+ };
+ setupWithPackages(packages);
+ assertEquals(
+ defaultPackage1, mWebViewUpdateServiceImpl.getDefaultWebViewPackage().packageName);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index b45dcd4..09ffe71 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -241,7 +241,6 @@
import com.android.internal.R;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.Flag;
import com.android.internal.config.sysui.TestableFlagResolver;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.InstanceIdSequenceFake;
@@ -5296,7 +5295,7 @@
new Intent(Intent.ACTION_LOCALE_CHANGED));
verify(mZenModeHelper, times(1)).updateDefaultZenRules(
- anyInt(), anyBoolean());
+ anyInt());
}
@Test
@@ -8752,7 +8751,7 @@
// verify that zen mode helper gets passed in a package name of "android"
verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString(),
- anyInt(), eq(true)); // system call counts as "is system or system ui"
+ anyInt(), eq(ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI)); // system call
}
@Test
@@ -8774,7 +8773,7 @@
// verify that zen mode helper gets passed in a package name of "android"
verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString(),
- anyInt(), eq(true)); // system call counts as "system or system ui"
+ anyInt(), eq(ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI)); // system call
}
@Test
@@ -8795,7 +8794,7 @@
// verify that zen mode helper gets passed in the package name from the arg, not the owner
verify(mockZenModeHelper).addAutomaticZenRule(
eq("another.package"), eq(rule), anyString(), anyInt(),
- eq(false)); // doesn't count as a system/systemui call
+ eq(ZenModeHelper.FROM_APP)); // doesn't count as a system/systemui call
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index 8f30f41..6976ec3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -82,7 +82,7 @@
}
@Override
- protected boolean isCallerIsSystemOrSystemUi() {
+ protected boolean isCallerSystemOrSystemUi() {
countSystemChecks++;
return isSystemUid || isSystemAppId;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 0313aaa..97b6b98 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -53,6 +53,9 @@
import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG;
import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW;
import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW;
+import static com.android.server.notification.ZenModeHelper.FROM_APP;
+import static com.android.server.notification.ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI;
+import static com.android.server.notification.ZenModeHelper.FROM_USER;
import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
import static com.google.common.truth.Truth.assertThat;
@@ -108,6 +111,7 @@
import android.provider.Settings;
import android.provider.Settings.Global;
import android.service.notification.Condition;
+import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.service.notification.ZenModeConfig.ZenRule;
@@ -1645,8 +1649,7 @@
ZenModeConfig config = new ZenModeConfig();
config.automaticRules = new ArrayMap<>();
mZenModeHelper.mConfig = config;
- mZenModeHelper.updateDefaultZenRules(
- Process.SYSTEM_UID, true); // shouldn't throw null pointer
+ mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID); // shouldn't throw null pointer
mZenModeHelper.pullRules(events); // shouldn't throw null pointer
}
@@ -1671,7 +1674,7 @@
autoRules.put(SCHEDULE_DEFAULT_RULE_ID, updatedDefaultRule);
mZenModeHelper.mConfig.automaticRules = autoRules;
- mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID, true);
+ mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID);
assertEquals(updatedDefaultRule,
mZenModeHelper.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID));
}
@@ -1697,7 +1700,7 @@
autoRules.put(SCHEDULE_DEFAULT_RULE_ID, updatedDefaultRule);
mZenModeHelper.mConfig.automaticRules = autoRules;
- mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID, true);
+ mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID);
assertEquals(updatedDefaultRule,
mZenModeHelper.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID));
}
@@ -1724,7 +1727,7 @@
autoRules.put(SCHEDULE_DEFAULT_RULE_ID, customDefaultRule);
mZenModeHelper.mConfig.automaticRules = autoRules;
- mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID, true);
+ mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID);
ZenModeConfig.ZenRule ruleAfterUpdating =
mZenModeHelper.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID);
assertEquals(customDefaultRule.enabled, ruleAfterUpdating.enabled);
@@ -1748,7 +1751,7 @@
// We need the package name to be something that's not "android" so there aren't any
// existing rules under that package.
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
- CUSTOM_PKG_UID, false);
+ CUSTOM_PKG_UID, FROM_APP);
assertNotNull(id);
}
try {
@@ -1759,7 +1762,7 @@
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
- CUSTOM_PKG_UID, false);
+ CUSTOM_PKG_UID, FROM_APP);
fail("allowed too many rules to be created");
} catch (IllegalArgumentException e) {
// yay
@@ -1780,7 +1783,7 @@
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
- CUSTOM_PKG_UID, false);
+ CUSTOM_PKG_UID, FROM_APP);
assertNotNull(id);
}
try {
@@ -1791,7 +1794,7 @@
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
- CUSTOM_PKG_UID, false);
+ CUSTOM_PKG_UID, FROM_APP);
fail("allowed too many rules to be created");
} catch (IllegalArgumentException e) {
// yay
@@ -1812,7 +1815,7 @@
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
- CUSTOM_PKG_UID, false);
+ CUSTOM_PKG_UID, FROM_APP);
assertNotNull(id);
}
try {
@@ -1823,7 +1826,7 @@
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
- CUSTOM_PKG_UID, false);
+ CUSTOM_PKG_UID, FROM_APP);
fail("allowed too many rules to be created");
} catch (IllegalArgumentException e) {
// yay
@@ -1839,7 +1842,7 @@
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test",
- Process.SYSTEM_UID, true);
+ Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
assertTrue(id != null);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
@@ -1860,7 +1863,7 @@
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test",
- Process.SYSTEM_UID, true);
+ Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
assertTrue(id != null);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
@@ -1884,7 +1887,7 @@
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
- CUSTOM_PKG_UID, false);
+ CUSTOM_PKG_UID, FROM_APP);
mZenModeHelper.setAutomaticZenRuleState(zenRule.getConditionId(),
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
CUSTOM_PKG_UID, false);
@@ -1903,7 +1906,7 @@
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
- CUSTOM_PKG_UID, false);
+ CUSTOM_PKG_UID, FROM_APP);
AutomaticZenRule zenRule2 = new AutomaticZenRule("NEW",
null,
@@ -1912,7 +1915,7 @@
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- mZenModeHelper.updateAutomaticZenRule(id, zenRule2, "", CUSTOM_PKG_UID, false);
+ mZenModeHelper.updateAutomaticZenRule(id, zenRule2, "", CUSTOM_PKG_UID, FROM_APP);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
assertEquals("NEW", ruleInConfig.name);
@@ -1928,7 +1931,7 @@
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
- CUSTOM_PKG_UID, false);
+ CUSTOM_PKG_UID, FROM_APP);
assertTrue(id != null);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
@@ -1948,7 +1951,7 @@
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
- CUSTOM_PKG_UID, false);
+ CUSTOM_PKG_UID, FROM_APP);
assertTrue(id != null);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
@@ -1972,13 +1975,13 @@
sharedUri,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test",
- Process.SYSTEM_UID, true);
+ Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
new ComponentName("android", "ScheduleConditionProvider"),
sharedUri,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id2 = mZenModeHelper.addAutomaticZenRule("android", zenRule2, "test",
- Process.SYSTEM_UID, true);
+ Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
Condition condition = new Condition(sharedUri, "", STATE_TRUE);
mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition, Process.SYSTEM_UID, true);
@@ -2010,6 +2013,182 @@
}
@Test
+ public void addAutomaticZenRule_fromApp_ignoresHiddenEffects() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+
+ ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
+ .setShouldDisplayGrayscale(true)
+ .setShouldSuppressAmbientDisplay(true)
+ .setShouldDimWallpaper(true)
+ .setShouldUseNightMode(true)
+ .setShouldDisableAutoBrightness(true)
+ .setShouldDisableTapToWake(true)
+ .setShouldDisableTiltToWake(true)
+ .setShouldDisableTouch(true)
+ .setShouldMinimizeRadioUsage(true)
+ .setShouldMaximizeDoze(true)
+ .build();
+
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+ .setOwner(OWNER)
+ .setDeviceEffects(zde)
+ .build(),
+ "reasons", 0, FROM_APP);
+
+ AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+ assertThat(savedRule.getDeviceEffects()).isEqualTo(
+ new ZenDeviceEffects.Builder()
+ .setShouldDisplayGrayscale(true)
+ .setShouldSuppressAmbientDisplay(true)
+ .setShouldDimWallpaper(true)
+ .setShouldUseNightMode(true)
+ .build());
+ }
+
+ @Test
+ public void addAutomaticZenRule_fromSystem_respectsHiddenEffects() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+
+ ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
+ .setShouldDisplayGrayscale(true)
+ .setShouldSuppressAmbientDisplay(true)
+ .setShouldDimWallpaper(true)
+ .setShouldUseNightMode(true)
+ .setShouldDisableAutoBrightness(true)
+ .setShouldDisableTapToWake(true)
+ .setShouldDisableTiltToWake(true)
+ .setShouldDisableTouch(true)
+ .setShouldMinimizeRadioUsage(true)
+ .setShouldMaximizeDoze(true)
+ .build();
+
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+ .setOwner(OWNER)
+ .setDeviceEffects(zde)
+ .build(),
+ "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI);
+
+ AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+ assertThat(savedRule.getDeviceEffects()).isEqualTo(zde);
+ }
+
+ @Test
+ public void addAutomaticZenRule_fromUser_respectsHiddenEffects() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+
+ ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
+ .setShouldDisplayGrayscale(true)
+ .setShouldSuppressAmbientDisplay(true)
+ .setShouldDimWallpaper(true)
+ .setShouldUseNightMode(true)
+ .setShouldDisableAutoBrightness(true)
+ .setShouldDisableTapToWake(true)
+ .setShouldDisableTiltToWake(true)
+ .setShouldDisableTouch(true)
+ .setShouldMinimizeRadioUsage(true)
+ .setShouldMaximizeDoze(true)
+ .build();
+
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+ .setOwner(OWNER)
+ .setDeviceEffects(zde)
+ .build(),
+ "reasons", 0, FROM_USER);
+
+ AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+ assertThat(savedRule.getDeviceEffects()).isEqualTo(zde);
+ }
+
+ @Test
+ public void updateAutomaticZenRule_fromApp_preservesPreviousHiddenEffects() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ ZenDeviceEffects original = new ZenDeviceEffects.Builder()
+ .setShouldDisableTapToWake(true)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+ .setOwner(OWNER)
+ .setDeviceEffects(original)
+ .build(),
+ "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI);
+
+ ZenDeviceEffects updateFromApp = new ZenDeviceEffects.Builder()
+ .setShouldUseNightMode(true) // Good
+ .setShouldMaximizeDoze(true) // Bad
+ .build();
+ mZenModeHelper.updateAutomaticZenRule(ruleId,
+ new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+ .setOwner(OWNER)
+ .setDeviceEffects(updateFromApp)
+ .build(),
+ "reasons", 0, FROM_APP);
+
+ AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+ assertThat(savedRule.getDeviceEffects()).isEqualTo(
+ new ZenDeviceEffects.Builder()
+ .setShouldUseNightMode(true) // From update.
+ .setShouldDisableTapToWake(true) // From original.
+ .build());
+ }
+
+ @Test
+ public void updateAutomaticZenRule_fromSystem_updatesHiddenEffects() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ ZenDeviceEffects original = new ZenDeviceEffects.Builder()
+ .setShouldDisableTapToWake(true)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+ .setOwner(OWNER)
+ .setDeviceEffects(original)
+ .build(),
+ "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI);
+
+ ZenDeviceEffects updateFromSystem = new ZenDeviceEffects.Builder()
+ .setShouldUseNightMode(true) // Good
+ .setShouldMaximizeDoze(true) // Also good
+ .build();
+ mZenModeHelper.updateAutomaticZenRule(ruleId,
+ new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+ .setDeviceEffects(updateFromSystem)
+ .build(),
+ "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI);
+
+ AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+ assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromSystem);
+ }
+
+ @Test
+ public void updateAutomaticZenRule_fromUser_updatesHiddenEffects() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ ZenDeviceEffects original = new ZenDeviceEffects.Builder()
+ .setShouldDisableTapToWake(true)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+ .setOwner(OWNER)
+ .setDeviceEffects(original)
+ .build(),
+ "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI);
+
+ ZenDeviceEffects updateFromUser = new ZenDeviceEffects.Builder()
+ .setShouldUseNightMode(true) // Good
+ .setShouldMaximizeDoze(true) // Also good
+ .build();
+ mZenModeHelper.updateAutomaticZenRule(ruleId,
+ new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+ .setDeviceEffects(updateFromUser)
+ .build(),
+ "reasons", 0, FROM_USER);
+
+ AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+ assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromUser);
+ }
+
+ @Test
public void testSetManualZenMode() {
mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
setupZenConfig();
@@ -2119,7 +2298,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
- "test", Process.SYSTEM_UID, true);
+ "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
// Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE
mZenModeHelper.setAutomaticZenRuleState(id,
@@ -2128,7 +2307,8 @@
// Event 2: "User" turns off the automatic rule (sets it to not enabled)
zenRule.setEnabled(false);
- mZenModeHelper.updateAutomaticZenRule(id, zenRule, "", Process.SYSTEM_UID, true);
+ mZenModeHelper.updateAutomaticZenRule(id, zenRule, "", Process.SYSTEM_UID,
+ FROM_SYSTEM_OR_SYSTEMUI);
// Add a new system rule
AutomaticZenRule systemRule = new AutomaticZenRule("systemRule",
@@ -2138,7 +2318,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String systemId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), systemRule,
- "test", Process.SYSTEM_UID, true);
+ "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
// Event 3: turn on the system rule
mZenModeHelper.setAutomaticZenRuleState(systemId,
@@ -2270,7 +2450,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
- Process.SYSTEM_UID, true);
+ Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
// Rule 2, same as rule 1
AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
@@ -2280,7 +2460,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, "test",
- Process.SYSTEM_UID, true);
+ Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
// Rule 3, has stricter settings than the default settings
ZenModeConfig ruleConfig = mZenModeHelper.mConfig.copy();
@@ -2294,7 +2474,7 @@
ruleConfig.toZenPolicy(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id3 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule3, "test",
- Process.SYSTEM_UID, true);
+ Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
// First: turn on rule 1
mZenModeHelper.setAutomaticZenRuleState(id,
@@ -2394,7 +2574,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
- Process.SYSTEM_UID, true);
+ Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
// Rule 2, same as rule 1 but owned by the system
AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
@@ -2404,7 +2584,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, "test",
- Process.SYSTEM_UID, true);
+ Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
// Turn on rule 1; call looks like it's from the system. Because setting a condition is
// typically an automatic (non-user-initiated) action, expect the calling UID to be
@@ -2422,7 +2602,8 @@
// Disable rule 1. Because this looks like a user action, the UID should not be modified
// from the system-provided one.
zenRule.setEnabled(false);
- mZenModeHelper.updateAutomaticZenRule(id, zenRule, "", Process.SYSTEM_UID, true);
+ mZenModeHelper.updateAutomaticZenRule(id, zenRule, "", Process.SYSTEM_UID,
+ FROM_SYSTEM_OR_SYSTEMUI);
// Add a manual rule. Any manual rule changes should not get calling uids reassigned.
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "",
@@ -2553,7 +2734,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
- Process.SYSTEM_UID, true);
+ Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
// enable the rule
mZenModeHelper.setAutomaticZenRuleState(id,
@@ -2596,7 +2777,7 @@
customPolicy,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
- Process.SYSTEM_UID, true);
+ Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
// enable the rule; this will update the consolidated policy
mZenModeHelper.setAutomaticZenRuleState(id,
@@ -2632,7 +2813,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
- Process.SYSTEM_UID, true);
+ Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
// enable rule 1
mZenModeHelper.setAutomaticZenRuleState(id,
@@ -2656,7 +2837,7 @@
customPolicy,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
- "test", Process.SYSTEM_UID, true);
+ "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
// enable rule 2; this will update the consolidated policy
mZenModeHelper.setAutomaticZenRuleState(id2,
@@ -2694,7 +2875,7 @@
customPolicy,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
- Process.SYSTEM_UID, true);
+ Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
// enable the rule; this will update the consolidated policy
mZenModeHelper.setAutomaticZenRuleState(id,
@@ -2716,7 +2897,7 @@
strictPolicy,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
- "test", Process.SYSTEM_UID, true);
+ "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
// enable rule 2; this will update the consolidated policy
mZenModeHelper.setAutomaticZenRuleState(id2,
@@ -2784,7 +2965,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- zenRule, "test", Process.SYSTEM_UID, true);
+ zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
CountDownLatch latch = new CountDownLatch(1);
final int[] actualStatus = new int[1];
@@ -2800,7 +2981,8 @@
mZenModeHelper.addCallback(callback);
zenRule.setEnabled(false);
- mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID, true);
+ mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID,
+ FROM_SYSTEM_OR_SYSTEMUI);
assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
assertEquals(AUTOMATIC_RULE_STATUS_DISABLED, actualStatus[0]);
@@ -2818,7 +3000,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, false);
final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- zenRule, "test", Process.SYSTEM_UID, true);
+ zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
CountDownLatch latch = new CountDownLatch(1);
final int[] actualStatus = new int[1];
@@ -2834,7 +3016,8 @@
mZenModeHelper.addCallback(callback);
zenRule.setEnabled(true);
- mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID, true);
+ mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID,
+ FROM_SYSTEM_OR_SYSTEMUI);
assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
assertEquals(AUTOMATIC_RULE_STATUS_ENABLED, actualStatus[0]);
@@ -2853,7 +3036,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- zenRule, "test", Process.SYSTEM_UID, true);
+ zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
CountDownLatch latch = new CountDownLatch(1);
final int[] actualStatus = new int[1];
@@ -2889,7 +3072,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- zenRule, "test", Process.SYSTEM_UID, true);
+ zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
CountDownLatch latch = new CountDownLatch(1);
final int[] actualStatus = new int[2];
@@ -2929,7 +3112,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- zenRule, "test", Process.SYSTEM_UID, true);
+ zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
CountDownLatch latch = new CountDownLatch(1);
final int[] actualStatus = new int[2];
@@ -2969,7 +3152,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- zenRule, "test", Process.SYSTEM_UID, true);
+ zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
// Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE
mZenModeHelper.setAutomaticZenRuleState(createdId,
@@ -2982,7 +3165,8 @@
// Event 3: "User" turns off the automatic rule (sets it to not enabled)
zenRule.setEnabled(false);
- mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID, true);
+ mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID,
+ FROM_SYSTEM_OR_SYSTEMUI);
assertEquals(false, mZenModeHelper.mConfig.automaticRules.get(createdId).snoozing);
}
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index f2a1fe8..c3074bb 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -93,8 +93,6 @@
android:showWhenLocked="true"/>
<activity android:name="android.view.cts.surfacevalidator.CapturedActivity"/>
- <activity android:name="com.android.server.wm.SurfaceControlViewHostTests$TestActivity" />
-
<activity android:name="com.android.server.wm.SurfaceSyncGroupTests$TestActivity"
android:screenOrientation="locked"
android:turnScreenOn="true"
@@ -122,6 +120,13 @@
<activity android:name="com.android.server.wm.ActivityRecordInputSinkTests$TestActivity"
android:exported="true">
</activity>
+
+ <activity android:name="com.android.server.wm.utils.TestActivity"
+ android:screenOrientation="locked"
+ android:turnScreenOn="true"
+ android:showWhenLocked="true"
+ android:theme="@style/WhiteBackgroundTheme"
+ android:exported="true" />
</application>
<instrumentation
diff --git a/services/tests/wmtests/AndroidTest.xml b/services/tests/wmtests/AndroidTest.xml
index f8ebead..46e87dc 100644
--- a/services/tests/wmtests/AndroidTest.xml
+++ b/services/tests/wmtests/AndroidTest.xml
@@ -30,4 +30,8 @@
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false" />
</test>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="settings put secure immersive_mode_confirmations confirmed" />
+ </target_preparer>
</configuration>
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 9f58491..9efbe35 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -25,8 +25,6 @@
import static com.android.server.wm.utils.LastCallVerifier.lastCall;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -36,11 +34,14 @@
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import com.android.server.testutils.StubTransaction;
import com.android.server.wm.utils.MockAnimationAdapter;
+import com.android.window.flags.Flags;
import org.junit.Before;
import org.junit.Test;
@@ -122,7 +123,8 @@
}
}
- static class MockAnimationAdapterFactory extends SmoothDimmer.AnimationAdapterFactory {
+ static class MockAnimationAdapterFactory extends DimmerAnimationHelper.AnimationAdapterFactory {
+ @Override
public AnimationAdapter get(LocalAnimationAdapter.AnimationSpec alphaAnimationSpec,
SurfaceAnimationRunner runner) {
return sTestAnimation;
@@ -175,8 +177,8 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Smooth() {
- assumeTrue(Dimmer.DIMMER_REFACTOR);
final float alpha = 0.7f;
final int blur = 50;
mHost.addChild(mChild, 0);
@@ -195,8 +197,8 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Legacy() {
- assumeFalse(Dimmer.DIMMER_REFACTOR);
final float alpha = 0.7f;
mHost.addChild(mChild, 0);
mDimmer.adjustAppearance(mChild, alpha, 20);
@@ -210,8 +212,8 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testDimBelowWithChildSurfaceDestroyedWhenReset_Smooth() {
- assumeTrue(Dimmer.DIMMER_REFACTOR);
mHost.addChild(mChild, 0);
final float alpha = 0.8f;
@@ -230,8 +232,8 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testDimBelowWithChildSurfaceDestroyedWhenReset_Legacy() {
- assumeFalse(Dimmer.DIMMER_REFACTOR);
mHost.addChild(mChild, 0);
final float alpha = 0.8f;
@@ -290,8 +292,8 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testRemoveDimImmediately_Smooth() {
- assumeTrue(Dimmer.DIMMER_REFACTOR);
mHost.addChild(mChild, 0);
mDimmer.adjustAppearance(mChild, 1, 2);
mDimmer.adjustRelativeLayer(mChild, -1);
@@ -310,8 +312,8 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testRemoveDimImmediately_Legacy() {
- assumeFalse(Dimmer.DIMMER_REFACTOR);
mHost.addChild(mChild, 0);
mDimmer.adjustAppearance(mChild, 1, 0);
mDimmer.adjustRelativeLayer(mChild, -1);
@@ -330,8 +332,8 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
public void testDimmerWithBlurUpdatesTransaction_Legacy() {
- assumeFalse(Dimmer.DIMMER_REFACTOR);
mHost.addChild(mChild, 0);
final int blurRadius = 50;
@@ -344,4 +346,120 @@
verify(mHost.getPendingTransaction()).setBackgroundBlurRadius(dimLayer, blurRadius);
verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, mChild.mControl, -1);
}
+
+ /**
+ * mChild is requesting the dim values to be set directly. In this case, dim won't play the
+ * standard animation, but directly apply mChild's requests to the dim surface
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+ public void testContainerDimsOpeningAnimationByItself() {
+ mHost.addChild(mChild, 0);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0.1f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0.2f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0.3f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ mDimmer.updateDims(mTransaction);
+
+ verify(mTransaction).setAlpha(dimLayer, 0.2f);
+ verify(mTransaction).setAlpha(dimLayer, 0.3f);
+ verify(sTestAnimation, times(1)).startAnimation(
+ any(SurfaceControl.class), any(SurfaceControl.Transaction.class),
+ anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
+ }
+
+ /**
+ * Same as testContainerDimsOpeningAnimationByItself, but this is a more specific case in which
+ * alpha is animated to 0. This corner case is needed to verify that the layer is removed anyway
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+ public void testContainerDimsClosingAnimationByItself() {
+ mHost.addChild(mChild, 0);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0.2f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0.1f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(mChild, 0f, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.updateDims(mTransaction);
+ verify(mTransaction).remove(dimLayer);
+ }
+
+ /**
+ * Check the handover of the dim between two windows and the consequent dim animation in between
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+ public void testMultipleContainersDimmingConsecutively() {
+ TestWindowContainer first = mChild;
+ TestWindowContainer second = new TestWindowContainer(mWm);
+ mHost.addChild(first, 0);
+ mHost.addChild(second, 1);
+
+ mDimmer.adjustAppearance(first, 0.5f, 0);
+ mDimmer.adjustRelativeLayer(first, -1);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+ mDimmer.updateDims(mTransaction);
+
+ mDimmer.resetDimStates();
+ mDimmer.adjustAppearance(second, 0.9f, 0);
+ mDimmer.adjustRelativeLayer(second, -1);
+ mDimmer.updateDims(mTransaction);
+
+ verify(sTestAnimation, times(2)).startAnimation(
+ any(SurfaceControl.class), any(SurfaceControl.Transaction.class),
+ anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
+ verify(mTransaction).setAlpha(dimLayer, 0.5f);
+ verify(mTransaction).setAlpha(dimLayer, 0.9f);
+ }
+
+ /**
+ * Two windows are trying to modify the dim at the same time, but only the last request before
+ * updateDims will be satisfied
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_INTRODUCE_SMOOTHER_DIMMER)
+ public void testMultipleContainersDimmingAtTheSameTime() {
+ TestWindowContainer first = mChild;
+ TestWindowContainer second = new TestWindowContainer(mWm);
+ mHost.addChild(first, 0);
+ mHost.addChild(second, 1);
+
+ mDimmer.adjustAppearance(first, 0.5f, 0);
+ mDimmer.adjustRelativeLayer(first, -1);
+ SurfaceControl dimLayer = mDimmer.getDimLayer();
+ mDimmer.adjustAppearance(second, 0.9f, 0);
+ mDimmer.adjustRelativeLayer(second, -1);
+ mDimmer.updateDims(mTransaction);
+
+ verify(sTestAnimation, times(1)).startAnimation(
+ any(SurfaceControl.class), any(SurfaceControl.Transaction.class),
+ anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
+ verify(mTransaction, never()).setAlpha(dimLayer, 0.5f);
+ verify(mTransaction).setAlpha(dimLayer, 0.9f);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 3ede135..acce2e2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -157,6 +157,10 @@
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* Tests for the {@link DisplayContent} class.
@@ -2285,7 +2289,7 @@
0 /* userId */);
dc.mCurrentUniqueDisplayId = mDisplayInfo.uniqueId + "-test";
// Trigger display changed.
- dc.onDisplayChanged();
+ updateDisplay(dc);
// Ensure overridden size and denisty match the most up-to-date values in settings for the
// display.
verifySizes(dc, forcedWidth, forcedHeight, forcedDensity);
@@ -2830,7 +2834,7 @@
*/
private DisplayContent createDisplayNoUpdateDisplayInfo() {
final DisplayContent displayContent = createNewDisplay();
- doNothing().when(displayContent).updateDisplayInfo();
+ doNothing().when(displayContent).updateDisplayInfo(any());
return displayContent;
}
@@ -2860,6 +2864,16 @@
return result;
}
+ private void updateDisplay(DisplayContent displayContent) {
+ CompletableFuture<Object> future = new CompletableFuture<>();
+ displayContent.requestDisplayUpdate(() -> future.complete(new Object()));
+ try {
+ future.get(15, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private void tapOnDisplay(final DisplayContent dc) {
final DisplayMetrics dm = dc.getDisplayMetrics();
final float x = dm.widthPixels / 2;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index e7ac33f..7601868 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -30,6 +30,7 @@
import static android.view.Surface.ROTATION_90;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -130,13 +131,17 @@
});
mDisplayRotationCompatPolicy = new DisplayRotationCompatPolicy(
mDisplayContent, mMockHandler);
+
+ // Do not show the real toast.
+ spyOn(mDisplayRotationCompatPolicy);
+ doNothing().when(mDisplayRotationCompatPolicy).showToast(anyInt());
+ doNothing().when(mDisplayRotationCompatPolicy).showToast(anyInt(), anyString());
}
@Test
public void testOpenedCameraInSplitScreen_showToast() throws Exception {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
spyOn(mTask);
- spyOn(mDisplayRotationCompatPolicy);
doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode();
doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mTask).getWindowingMode();
@@ -160,7 +165,6 @@
public void testOpenedCameraInSplitScreen_orientationNotFixed_doNotShowToast() {
configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
spyOn(mTask);
- spyOn(mDisplayRotationCompatPolicy);
doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode();
doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mTask).getWindowingMode();
@@ -175,7 +179,6 @@
public void testOnScreenRotationAnimationFinished_treatmentNotEnabled_doNotShowToast() {
when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled())
.thenReturn(false);
- spyOn(mDisplayRotationCompatPolicy);
mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
@@ -185,8 +188,6 @@
@Test
public void testOnScreenRotationAnimationFinished_noOpenCamera_doNotShowToast() {
- spyOn(mDisplayRotationCompatPolicy);
-
mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
verify(mDisplayRotationCompatPolicy, never()).showToast(
@@ -197,7 +198,6 @@
public void testOnScreenRotationAnimationFinished_notFullscreen_doNotShowToast() {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
doReturn(true).when(mActivity).inMultiWindowMode();
- spyOn(mDisplayRotationCompatPolicy);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
@@ -211,7 +211,6 @@
public void testOnScreenRotationAnimationFinished_orientationNotFixed_doNotShowToast() {
configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
- spyOn(mDisplayRotationCompatPolicy);
mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
@@ -223,7 +222,6 @@
public void testOnScreenRotationAnimationFinished_showToast() {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
- spyOn(mDisplayRotationCompatPolicy);
mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 2b19ad9..1be61c3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -114,8 +115,7 @@
mDisplayUniqueId = "test:" + sNextUniqueId++;
mTestDisplay = new TestDisplayContent.Builder(mAtm, 1000, 1500)
.setUniqueId(mDisplayUniqueId).build();
- when(mRootWindowContainer.getDisplayContent(eq(mDisplayUniqueId)))
- .thenReturn(mTestDisplay);
+ doReturn(mTestDisplay).when(mRootWindowContainer).getDisplayContent(mDisplayUniqueId);
Task rootTask = mTestDisplay.getDefaultTaskDisplayArea()
.createRootTask(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true);
@@ -200,7 +200,7 @@
public void testReturnsEmptyDisplayIfDisplayIsNotFound() {
mTarget.saveTask(mTestTask);
- when(mRootWindowContainer.getDisplayContent(eq(mDisplayUniqueId))).thenReturn(null);
+ doReturn(null).when(mRootWindowContainer).getDisplayContent(eq(mDisplayUniqueId));
mTarget.getLaunchParams(mTestTask, null, mResult);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index eb78906..5a43498 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -317,6 +317,31 @@
assertTrue(firstActivity.mRequestForceTransition);
}
+ @Test
+ public void testMultipleActivitiesTaskEnterPip() {
+ // Enable shell transition because the order of setting windowing mode is different.
+ registerTestTransitionPlayer();
+ final ActivityRecord transientActivity = new ActivityBuilder(mAtm)
+ .setCreateTask(true).build();
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm)
+ .setTask(activity1.getTask()).build();
+ activity2.setState(RESUMED, "test");
+
+ // Assume the top activity switches to a transient-launch, e.g. recents.
+ transientActivity.setState(RESUMED, "test");
+ transientActivity.getTask().moveToFront("test");
+
+ mRootWindowContainer.moveActivityToPinnedRootTask(activity2,
+ null /* launchIntoPipHostActivity */, "test");
+ assertEquals("Created PiP task must not change focus", transientActivity.getTask(),
+ mRootWindowContainer.getTopDisplayFocusedRootTask());
+ final Task newPipTask = activity2.getTask();
+ assertEquals(newPipTask, mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask());
+ assertNotEquals(newPipTask, activity1.getTask());
+ assertFalse("Created PiP task must not be in recents", newPipTask.inRecents);
+ }
+
/**
* When there is only one activity in the Task, and the activity is requesting to enter PIP, the
* whole Task should enter PIP.
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
index 8119fd4..3b9ed26 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
@@ -23,15 +23,14 @@
import static org.junit.Assert.assertTrue;
-import android.app.Activity;
import android.app.Instrumentation;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.PixelFormat;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.server.wm.BuildUtils;
import android.view.Gravity;
import android.view.IWindow;
import android.view.SurfaceControl;
@@ -46,12 +45,12 @@
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import com.android.server.wm.utils.CommonUtils;
+import com.android.server.wm.utils.TestActivity;
import org.junit.After;
import org.junit.Before;
@@ -59,11 +58,14 @@
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
@Presubmit
@SmallTest
@RunWith(WindowTestRunner.class)
public class SurfaceControlViewHostTests {
+ private static final long WAIT_TIME_S = 5L * BuildUtils.HW_TIMEOUT_MULTIPLIER;
+
private static final String TAG = "SurfaceControlViewHostTests";
private final ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
@@ -76,6 +78,8 @@
private SurfaceControlViewHost mScvh1;
private SurfaceControlViewHost mScvh2;
+ private SurfaceView mSurfaceView;
+
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -96,15 +100,17 @@
mView1 = new Button(mActivity);
mView2 = new Button(mActivity);
- mInstrumentation.runOnMainSync(() -> {
- try {
- mActivity.attachToSurfaceView(sc);
- } catch (InterruptedException e) {
- }
+ CountDownLatch svReadyLatch = new CountDownLatch(1);
+ mActivity.runOnUiThread(() -> addSurfaceView(svReadyLatch));
+ assertTrue("Failed to wait for SV to get created",
+ svReadyLatch.await(WAIT_TIME_S, TimeUnit.SECONDS));
+ new SurfaceControl.Transaction().reparent(sc, mSurfaceView.getSurfaceControl())
+ .show(sc).apply();
+ mInstrumentation.runOnMainSync(() -> {
TestWindowlessWindowManager wwm = new TestWindowlessWindowManager(
mActivity.getResources().getConfiguration(), sc,
- mActivity.mSurfaceView.getHostToken());
+ mSurfaceView.getHostToken());
mScvh1 = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
wwm, "requestFocusWithMultipleWindows");
@@ -135,7 +141,7 @@
}
assertTrue("Failed to wait for view2", wasVisible);
- IWindow window = IWindow.Stub.asInterface(mActivity.mSurfaceView.getWindowToken());
+ IWindow window = IWindow.Stub.asInterface(mSurfaceView.getWindowToken());
WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window,
mScvh1.getInputTransferToken(), true);
@@ -162,43 +168,30 @@
}
}
- public static class TestActivity extends Activity implements SurfaceHolder.Callback {
- private SurfaceView mSurfaceView;
- private final CountDownLatch mSvReadyLatch = new CountDownLatch(1);
+ private void addSurfaceView(CountDownLatch svReadyLatch) {
+ final FrameLayout content = mActivity.getParentLayout();
+ mSurfaceView = new SurfaceView(mActivity);
+ mSurfaceView.setZOrderOnTop(true);
+ final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(500, 500,
+ Gravity.LEFT | Gravity.TOP);
+ mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(@NonNull SurfaceHolder holder) {
+ svReadyLatch.countDown();
+ }
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final FrameLayout content = new FrameLayout(this);
- mSurfaceView = new SurfaceView(this);
- mSurfaceView.setBackgroundColor(Color.BLACK);
- mSurfaceView.setZOrderOnTop(true);
- final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(500, 500,
- Gravity.LEFT | Gravity.TOP);
- content.addView(mSurfaceView, lp);
- setContentView(content);
- mSurfaceView.getHolder().addCallback(this);
- }
+ @Override
+ public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
+ int height) {
- @Override
- public void surfaceCreated(@NonNull SurfaceHolder holder) {
- mSvReadyLatch.countDown();
- }
+ }
- @Override
- public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
- int height) {
- }
+ @Override
+ public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
- @Override
- public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
- }
-
- public void attachToSurfaceView(SurfaceControl sc) throws InterruptedException {
- mSvReadyLatch.await();
- new SurfaceControl.Transaction().reparent(sc, mSurfaceView.getSurfaceControl())
- .show(sc).apply();
- }
+ }
+ });
+ content.addView(mSurfaceView, lp);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
index 3cb4a1d..e65a9fe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
@@ -19,6 +19,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import android.os.Handler;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.DexmakerShareClassLoaderRule;
@@ -39,6 +41,9 @@
public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule(
this::onBeforeSystemServicesCreated);
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@WindowTestRunner.MethodWrapperRule
public final WindowManagerGlobalLockRule mLockRule =
new WindowManagerGlobalLockRule(mSystemServicesTestRule);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 9af5ba5..51f0404 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -38,6 +38,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
@@ -57,7 +58,7 @@
import android.content.pm.PackageManagerInternal;
import android.database.ContentObserver;
import android.hardware.devicestate.DeviceStateManager;
-import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
import android.net.Uri;
import android.os.Handler;
@@ -69,6 +70,7 @@
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.Log;
+import android.view.DisplayInfo;
import android.view.InputChannel;
import android.view.SurfaceControl;
@@ -101,6 +103,7 @@
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -239,6 +242,12 @@
doNothing().when(contentResolver)
.registerContentObserver(any(Uri.class), anyBoolean(), any(ContentObserver.class),
anyInt());
+
+ // Unit test should not register listener to the real service.
+ final DisplayManagerGlobal dmg = DisplayManagerGlobal.getInstance();
+ spyOn(dmg);
+ doNothing().when(dmg).registerDisplayListener(
+ any(), any(Executor.class), anyLong(), anyString());
}
private void setUpLocalServices() {
@@ -267,6 +276,12 @@
// DisplayManagerInternal
final DisplayManagerInternal dmi = mock(DisplayManagerInternal.class);
doReturn(dmi).when(() -> LocalServices.getService(eq(DisplayManagerInternal.class)));
+ doAnswer(invocation -> {
+ int displayId = invocation.getArgument(0);
+ DisplayInfo displayInfo = invocation.getArgument(1);
+ mWmService.mRoot.getDisplayContent(displayId).getDisplay().getDisplayInfo(displayInfo);
+ return null;
+ }).when(dmi).getNonOverrideDisplayInfo(anyInt(), any());
// ColorDisplayServiceInternal
final ColorDisplayService.ColorDisplayServiceInternal cds =
@@ -376,9 +391,6 @@
mWmService.onInitReady();
mAtmService.setWindowManager(mWmService);
- // Avoid real display events interfering with the test. Also avoid leaking registration.
- mContext.getSystemService(DisplayManager.class)
- .unregisterDisplayListener(mAtmService.mRootWindowContainer);
mWmService.mDisplayEnabled = true;
mWmService.mDisplayReady = true;
mAtmService.getTransitionController().mIsWaitingForDisplayEnabled = false;
@@ -432,7 +444,9 @@
SurfaceAnimationThread.dispose();
AnimationThread.dispose();
UiThread.dispose();
- mInputChannel.dispose();
+ if (mInputChannel != null) {
+ mInputChannel.dispose();
+ }
tearDownLocalServices();
// Reset priority booster because animation thread has been changed.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index dade3b9..71447e7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -2015,6 +2015,9 @@
transition.collect(leafTaskA);
rootTaskA.moveToFront("test", leafTaskA);
+ // Test has order changes, a shallow check of order changes
+ assertTrue(transition.hasOrderChanges());
+
// All the tasks were already visible, so there shouldn't be any changes
ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
participants, changes);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
index 2d5b72b..d183cf7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
@@ -60,6 +60,11 @@
import org.mockito.Mock;
import org.mockito.Mockito;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
/**
* Build/Install/Run:
* atest WmTests:WindowContextListenerControllerTests
@@ -309,7 +314,7 @@
return null;
}).when(display).getDisplayInfo(any(DisplayInfo.class));
- mContainer.getDisplayContent().onDisplayChanged();
+ updateDisplay(mContainer.getDisplayContent());
assertThat(mockToken.mConfiguration).isEqualTo(config1);
assertThat(mockToken.mDisplayId).isEqualTo(mContainer.getDisplayContent().getDisplayId());
@@ -352,4 +357,14 @@
mRemoved = true;
}
}
+
+ private void updateDisplay(DisplayContent displayContent) {
+ CompletableFuture<Object> future = new CompletableFuture<>();
+ displayContent.requestDisplayUpdate(() -> future.complete(new Object()));
+ try {
+ future.get(15, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index d8a9a28..2007f68 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -757,7 +757,6 @@
startingApp.getWindowFrames().setInsetsChanged(true);
startingApp.updateResizingWindowIfNeeded();
assertTrue(startingApp.isDrawn());
- assertFalse(startingApp.getOrientationChanging());
}
@SetupWindows(addWindows = W_ABOVE_ACTIVITY)
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/TestActivity.java b/services/tests/wmtests/src/com/android/server/wm/utils/TestActivity.java
new file mode 100644
index 0000000..c12dcdd
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/TestActivity.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import static android.view.WindowInsets.Type.displayCutout;
+import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.os.Bundle;
+import android.view.WindowInsetsController;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import androidx.annotation.Nullable;
+
+/**
+ * TestActivity that will ensure it dismisses keyguard and shows as a fullscreen activity.
+ */
+public class TestActivity extends Activity {
+ private static final int sTypeMask = systemBars() | displayCutout();
+ private FrameLayout mParentLayout;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mParentLayout = new FrameLayout(this);
+ FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT);
+ setContentView(mParentLayout, layoutParams);
+
+ WindowInsetsController windowInsetsController = getWindow().getInsetsController();
+ windowInsetsController.hide(sTypeMask);
+ WindowManager.LayoutParams params = getWindow().getAttributes();
+ params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ getWindow().setAttributes(params);
+ getWindow().setDecorFitsSystemWindows(false);
+
+ final KeyguardManager keyguardManager = getInstrumentation().getContext().getSystemService(
+ KeyguardManager.class);
+ if (keyguardManager != null && keyguardManager.isKeyguardLocked()) {
+ keyguardManager.requestDismissKeyguard(this, null);
+ }
+ }
+
+ public FrameLayout getParentLayout() {
+ return mParentLayout;
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index bfb159f..dce4818 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -31,6 +31,7 @@
import static android.app.usage.UsageEvents.Event.LOCUS_ID_SET;
import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
import static android.app.usage.UsageEvents.Event.SCREEN_INTERACTIVE;
import static android.app.usage.UsageEvents.Event.SCREEN_NON_INTERACTIVE;
import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
@@ -42,7 +43,9 @@
import android.app.usage.EventStats;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
import android.content.res.Configuration;
+import android.os.PersistableBundle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -575,6 +578,23 @@
continue;
}
break;
+ case USER_INTERACTION:
+ if (event.mUserInteractionExtrasToken != null) {
+ String category = packagesTokenData.getString(packageToken,
+ event.mUserInteractionExtrasToken.mCategoryToken);
+ String action = packagesTokenData.getString(packageToken,
+ event.mUserInteractionExtrasToken.mActionToken);
+ if (TextUtils.isEmpty(category) || TextUtils.isEmpty(action)) {
+ this.events.remove(i);
+ dataOmitted = true;
+ continue;
+ }
+ event.mExtras = new PersistableBundle();
+ event.mExtras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, category);
+ event.mExtras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, action);
+ event.mUserInteractionExtrasToken = null;
+ }
+ break;
}
}
if (dataOmitted) {
@@ -692,13 +712,30 @@
event.mPackage, event.mLocusId);
}
break;
+ case USER_INTERACTION:
+ if (event.mExtras != null && event.mExtras.size() != 0) {
+ final String category = event.mExtras.getString(
+ UsageStatsManager.EXTRA_EVENT_CATEGORY);
+ final String action = event.mExtras.getString(
+ UsageStatsManager.EXTRA_EVENT_ACTION);
+ if (!TextUtils.isEmpty(category) && !TextUtils.isEmpty(action)) {
+ event.mUserInteractionExtrasToken =
+ new Event.UserInteractionEventExtrasToken();
+ event.mUserInteractionExtrasToken.mCategoryToken =
+ packagesTokenData.getTokenOrAdd(packageToken, event.mPackage,
+ category);
+ event.mUserInteractionExtrasToken.mActionToken =
+ packagesTokenData.getTokenOrAdd(packageToken, event.mPackage,
+ action);
+ }
+ }
+ break;
}
}
}
/**
* Obfuscates the data in this instance of interval stats.
- *
* @hide
*/
public void obfuscateData(PackagesTokenData packagesTokenData) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
index 8138747..d865345 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
@@ -17,8 +17,10 @@
import android.app.usage.ConfigurationStats;
import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event.UserInteractionEventExtrasToken;
import android.app.usage.UsageStats;
import android.content.res.Configuration;
+import android.os.PersistableBundle;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -26,6 +28,8 @@
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -282,6 +286,16 @@
event.mLocusIdToken = proto.readInt(
EventObfuscatedProto.LOCUS_ID_TOKEN) - 1;
break;
+ case (int) EventObfuscatedProto.INTERACTION_EXTRAS:
+ try {
+ final long interactionExtrasToken = proto.start(
+ EventObfuscatedProto.INTERACTION_EXTRAS);
+ event.mUserInteractionExtrasToken = parseUserInteractionEventExtras(proto);
+ proto.end(interactionExtrasToken);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read some user interaction extras from proto.", e);
+ }
+ break;
case ProtoInputStream.NO_MORE_FIELDS:
return event.mPackageToken == PackagesTokenData.UNASSIGNED_TOKEN ? null : event;
}
@@ -386,7 +400,7 @@
}
private static void writeEvent(ProtoOutputStream proto, final long statsBeginTime,
- final UsageEvents.Event event) throws IllegalArgumentException {
+ final UsageEvents.Event event) throws IOException, IllegalArgumentException {
proto.write(EventObfuscatedProto.PACKAGE_TOKEN, event.mPackageToken + 1);
if (event.mClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
proto.write(EventObfuscatedProto.CLASS_TOKEN, event.mClassToken + 1);
@@ -429,6 +443,12 @@
event.mNotificationChannelIdToken + 1);
}
break;
+ case UsageEvents.Event.USER_INTERACTION:
+ if (event.mUserInteractionExtrasToken != null) {
+ writeUserInteractionEventExtras(proto, EventObfuscatedProto.INTERACTION_EXTRAS,
+ event.mUserInteractionExtrasToken);
+ }
+ break;
}
}
@@ -703,6 +723,9 @@
case (int) PendingEventProto.TASK_ROOT_CLASS:
event.mTaskRootClass = proto.readString(PendingEventProto.TASK_ROOT_CLASS);
break;
+ case (int) PendingEventProto.EXTRAS:
+ event.mExtras = parsePendingEventExtras(proto, PendingEventProto.EXTRAS);
+ break;
case ProtoInputStream.NO_MORE_FIELDS:
// Handle default values for certain events types
switch (event.mEventType) {
@@ -757,7 +780,7 @@
}
private static void writePendingEvent(ProtoOutputStream proto, UsageEvents.Event event)
- throws IllegalArgumentException {
+ throws IOException, IllegalArgumentException {
proto.write(PendingEventProto.PACKAGE_NAME, event.mPackage);
if (event.mClass != null) {
proto.write(PendingEventProto.CLASS_NAME, event.mClass);
@@ -794,6 +817,11 @@
event.mNotificationChannelId);
}
break;
+ case UsageEvents.Event.USER_INTERACTION:
+ if (event.mExtras != null && event.mExtras.size() != 0) {
+ writePendingEventExtras(proto, PendingEventProto.EXTRAS, event.mExtras);
+ }
+ break;
}
}
@@ -888,4 +916,52 @@
proto.end(token);
}
}
+
+ private static UserInteractionEventExtrasToken parseUserInteractionEventExtras(
+ ProtoInputStream proto) throws IOException {
+ UserInteractionEventExtrasToken interactionExtrasToken =
+ new UserInteractionEventExtrasToken();
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) ObfuscatedUserInteractionExtrasProto.CATEGORY_TOKEN:
+ interactionExtrasToken.mCategoryToken = proto.readInt(
+ ObfuscatedUserInteractionExtrasProto.CATEGORY_TOKEN) - 1;
+ break;
+ case (int) ObfuscatedUserInteractionExtrasProto.ACTION_TOKEN:
+ interactionExtrasToken.mActionToken = proto.readInt(
+ ObfuscatedUserInteractionExtrasProto.ACTION_TOKEN) - 1;
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return interactionExtrasToken;
+ }
+ }
+ }
+
+ static void writeUserInteractionEventExtras(ProtoOutputStream proto, long fieldId,
+ UserInteractionEventExtrasToken interactionExtras) {
+ final long token = proto.start(fieldId);
+ proto.write(ObfuscatedUserInteractionExtrasProto.CATEGORY_TOKEN,
+ interactionExtras.mCategoryToken + 1);
+ proto.write(ObfuscatedUserInteractionExtrasProto.ACTION_TOKEN,
+ interactionExtras.mActionToken + 1);
+ proto.end(token);
+ }
+
+ /**
+ * Populates the extra details for pending interaction event from the protobuf stream.
+ */
+ private static PersistableBundle parsePendingEventExtras(ProtoInputStream proto, long fieldId)
+ throws IOException {
+ return PersistableBundle.readFromStream(new ByteArrayInputStream(proto.readBytes(fieldId)));
+ }
+
+ /**
+ * Write the extra details for pending interaction event to a protobuf stream.
+ */
+ static void writePendingEventExtras(ProtoOutputStream proto, long fieldId,
+ PersistableBundle eventExtras) throws IOException {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ eventExtras.writeToStream(baos);
+ proto.write(fieldId, baos.toByteArray());
+ }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 4c56f33..0e1e0c8 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -83,6 +83,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -91,6 +92,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -193,6 +195,11 @@
private static final char TOKEN_DELIMITER = '/';
+ // The maximum length for extras {@link UsageStatsManager#EXTRA_EVENT_CATEGORY},
+ // {@link UsageStatsManager#EXTRA_EVENT_ACTION} in a {@link UsageEvents.Event#mExtras}.
+ // The value will be truncated at this limit.
+ private static final int MAX_TEXT_LENGTH = 127;
+
// Handler message types.
static final int MSG_REPORT_EVENT = 0;
static final int MSG_FLUSH_TO_DISK = 1;
@@ -1814,6 +1821,13 @@
mHandler.removeMessages(MSG_FLUSH_TO_DISK);
}
+ private String getTrimmedString(String input) {
+ if (input != null && input.length() > MAX_TEXT_LENGTH) {
+ return input.substring(0, MAX_TEXT_LENGTH);
+ }
+ return input;
+ }
+
/**
* Called by the Binder stub.
*/
@@ -2253,6 +2267,32 @@
}
}
+ private void reportUserInteractionInnerHelper(String packageName, @UserIdInt int userId,
+ PersistableBundle extras) {
+ if (Flags.reportUsageStatsPermission()) {
+ if (!canReportUsageStats()) {
+ throw new SecurityException(
+ "Only the system or holders of the REPORT_USAGE_STATS"
+ + " permission are allowed to call reportUserInteraction");
+ }
+ } else {
+ if (!isCallingUidSystem()) {
+ throw new SecurityException("Only system is allowed to call"
+ + " reportUserInteraction");
+ }
+ }
+
+ // Verify if this package exists before reporting an event for it.
+ if (mPackageManagerInternal.getPackageUid(packageName, 0, userId) < 0) {
+ throw new IllegalArgumentException("Package " + packageName + "not exist!");
+ }
+
+ final Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
+ event.mPackage = packageName;
+ event.mExtras = extras;
+ reportEventOrAddToQueue(userId, event);
+ }
+
@Override
public ParceledListSlice<UsageStats> queryUsageStats(int bucketType, long beginTime,
long endTime, String callingPackage, int userId) {
@@ -2686,23 +2726,36 @@
@Override
public void reportUserInteraction(String packageName, int userId) {
+ reportUserInteractionInnerHelper(packageName, userId, null);
+ }
+
+ @Override
+ public void reportUserInteractionWithBundle(String packageName, @UserIdInt int userId,
+ PersistableBundle extras) {
Objects.requireNonNull(packageName);
- if (Flags.reportUsageStatsPermission()) {
- if (!canReportUsageStats()) {
- throw new SecurityException(
- "Only the system or holders of the REPORT_USAGE_STATS"
- + " permission are allowed to call reportUserInteraction");
- }
- } else {
- if (!isCallingUidSystem()) {
- throw new SecurityException("Only system is allowed to call"
- + " reportUserInteraction");
- }
+ if (extras == null || extras.size() == 0) {
+ throw new IllegalArgumentException("Emtry extras!");
}
- final Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
- event.mPackage = packageName;
- reportEventOrAddToQueue(userId, event);
+ // Only category/action are allowed now, other unknown keys will be trimmed.
+ // Also, empty category/action is not meanful.
+ String category = extras.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY);
+ if (TextUtils.isEmpty(category)) {
+ throw new IllegalArgumentException("Empty "
+ + UsageStatsManager.EXTRA_EVENT_CATEGORY);
+ }
+ String action = extras.getString(UsageStatsManager.EXTRA_EVENT_ACTION);
+ if (TextUtils.isEmpty(action)) {
+ throw new IllegalArgumentException("Empty "
+ + UsageStatsManager.EXTRA_EVENT_ACTION);
+ }
+
+ PersistableBundle extrasCopy = new PersistableBundle();
+ extrasCopy.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY,
+ getTrimmedString(category));
+ extrasCopy.putString(UsageStatsManager.EXTRA_EVENT_ACTION, getTrimmedString(action));
+
+ reportUserInteractionInnerHelper(packageName, userId, extrasCopy);
}
@Override
@@ -3160,6 +3213,24 @@
}
@Override
+ public void reportUserInteractionEvent(@NonNull String pkgName, @UserIdInt int userId,
+ @NonNull PersistableBundle extras) {
+ if (extras != null && extras.size() != 0) {
+ // Truncate the value if necessary.
+ String category = extras.getString(UsageStatsManager.EXTRA_EVENT_CATEGORY);
+ String action = extras.getString(UsageStatsManager.EXTRA_EVENT_ACTION);
+ extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY,
+ getTrimmedString(category));
+ extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, getTrimmedString(action));
+ }
+
+ Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
+ event.mPackage = pkgName;
+ event.mExtras = extras;
+ reportEventOrAddToQueue(userId, event);
+ }
+
+ @Override
public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
return mAppStandby.isAppIdleFiltered(packageName, uidForAppId,
userId, SystemClock.elapsedRealtime());
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 9b67ab6..3bc7752 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -1110,6 +1110,10 @@
if (event.mNotificationChannelId != null) {
pw.printPair("channelId", event.mNotificationChannelId);
}
+
+ if ((event.mEventType == Event.USER_INTERACTION) && (event.mExtras != null)) {
+ pw.print(event.mExtras.toString());
+ }
pw.printHexPair("flags", event.mFlags);
pw.println();
}
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
index 4548a7d..1e5f33f 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
@@ -710,9 +710,15 @@
float childFrameRate = Collections.max(frameRates);
float parentFrameRate = childFrameRate / 2;
int initialNumEvents = mModeChangedEvents.size();
- parent.setFrameRate(parentFrameRate);
parent.setFrameRateSelectionStrategy(parentStrategy);
- child.setFrameRate(childFrameRate);
+
+ // For Self case, we want to test that child gets default behavior
+ if (parentStrategy == SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF) {
+ parent.setFrameRateCategory(Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ } else {
+ parent.setFrameRate(parentFrameRate);
+ child.setFrameRate(childFrameRate);
+ }
// Verify
float expectedFrameRate =
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
index bed9cff..29f6879 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
@@ -16,11 +16,8 @@
package android.view.surfacecontroltests;
-import static org.junit.Assume.assumeTrue;
-
import android.Manifest;
import android.hardware.display.DisplayManager;
-import android.os.SystemProperties;
import android.support.test.uiautomator.UiDevice;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -54,10 +51,6 @@
UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- // TODO(b/290634611): clean this up once SF new front end is enabled by default
- assumeTrue(SystemProperties.getBoolean(
- "persist.debug.sf.enable_layer_lifecycle_manager", false));
-
uiDevice.wakeUp();
uiDevice.executeShellCommand("wm dismiss-keyguard");
@@ -118,10 +111,11 @@
}
@Test
- public void testSurfaceControlFrameRateSelectionStrategySelf() throws InterruptedException {
+ public void testSurfaceControlFrameRateSelectionStrategyPropagate()
+ throws InterruptedException {
GraphicsActivity activity = mActivityRule.getActivity();
activity.testSurfaceControlFrameRateSelectionStrategy(
- SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF);
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_PROPAGATE);
}
@Test
@@ -131,4 +125,12 @@
activity.testSurfaceControlFrameRateSelectionStrategy(
SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
}
+
+ @Test
+ public void testSurfaceControlFrameRateSelectionStrategySelf()
+ throws InterruptedException {
+ GraphicsActivity activity = mActivityRule.getActivity();
+ activity.testSurfaceControlFrameRateSelectionStrategy(
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF);
+ }
}
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
index d075b5f..5434c82 100644
--- a/tests/Input/src/com/android/test/input/InputDeviceTest.java
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -51,6 +51,7 @@
assertEquals(device.getName(), outDevice.getName());
assertEquals(device.getVendorId(), outDevice.getVendorId());
assertEquals(device.getProductId(), outDevice.getProductId());
+ assertEquals(device.getDeviceBus(), outDevice.getDeviceBus());
assertEquals(device.getDescriptor(), outDevice.getDescriptor());
assertEquals(device.isExternal(), outDevice.isExternal());
assertEquals(device.getSources(), outDevice.getSources());
@@ -79,6 +80,7 @@
.setName("Test Device " + DEVICE_ID)
.setVendorId(44)
.setProductId(45)
+ .setDeviceBus(3)
.setDescriptor("descriptor")
.setExternal(true)
.setSources(InputDevice.SOURCE_HDMI)
diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
index 85038be..91e6814 100755
--- a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
+++ b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
@@ -51,6 +51,8 @@
HOSTSTUBGEN_OUT=$TEMP/output.txt
+EXTRA_ARGS=""
+
# Because of `set -e`, we can't return non-zero from functions, so we store
# HostStubGen result in it.
HOSTSTUBGEN_RC=0
@@ -115,6 +117,7 @@
--keep-static-initializer-annotation \
android.hosttest.annotation.HostSideTestStaticInitializerKeep \
$filter_arg \
+ $EXTRA_ARGS \
|& tee $HOSTSTUBGEN_OUT
HOSTSTUBGEN_RC=${PIPESTATUS[0]}
echo "HostStubGen exited with $HOSTSTUBGEN_RC"
@@ -209,7 +212,6 @@
com.unsupported.*
"
-
run_hoststubgen_for_failure "One specific class disallowed" \
"TinyFrameworkClassAnnotations is not allowed to have Ravenwood annotations" \
"
@@ -229,6 +231,14 @@
STUB="" IMPL="" run_hoststubgen_for_success "No stub, no impl generation" ""
+EXTRA_ARGS="--in-jar abc" run_hoststubgen_for_failure "Duplicate arg" \
+ "Duplicate or conflicting argument found: --in-jar" \
+ ""
+
+EXTRA_ARGS="--quiet" run_hoststubgen_for_failure "Conflicting arg" \
+ "Duplicate or conflicting argument found: --quiet" \
+ ""
+
echo "All tests passed"
exit 0
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 3cdddc2..dbcf3a5 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -22,7 +22,6 @@
import com.android.hoststubgen.filters.DefaultHookInjectingFilter
import com.android.hoststubgen.filters.FilterPolicy
import com.android.hoststubgen.filters.ImplicitOutputFilter
-import com.android.hoststubgen.filters.KeepAllClassesFilter
import com.android.hoststubgen.filters.OutputFilter
import com.android.hoststubgen.filters.StubIntersectingFilter
import com.android.hoststubgen.filters.createFilterFromTextPolicyFile
@@ -52,15 +51,15 @@
val errors = HostStubGenErrors()
// Load all classes.
- val allClasses = loadClassStructures(options.inJar)
+ val allClasses = loadClassStructures(options.inJar.get)
// Dump the classes, if specified.
- options.inputJarDumpFile?.let {
+ options.inputJarDumpFile.ifSet {
PrintWriter(it).use { pw -> allClasses.dump(pw) }
log.i("Dump file created at $it")
}
- options.inputJarAsKeepAllFile?.let {
+ options.inputJarAsKeepAllFile.ifSet {
PrintWriter(it).use {
pw -> allClasses.forEach {
classNode -> printAsTextPolicy(pw, classNode)
@@ -74,11 +73,11 @@
// Transform the jar.
convert(
- options.inJar,
- options.outStubJar,
- options.outImplJar,
+ options.inJar.get,
+ options.outStubJar.get,
+ options.outImplJar.get,
filter,
- options.enableClassChecker,
+ options.enableClassChecker.get,
allClasses,
errors,
)
@@ -153,7 +152,7 @@
// text-file based filter, which is handled by parseTextFilterPolicyFile.
// The first filter is for the default policy from the command line options.
- var filter: OutputFilter = ConstantFilter(options.defaultPolicy, "default-by-options")
+ var filter: OutputFilter = ConstantFilter(options.defaultPolicy.get, "default-by-options")
// Next, we need a filter that resolves "class-wide" policies.
// This is used when a member (methods, fields, nested classes) don't get any polices
@@ -163,16 +162,16 @@
// Inject default hooks from options.
filter = DefaultHookInjectingFilter(
- options.defaultClassLoadHook,
- options.defaultMethodCallHook,
+ options.defaultClassLoadHook.get,
+ options.defaultMethodCallHook.get,
filter
)
- val annotationAllowedClassesFilter = options.annotationAllowedClassesFile.let { filename ->
- if (filename == null) {
+ val annotationAllowedClassesFilter = options.annotationAllowedClassesFile.get.let { file ->
+ if (file == null) {
ClassFilter.newNullFilter(true) // Allow all classes
} else {
- ClassFilter.loadFromFile(filename, false)
+ ClassFilter.loadFromFile(file, false)
}
}
@@ -196,7 +195,7 @@
// Next, "text based" filter, which allows to override polices without touching
// the target code.
- options.policyOverrideFile?.let {
+ options.policyOverrideFile.ifSet {
filter = createFilterFromTextPolicyFile(it, allClasses, filter)
}
@@ -212,11 +211,6 @@
// Apply the implicit filter.
filter = ImplicitOutputFilter(errors, allClasses, filter)
- // Optionally keep all classes.
- if (options.keepAllClasses) {
- filter = KeepAllClassesFilter(filter)
- }
-
return filter
}
@@ -422,9 +416,9 @@
outVisitor = CheckClassAdapter(outVisitor)
}
val visitorOptions = BaseAdapter.Options(
- enablePreTrace = options.enablePreTrace,
- enablePostTrace = options.enablePostTrace,
- enableNonStubMethodCallDetection = options.enableNonStubMethodCallDetection,
+ enablePreTrace = options.enablePreTrace.get,
+ enablePostTrace = options.enablePostTrace.get,
+ enableNonStubMethodCallDetection = options.enableNonStubMethodCallDetection.get,
errors = errors,
)
outVisitor = BaseAdapter.getVisitor(classInternalName, classes, outVisitor, filter,
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index 83f873d..0ae52af 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -21,21 +21,60 @@
import java.io.FileReader
/**
+ * A single value that can only set once.
+ */
+class SetOnce<T>(
+ private var value: T,
+) {
+ class SetMoreThanOnceException : Exception()
+
+ private var set = false
+
+ fun set(v: T) {
+ if (set) {
+ throw SetMoreThanOnceException()
+ }
+ if (v == null) {
+ throw NullPointerException("This shouldn't happen")
+ }
+ set = true
+ value = v
+ }
+
+ val get: T
+ get() = this.value
+
+ val isSet: Boolean
+ get() = this.set
+
+ fun <R> ifSet(block: (T & Any) -> R): R? {
+ if (isSet) {
+ return block(value!!)
+ }
+ return null
+ }
+
+ override fun toString(): String {
+ return "$value"
+ }
+}
+
+/**
* Options that can be set from command line arguments.
*/
class HostStubGenOptions(
/** Input jar file*/
- var inJar: String = "",
+ var inJar: SetOnce<String> = SetOnce(""),
/** Output stub jar file */
- var outStubJar: String? = null,
+ var outStubJar: SetOnce<String?> = SetOnce(null),
/** Output implementation jar file */
- var outImplJar: String? = null,
+ var outImplJar: SetOnce<String?> = SetOnce(null),
- var inputJarDumpFile: String? = null,
+ var inputJarDumpFile: SetOnce<String?> = SetOnce(null),
- var inputJarAsKeepAllFile: String? = null,
+ var inputJarAsKeepAllFile: SetOnce<String?> = SetOnce(null),
var stubAnnotations: MutableSet<String> = mutableSetOf(),
var keepAnnotations: MutableSet<String> = mutableSetOf(),
@@ -51,27 +90,26 @@
var packageRedirects: MutableList<Pair<String, String>> = mutableListOf(),
- var annotationAllowedClassesFile: String? = null,
+ var annotationAllowedClassesFile: SetOnce<String?> = SetOnce(null),
- var defaultClassLoadHook: String? = null,
- var defaultMethodCallHook: String? = null,
+ var defaultClassLoadHook: SetOnce<String?> = SetOnce(null),
+ var defaultMethodCallHook: SetOnce<String?> = SetOnce(null),
var intersectStubJars: MutableSet<String> = mutableSetOf(),
- var policyOverrideFile: String? = null,
+ var policyOverrideFile: SetOnce<String?> = SetOnce(null),
- var defaultPolicy: FilterPolicy = FilterPolicy.Remove,
- var keepAllClasses: Boolean = false,
+ var defaultPolicy: SetOnce<FilterPolicy> = SetOnce(FilterPolicy.Remove),
- var logLevel: LogLevel = LogLevel.Info,
+ var logLevel: SetOnce<LogLevel> = SetOnce(LogLevel.Info),
- var cleanUpOnError: Boolean = false,
+ var cleanUpOnError: SetOnce<Boolean> = SetOnce(true),
- var enableClassChecker: Boolean = false,
- var enablePreTrace: Boolean = false,
- var enablePostTrace: Boolean = false,
+ var enableClassChecker: SetOnce<Boolean> = SetOnce(false),
+ var enablePreTrace: SetOnce<Boolean> = SetOnce(false),
+ var enablePostTrace: SetOnce<Boolean> = SetOnce(false),
- var enableNonStubMethodCallDetection: Boolean = false,
+ var enableNonStubMethodCallDetection: SetOnce<Boolean> = SetOnce(false),
) {
companion object {
@@ -111,110 +149,120 @@
break
}
- when (arg) {
- // TODO: Write help
- "-h", "--h" -> TODO("Help is not implemented yet")
+ // Define some shorthands...
+ fun nextArg(): String = ai.nextArgRequired(arg)
+ fun SetOnce<String>.setNextStringArg(): String = nextArg().also { this.set(it) }
+ fun SetOnce<String?>.setNextStringArg(): String = nextArg().also { this.set(it) }
+ fun MutableSet<String>.addUniqueAnnotationArg(): String =
+ nextArg().also { this += ensureUniqueAnnotation(it) }
- "-v", "--verbose" -> ret.logLevel = LogLevel.Verbose
- "-d", "--debug" -> ret.logLevel = LogLevel.Debug
- "-q", "--quiet" -> ret.logLevel = LogLevel.None
+ try {
+ when (arg) {
+ // TODO: Write help
+ "-h", "--help" -> TODO("Help is not implemented yet")
- "--in-jar" -> ret.inJar = ai.nextArgRequired(arg).ensureFileExists()
- "--out-stub-jar" -> ret.outStubJar = ai.nextArgRequired(arg)
- "--out-impl-jar" -> ret.outImplJar = ai.nextArgRequired(arg)
+ "-v", "--verbose" -> ret.logLevel.set(LogLevel.Verbose)
+ "-d", "--debug" -> ret.logLevel.set(LogLevel.Debug)
+ "-q", "--quiet" -> ret.logLevel.set(LogLevel.None)
- "--policy-override-file" ->
- ret.policyOverrideFile = ai.nextArgRequired(arg).ensureFileExists()
+ "--in-jar" -> ret.inJar.setNextStringArg().ensureFileExists()
+ "--out-stub-jar" -> ret.outStubJar.setNextStringArg()
+ "--out-impl-jar" -> ret.outImplJar.setNextStringArg()
- "--clean-up-on-error" -> ret.cleanUpOnError = true
- "--no-clean-up-on-error" -> ret.cleanUpOnError = false
+ "--policy-override-file" ->
+ ret.policyOverrideFile.setNextStringArg().ensureFileExists()
- "--default-remove" -> ret.defaultPolicy = FilterPolicy.Remove
- "--default-throw" -> ret.defaultPolicy = FilterPolicy.Throw
- "--default-keep" -> ret.defaultPolicy = FilterPolicy.Keep
- "--default-stub" -> ret.defaultPolicy = FilterPolicy.Stub
+ "--clean-up-on-error" -> ret.cleanUpOnError.set(true)
+ "--no-clean-up-on-error" -> ret.cleanUpOnError.set(false)
- "--keep-all-classes" -> ret.keepAllClasses = true
- "--no-keep-all-classes" -> ret.keepAllClasses = false
+ "--default-remove" -> ret.defaultPolicy.set(FilterPolicy.Remove)
+ "--default-throw" -> ret.defaultPolicy.set(FilterPolicy.Throw)
+ "--default-keep" -> ret.defaultPolicy.set(FilterPolicy.Keep)
+ "--default-stub" -> ret.defaultPolicy.set(FilterPolicy.Stub)
- "--stub-annotation" ->
- ret.stubAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--stub-annotation" ->
+ ret.stubAnnotations.addUniqueAnnotationArg()
- "--keep-annotation" ->
- ret.keepAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--keep-annotation" ->
+ ret.keepAnnotations.addUniqueAnnotationArg()
- "--stub-class-annotation" ->
- ret.stubClassAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--stub-class-annotation" ->
+ ret.stubClassAnnotations.addUniqueAnnotationArg()
- "--keep-class-annotation" ->
- ret.keepClassAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--keep-class-annotation" ->
+ ret.keepClassAnnotations.addUniqueAnnotationArg()
- "--throw-annotation" ->
- ret.throwAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--throw-annotation" ->
+ ret.throwAnnotations.addUniqueAnnotationArg()
- "--remove-annotation" ->
- ret.removeAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--remove-annotation" ->
+ ret.removeAnnotations.addUniqueAnnotationArg()
- "--substitute-annotation" ->
- ret.substituteAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--substitute-annotation" ->
+ ret.substituteAnnotations.addUniqueAnnotationArg()
- "--native-substitute-annotation" ->
- ret.nativeSubstituteAnnotations +=
- ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--native-substitute-annotation" ->
+ ret.nativeSubstituteAnnotations.addUniqueAnnotationArg()
- "--class-load-hook-annotation" ->
- ret.classLoadHookAnnotations +=
- ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--class-load-hook-annotation" ->
+ ret.classLoadHookAnnotations.addUniqueAnnotationArg()
- "--keep-static-initializer-annotation" ->
- ret.keepStaticInitializerAnnotations +=
- ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--keep-static-initializer-annotation" ->
+ ret.keepStaticInitializerAnnotations.addUniqueAnnotationArg()
- "--package-redirect" ->
- ret.packageRedirects += parsePackageRedirect(ai.nextArgRequired(arg))
+ "--package-redirect" ->
+ ret.packageRedirects += parsePackageRedirect(ai.nextArgRequired(arg))
- "--annotation-allowed-classes-file" ->
- ret.annotationAllowedClassesFile = ai.nextArgRequired(arg)
+ "--annotation-allowed-classes-file" ->
+ ret.annotationAllowedClassesFile.setNextStringArg()
- "--default-class-load-hook" ->
- ret.defaultClassLoadHook = ai.nextArgRequired(arg)
+ "--default-class-load-hook" ->
+ ret.defaultClassLoadHook.setNextStringArg()
- "--default-method-call-hook" ->
- ret.defaultMethodCallHook = ai.nextArgRequired(arg)
+ "--default-method-call-hook" ->
+ ret.defaultMethodCallHook.setNextStringArg()
- "--intersect-stub-jar" ->
- ret.intersectStubJars += ai.nextArgRequired(arg).ensureFileExists()
+ "--intersect-stub-jar" ->
+ ret.intersectStubJars += nextArg().ensureFileExists()
- "--gen-keep-all-file" ->
- ret.inputJarAsKeepAllFile = ai.nextArgRequired(arg)
+ "--gen-keep-all-file" ->
+ ret.inputJarAsKeepAllFile.setNextStringArg()
- // Following options are for debugging.
- "--enable-class-checker" -> ret.enableClassChecker = true
- "--no-class-checker" -> ret.enableClassChecker = false
+ // Following options are for debugging.
+ "--enable-class-checker" -> ret.enableClassChecker.set(true)
+ "--no-class-checker" -> ret.enableClassChecker.set(false)
- "--enable-pre-trace" -> ret.enablePreTrace = true
- "--no-pre-trace" -> ret.enablePreTrace = false
+ "--enable-pre-trace" -> ret.enablePreTrace.set(true)
+ "--no-pre-trace" -> ret.enablePreTrace.set(false)
- "--enable-post-trace" -> ret.enablePostTrace = true
- "--no-post-trace" -> ret.enablePostTrace = false
+ "--enable-post-trace" -> ret.enablePostTrace.set(true)
+ "--no-post-trace" -> ret.enablePostTrace.set(false)
- "--enable-non-stub-method-check" -> ret.enableNonStubMethodCallDetection = true
- "--no-non-stub-method-check" -> ret.enableNonStubMethodCallDetection = false
+ "--enable-non-stub-method-check" ->
+ ret.enableNonStubMethodCallDetection.set(true)
- "--gen-input-dump-file" -> ret.inputJarDumpFile = ai.nextArgRequired(arg)
+ "--no-non-stub-method-check" ->
+ ret.enableNonStubMethodCallDetection.set(false)
- else -> throw ArgumentsException("Unknown option: $arg")
+ "--gen-input-dump-file" -> ret.inputJarDumpFile.setNextStringArg()
+
+ else -> throw ArgumentsException("Unknown option: $arg")
+ }
+ } catch (e: SetOnce.SetMoreThanOnceException) {
+ throw ArgumentsException("Duplicate or conflicting argument found: $arg")
}
}
- if (ret.inJar.isEmpty()) {
+ log.w(ret.toString())
+
+ if (!ret.inJar.isSet) {
throw ArgumentsException("Required option missing: --in-jar")
}
- if (ret.outStubJar == null && ret.outImplJar == null) {
+ if (!ret.outStubJar.isSet && !ret.outImplJar.isSet) {
log.w("Neither --out-stub-jar nor --out-impl-jar is set." +
" $COMMAND_NAME will not generate jar files.")
}
- if (ret.enableNonStubMethodCallDetection) {
+ if (ret.enableNonStubMethodCallDetection.get) {
log.w("--enable-non-stub-method-check is not fully implemented yet." +
" See the todo in doesMethodNeedNonStubCallCheck().")
}
@@ -329,7 +377,6 @@
intersectStubJars=$intersectStubJars,
policyOverrideFile=$policyOverrideFile,
defaultPolicy=$defaultPolicy,
- keepAllClasses=$keepAllClasses,
logLevel=$logLevel,
cleanUpOnError=$cleanUpOnError,
enableClassChecker=$enableClassChecker,
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
index 0321d9d..38ba0cc 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
@@ -28,9 +28,9 @@
try {
// Parse the command line arguments.
val options = HostStubGenOptions.parseArgs(args)
- clanupOnError = options.cleanUpOnError
+ clanupOnError = options.cleanUpOnError.get
- log.level = options.logLevel
+ log.level = options.logLevel.get
log.v("HostStubGen started")
log.v("Options: $options")
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
index 356e1fa..8354d98 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
@@ -39,5 +39,5 @@
private fun ClassNodes.isAidlClass(className: String): Boolean {
return hasClass(className) &&
hasClass("$className\$Stub") &&
- hasClass("$className\$Proxy")
+ hasClass("$className\$Stub\$Proxy")
}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepAllClassesFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepAllClassesFilter.kt
deleted file mode 100644
index 45dd38d1..0000000
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepAllClassesFilter.kt
+++ /dev/null
@@ -1,30 +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.hoststubgen.filters
-
-/**
- * An [OutputFilter] that keeps all classes by default. (but none of its members)
- *
- * We're not currently using it, but using it *might* make certain things easier. For example, with
- * this, all classes would at least be loadable.
- */
-class KeepAllClassesFilter(fallback: OutputFilter) : DelegatingFilter(fallback) {
- override fun getPolicyForClass(className: String): FilterPolicyWithReason {
- // If the default visibility wouldn't keep it, change it to "keep".
- val f = super.getPolicyForClass(className)
- return f.promoteToKeep("keep-all-classes")
- }
-}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 214de59..3956893 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -223,16 +223,16 @@
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD]
)
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 3
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+ public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
@@ -243,7 +243,7 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
public static int addTwo(int);
descriptor: (I)I
@@ -262,7 +262,8 @@
SourceFile: "IPretendingAidl.java"
NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class
Compiled from "IPretendingAidl.java"
public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
@@ -303,6 +304,7 @@
NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class
Compiled from "IPretendingAidl.java"
public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
@@ -315,11 +317,11 @@
}
SourceFile: "IPretendingAidl.java"
NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+ com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index 9031228..9349355 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -1,13 +1,13 @@
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 4
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+ public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
@@ -30,7 +30,8 @@
x: athrow
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -71,6 +72,7 @@
}
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -89,8 +91,8 @@
interfaces: 0, fields: 0, methods: 0, attributes: 4
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -98,8 +100,8 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+ com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index e01f49b..4f8c408e 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -205,16 +205,16 @@
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 4
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+ public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
@@ -225,7 +225,7 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy;
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
public static int addTwo(int);
descriptor: (I)I
@@ -242,7 +242,8 @@
0 4 0 a I
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -288,6 +289,7 @@
}
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -306,8 +308,8 @@
interfaces: 0, fields: 0, methods: 0, attributes: 4
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -315,8 +317,8 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+ com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index 9031228..9349355 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -1,13 +1,13 @@
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 4
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+ public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
@@ -30,7 +30,8 @@
x: athrow
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -71,6 +72,7 @@
}
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -89,8 +91,8 @@
interfaces: 0, fields: 0, methods: 0, attributes: 4
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
- public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -98,8 +100,8 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+ com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index 5246355..5ff3cde 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -289,13 +289,13 @@
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy.class
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
- this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
super_class: #x // java/lang/Object
interfaces: 0, fields: 0, methods: 3, attributes: 4
private static {};
@@ -303,17 +303,17 @@
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
- public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Proxy();
+ public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
x: ldc #x // String <init>
x: ldc #x // String ()V
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -324,14 +324,14 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy;
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
public static int addTwo(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
x: ldc #x // String addTwo
x: ldc #x // String (I)I
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -346,7 +346,8 @@
11 4 0 a I
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -412,6 +413,7 @@
}
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -439,8 +441,8 @@
x: return
}
InnerClasses:
- public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+ public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
SourceFile: "IPretendingAidl.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -448,8 +450,8 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestMembers:
- com/android/hoststubgen/test/tinyframework/IPretendingAidl$Proxy
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+ com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
index 583e13c..0a07c2b 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
@@ -25,11 +25,12 @@
public static int addOne(int a) {
return a + 1;
}
- }
- public static class Proxy {
- public static int addTwo(int a) {
- return a + 2;
+ public static class Proxy {
+ public static int addTwo(int a) {
+ return a + 2;
+ }
}
}
+
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index 0d52791..d350105 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -289,6 +289,6 @@
@Test
public void testAidlHeuristics() {
assertThat(IPretendingAidl.Stub.addOne(1)).isEqualTo(2);
- assertThat(IPretendingAidl.Proxy.addTwo(1)).isEqualTo(3);
+ assertThat(IPretendingAidl.Stub.Proxy.addTwo(1)).isEqualTo(3);
}
}