Merge "Reattaching and dismissing notifications when detached." into main
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index a9c4a15..6dd7521 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -36,7 +36,6 @@
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.UidObserver;
-import android.app.compat.CompatChanges;
import android.app.job.JobInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
@@ -54,6 +53,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
@@ -74,6 +74,7 @@
import com.android.server.AppSchedulingModuleThread;
import com.android.server.LocalServices;
import com.android.server.PowerAllowlistInternal;
+import com.android.server.compat.PlatformCompat;
import com.android.server.job.ConstantsProto;
import com.android.server.job.Flags;
import com.android.server.job.JobSchedulerService;
@@ -157,6 +158,15 @@
@Overridable // The change can be overridden in user build.
static final long OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS = 374323858L;
+ /**
+ * When enabled this change id overrides the default quota parameters adjustment.
+ */
+ @VisibleForTesting
+ @ChangeId
+ @Disabled // Disabled by default
+ @Overridable // The change can be overridden in user build.
+ static final long OVERRIDE_QUOTA_ADJUST_DEFAULT_CONSTANTS = 378129159L;
+
@VisibleForTesting
static class ExecutionStats {
/**
@@ -536,6 +546,8 @@
*/
private final SparseSetArray<String> mSystemInstallers = new SparseSetArray<>();
+ private final PlatformCompat mPlatformCompat;
+
/** An app has reached its quota. The message should contain a {@link UserPackage} object. */
@VisibleForTesting
static final int MSG_REACHED_TIME_QUOTA = 0;
@@ -587,6 +599,13 @@
PowerAllowlistInternal pai = LocalServices.getService(PowerAllowlistInternal.class);
pai.registerTempAllowlistChangeListener(new TempAllowlistTracker());
+ mPlatformCompat = (PlatformCompat)
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
+ if (Flags.adjustQuotaDefaultConstants()) {
+ mPlatformCompat.registerListener(OVERRIDE_QUOTA_ADJUST_DEFAULT_CONSTANTS,
+ (packageName) -> handleQuotaDefaultConstantsCompatChange());
+ }
+
try {
ActivityManager.getService().registerUidObserver(new QcUidObserver(),
ActivityManager.UID_OBSERVER_PROCSTATE,
@@ -651,8 +670,9 @@
final int uid = jobStatus.getSourceUid();
if ((!Flags.enforceQuotaPolicyToTopStartedJobs()
- || CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS,
- uid)) && mTopAppCache.get(uid)) {
+ || mPlatformCompat.isChangeEnabledByUid(
+ OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS, uid))
+ && mTopAppCache.get(uid)) {
if (DEBUG) {
Slog.d(TAG, jobStatus.toShortString() + " is top started job");
}
@@ -690,8 +710,8 @@
}
}
if (!Flags.enforceQuotaPolicyToTopStartedJobs()
- || CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS,
- jobStatus.getSourceUid())) {
+ || mPlatformCompat.isChangeEnabledByUid(
+ OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS, jobStatus.getSourceUid())) {
mTopStartedJobs.remove(jobStatus);
}
}
@@ -805,8 +825,8 @@
/** @return true if the job was started while the app was in the TOP state. */
private boolean isTopStartedJobLocked(@NonNull final JobStatus jobStatus) {
if (!Flags.enforceQuotaPolicyToTopStartedJobs()
- || CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS,
- jobStatus.getSourceUid())) {
+ || mPlatformCompat.isChangeEnabledByUid(
+ OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS, jobStatus.getSourceUid())) {
return mTopStartedJobs.contains(jobStatus);
}
@@ -1102,6 +1122,7 @@
final int standbyBucket) {
final long baseLimitMs = mAllowedTimePerPeriodMs[standbyBucket];
if (Flags.adjustQuotaDefaultConstants()
+ && !isCompatOverridedForQuotaConstantAdjustment()
&& Flags.additionalQuotaForSystemInstaller()
&& standbyBucket == EXEMPTED_INDEX
&& mSystemInstallers.contains(userId, pkgName)) {
@@ -1473,10 +1494,21 @@
}
}
+ void handleQuotaDefaultConstantsCompatChange() {
+ synchronized (mLock) {
+ final boolean isCompatEnabled = isCompatOverridedForQuotaConstantAdjustment();
+ mQcConstants.adjustDefaultBucketWindowSizes(isCompatEnabled);
+ mQcConstants.adjustDefaultEjLimits(isCompatEnabled);
+ mQcConstants.mShouldReevaluateConstraints = true;
+ onConstantsUpdatedLocked();
+ }
+ }
+
void processQuotaConstantsAdjustment() {
- if (Flags.adjustQuotaDefaultConstants()) {
- mQcConstants.adjustDefaultBucketWindowSizes();
- mQcConstants.adjustDefaultEjLimits();
+ if (Flags.adjustQuotaDefaultConstants()
+ && !isCompatOverridedForQuotaConstantAdjustment()) {
+ mQcConstants.adjustDefaultBucketWindowSizes(false);
+ mQcConstants.adjustDefaultEjLimits(false);
}
}
@@ -1505,6 +1537,11 @@
}
}
+ private boolean isCompatOverridedForQuotaConstantAdjustment() {
+ return mPlatformCompat.isChangeEnabledByPackageName(
+ OVERRIDE_QUOTA_ADJUST_DEFAULT_CONSTANTS, "android", UserHandle.USER_SYSTEM);
+ }
+
private void incrementTimingSessionCountLocked(final int userId,
@NonNull final String packageName) {
final long now = sElapsedRealtimeClock.millis();
@@ -2689,7 +2726,8 @@
@VisibleForTesting
int getProcessStateQuotaFreeThreshold(int uid) {
if (Flags.enforceQuotaPolicyToFgsJobs()
- && !CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_FGS_JOBS, uid)) {
+ && !mPlatformCompat.isChangeEnabledByUid(
+ OVERRIDE_QUOTA_ENFORCEMENT_TO_FGS_JOBS, uid)) {
return ActivityManager.PROCESS_STATE_BOUND_TOP;
}
@@ -3596,25 +3634,40 @@
*/
public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
- void adjustDefaultBucketWindowSizes() {
- ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
- ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
- DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
- ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
- ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
- DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
- ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS = Flags.tuneQuotaWindowDefaultParameters()
- ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS :
- DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;
+ void adjustDefaultBucketWindowSizes(boolean useLegacyQuotaConstants) {
+ if (useLegacyQuotaConstants) {
+ ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;
- WINDOW_SIZE_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
- ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
- DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
- WINDOW_SIZE_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
- ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
- DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
- WINDOW_SIZE_WORKING_MS = DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS;
- WINDOW_SIZE_FREQUENT_MS = DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS;
+ WINDOW_SIZE_EXEMPTED_MS = DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS;
+ WINDOW_SIZE_ACTIVE_MS = DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS;
+ WINDOW_SIZE_WORKING_MS = DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS;
+ WINDOW_SIZE_FREQUENT_MS = DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS;
+ } else {
+ ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+ Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS :
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;
+
+ WINDOW_SIZE_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
+ DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
+ WINDOW_SIZE_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
+ DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
+ WINDOW_SIZE_WORKING_MS = DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS;
+ WINDOW_SIZE_FREQUENT_MS = DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS;
+ }
mAllowedTimePerPeriodMs[EXEMPTED_INDEX] = Math.min(MAX_PERIOD_MS,
Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS));
@@ -3640,10 +3693,15 @@
ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
}
- void adjustDefaultEjLimits() {
- EJ_LIMIT_WORKING_MS = DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS;
- EJ_TOP_APP_TIME_CHUNK_SIZE_MS = DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
- EJ_REWARD_INTERACTION_MS = DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS;
+ void adjustDefaultEjLimits(boolean useLegacyQuotaConstants) {
+ EJ_LIMIT_WORKING_MS = useLegacyQuotaConstants ? DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS
+ : DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS;
+ EJ_TOP_APP_TIME_CHUNK_SIZE_MS = useLegacyQuotaConstants
+ ? DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS :
+ DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
+ EJ_REWARD_INTERACTION_MS = useLegacyQuotaConstants
+ ? DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS
+ : DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS;
// The limit must be in the range [15 minutes, active limit].
mEJLimitsMs[WORKING_INDEX] = Math.max(15 * MINUTE_IN_MILLIS,
@@ -3668,6 +3726,8 @@
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@NonNull String key) {
+ final boolean isCompatEnabled = isCompatOverridedForQuotaConstantAdjustment();
+
switch (key) {
case KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS:
case KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS:
@@ -3835,7 +3895,8 @@
case KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS:
// We don't need to re-evaluate execution stats or constraint status for this.
EJ_TOP_APP_TIME_CHUNK_SIZE_MS =
- properties.getLong(key, Flags.adjustQuotaDefaultConstants()
+ properties.getLong(key,
+ Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS :
DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS);
// Limit chunking to be in the range [1 millisecond, 15 minutes] per event.
@@ -3873,7 +3934,8 @@
case KEY_EJ_REWARD_INTERACTION_MS:
// We don't need to re-evaluate execution stats or constraint status for this.
EJ_REWARD_INTERACTION_MS =
- properties.getLong(key, Flags.adjustQuotaDefaultConstants()
+ properties.getLong(key,
+ Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS :
DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS);
// Limit interaction reward to be in the range [5 seconds, 15 minutes] per
@@ -3914,6 +3976,8 @@
}
mExecutionPeriodConstantsUpdated = true;
+ final boolean isCompatEnabled = isCompatOverridedForQuotaConstantAdjustment();
+
// Query the values as an atomic set.
final DeviceConfig.Properties properties = DeviceConfig.getProperties(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
@@ -3958,27 +4022,27 @@
MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
DEFAULT_MAX_EXECUTION_TIME_MS);
WINDOW_SIZE_EXEMPTED_MS = properties.getLong(KEY_WINDOW_SIZE_EXEMPTED_MS,
- (Flags.adjustQuotaDefaultConstants()
+ (Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
&& Flags.tuneQuotaWindowDefaultParameters())
? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
- (Flags.adjustQuotaDefaultConstants()
+ (Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS));
WINDOW_SIZE_ACTIVE_MS = properties.getLong(KEY_WINDOW_SIZE_ACTIVE_MS,
- (Flags.adjustQuotaDefaultConstants()
+ (Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
&& Flags.tuneQuotaWindowDefaultParameters())
? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
- (Flags.adjustQuotaDefaultConstants()
+ (Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS));
WINDOW_SIZE_WORKING_MS =
properties.getLong(KEY_WINDOW_SIZE_WORKING_MS,
- Flags.adjustQuotaDefaultConstants()
+ Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS :
DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS);
WINDOW_SIZE_FREQUENT_MS =
properties.getLong(KEY_WINDOW_SIZE_FREQUENT_MS,
- Flags.adjustQuotaDefaultConstants()
+ Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS :
DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS);
WINDOW_SIZE_RARE_MS = properties.getLong(KEY_WINDOW_SIZE_RARE_MS,
@@ -4149,6 +4213,8 @@
}
mEJLimitConstantsUpdated = true;
+ final boolean isCompatEnabled = isCompatOverridedForQuotaConstantAdjustment();
+
// Query the values as an atomic set.
final DeviceConfig.Properties properties = DeviceConfig.getProperties(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
@@ -4163,7 +4229,7 @@
EJ_LIMIT_ACTIVE_MS = properties.getLong(
KEY_EJ_LIMIT_ACTIVE_MS, DEFAULT_EJ_LIMIT_ACTIVE_MS);
EJ_LIMIT_WORKING_MS = properties.getLong(
- KEY_EJ_LIMIT_WORKING_MS, Flags.adjustQuotaDefaultConstants()
+ KEY_EJ_LIMIT_WORKING_MS, Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS :
DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS);
EJ_LIMIT_FREQUENT_MS = properties.getLong(
diff --git a/core/api/current.txt b/core/api/current.txt
index fbc8eef..216bbab 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -34264,6 +34264,7 @@
method public boolean hasFileDescriptors();
method public boolean hasFileDescriptors(int, int);
method public byte[] marshall();
+ method @FlaggedApi("android.os.parcel_marshall_bytebuffer") public void marshall(@NonNull java.nio.ByteBuffer);
method @NonNull public static android.os.Parcel obtain();
method @NonNull public static android.os.Parcel obtain(@NonNull android.os.IBinder);
method @Deprecated @Nullable public Object[] readArray(@Nullable ClassLoader);
@@ -34333,6 +34334,7 @@
method public void setDataSize(int);
method public void setPropagateAllowBlocking();
method public void unmarshall(@NonNull byte[], int, int);
+ method @FlaggedApi("android.os.parcel_marshall_bytebuffer") public void unmarshall(@NonNull java.nio.ByteBuffer);
method public void writeArray(@Nullable Object[]);
method public void writeBinderArray(@Nullable android.os.IBinder[]);
method public void writeBinderList(@Nullable java.util.List<android.os.IBinder>);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 35720fd..12f302a 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -7354,7 +7354,15 @@
public class AudioDeviceVolumeManager {
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public android.media.VolumeInfo getDeviceVolume(@NonNull android.media.VolumeInfo, @NonNull android.media.AudioDeviceAttributes);
+ method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE", android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public void setDeviceVolume(@NonNull android.media.VolumeInfo, @NonNull android.media.AudioDeviceAttributes);
+ method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
+ field @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3; // 0x3
+ field @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5; // 0x5
+ field @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4; // 0x4
+ field @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2; // 0x2
+ field @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1; // 0x1
+ field @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0; // 0x0
}
public final class AudioFocusInfo implements android.os.Parcelable {
@@ -7399,7 +7407,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups();
method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioRecord getCallDownlinkExtractionAudioRecord(@NonNull android.media.AudioFormat);
method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioTrack getCallUplinkInjectionAudioTrack(@NonNull android.media.AudioFormat);
- method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE", android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
+ method @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE", android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public java.util.List<android.media.AudioDeviceAttributes> getDevicesForAttributes(@NonNull android.media.AudioAttributes);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public java.util.List<java.lang.Integer> getIndependentStreamTypes();
method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int getLastAudibleStreamVolume(int);
@@ -7445,7 +7453,7 @@
method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setBluetoothVariableLatencyEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setDeviceAsNonDefaultForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
- method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
+ method @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForCapturePreset(int, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
@@ -7465,12 +7473,12 @@
field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int DEVICE_CONNECTION_STATE_CONNECTED = 1; // 0x1
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int DEVICE_CONNECTION_STATE_DISCONNECTED = 0; // 0x0
- field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3; // 0x3
- field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5; // 0x5
- field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4; // 0x4
- field public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2; // 0x2
- field public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1; // 0x1
- field public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0; // 0x0
+ field @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3; // 0x3
+ field @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5; // 0x5
+ field @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4; // 0x4
+ field @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2; // 0x2
+ field @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1; // 0x1
+ field @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0; // 0x0
field public static final String EXTRA_VOLUME_STREAM_TYPE = "android.media.EXTRA_VOLUME_STREAM_TYPE";
field public static final String EXTRA_VOLUME_STREAM_VALUE = "android.media.EXTRA_VOLUME_STREAM_VALUE";
field public static final int FLAG_BLUETOOTH_ABS_VOLUME = 64; // 0x40
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 4c82839..daa1902 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1955,6 +1955,10 @@
method public static void enforceValidAudioDeviceTypeOut(int);
}
+ public class AudioDeviceVolumeManager {
+ method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public boolean isFullVolumeDevice();
+ }
+
public final class AudioFocusRequest {
method @Nullable public android.media.AudioManager.OnAudioFocusChangeListener getOnAudioFocusChangeListener();
}
@@ -2010,7 +2014,6 @@
method @NonNull public android.media.VolumePolicy getVolumePolicy();
method public boolean hasRegisteredDynamicPolicy();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public boolean isCsdEnabled();
- method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public boolean isFullVolumeDevice();
method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public boolean isVolumeControlUsingVolumeGroups();
method public void permissionUpdateBarrier();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 62816a2..9cc7b8f 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1188,7 +1188,7 @@
/** @hide Should this process state be considered jank perceptible? */
public static final boolean isProcStateJankPerceptible(int procState) {
- if (Flags.jankPerceptibleNarrow()) {
+ if (Flags.jankPerceptibleNarrow() && !Flags.jankPerceptibleNarrowHoldback()) {
return procState == PROCESS_STATE_PERSISTENT_UI
|| procState == PROCESS_STATE_TOP
|| procState == PROCESS_STATE_IMPORTANT_FOREGROUND
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 96b5096..f44c230 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4014,7 +4014,7 @@
return VM_PROCESS_STATE_JANK_PERCEPTIBLE;
}
- if (Flags.jankPerceptibleNarrow()) {
+ if (Flags.jankPerceptibleNarrow() && !Flags.jankPerceptibleNarrowHoldback()) {
// Unlike other persistent processes, system server is often on
// the critical path for application startup. Mark it explicitly
// as jank perceptible regardless of processState.
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 8af5b1b..19fecb9 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -101,14 +101,20 @@
*/
public static final String REPORT_KEY_STREAMRESULT = "stream";
- static final String TAG = "Instrumentation";
+ /**
+ * @hide
+ */
+ public static final String TAG = "Instrumentation";
private static final long CONNECT_TIMEOUT_MILLIS = 60_000;
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
- // If set, will print the stack trace for activity starts within the process
- static final boolean DEBUG_START_ACTIVITY = Build.IS_DEBUGGABLE &&
+ /**
+ * If set, will print the stack trace for activity starts within the process
+ * @hide
+ */
+ public static final boolean DEBUG_START_ACTIVITY = Build.IS_DEBUGGABLE &&
SystemProperties.getBoolean("persist.wm.debug.start_activity", false);
static final boolean DEBUG_FINISH_ACTIVITY = Build.IS_DEBUGGABLE &&
SystemProperties.getBoolean("persist.wm.debug.finish_activity", false);
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index afe915e..dd87d28 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -594,7 +594,7 @@
* @see PictureInPictureParams.Builder#setSeamlessResizeEnabled(boolean)
*/
public boolean isSeamlessResizeEnabled() {
- return mSeamlessResizeEnabled == null ? true : mSeamlessResizeEnabled;
+ return mSeamlessResizeEnabled == null ? false : mSeamlessResizeEnabled;
}
/**
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 9574824..7f1870b 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -248,7 +248,9 @@
/**
* Command for {@link #sendWallpaperCommand}: reported by System UI when the device keyguard
* starts going away.
- * This command is triggered by {@link android.app.IActivityTaskManager#keyguardGoingAway(int)}.
+ * <p>
+ * This command is triggered by {@link android.app.IActivityTaskManager#keyguardGoingAway(int)}
+ * or by {@link android.app.IActivityTaskManager#setLockScreenShown(boolean, boolean)}.
*
* @hide
*/
@@ -256,6 +258,18 @@
"android.wallpaper.keyguardgoingaway";
/**
+ * Command for {@link #sendWallpaperCommand}: reported by System UI when the device keyguard
+ * starts going away.
+ *
+ * <p>This command is triggered by
+ * {@link android.app.IActivityTaskManager#setLockScreenShown(boolean, boolean)}.
+ *
+ * @hide
+ */
+ public static final String COMMAND_KEYGUARD_APPEARING =
+ "android.wallpaper.keyguardappearing";
+
+ /**
* Command for {@link #sendWallpaperCommand}: reported by System UI when the device is going to
* sleep. The x and y arguments are a location (possibly very roughly) corresponding to the
* action that caused the device to go to sleep. For example, if the power button was pressed,
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
index 720e045..e431426 100644
--- a/core/java/android/app/activity_manager.aconfig
+++ b/core/java/android/app/activity_manager.aconfig
@@ -163,3 +163,10 @@
description: "Narrow the scope of Jank Perceptible"
bug: "304837972"
}
+
+flag {
+ name: "jank_perceptible_narrow_holdback"
+ namespace: "system_performance"
+ description: "Holdback study for jank_perceptible_narrow"
+ bug: "304837972"
+}
diff --git a/core/java/android/app/admin/StringSetIntersection.java b/core/java/android/app/admin/StringSetIntersection.java
new file mode 100644
index 0000000..5f2031e
--- /dev/null
+++ b/core/java/android/app/admin/StringSetIntersection.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2025 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.admin;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Set;
+
+/**
+ * Class to identify a intersection resolution mechanism for {@code Set<String>} policies, it's
+ * used to resolve the enforced policy when being set by multiple admins (see {@link
+ * PolicyState#getResolutionMechanism()}).
+ *
+ * @hide
+ */
+public final class StringSetIntersection extends ResolutionMechanism<Set<String>> {
+
+ /**
+ * Intersection resolution for policies represented {@code Set<String>} which resolves as the
+ * intersection of all sets.
+ */
+ @NonNull
+ public static final StringSetIntersection STRING_SET_INTERSECTION = new StringSetIntersection();
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ return o != null && getClass() == o.getClass();
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "StringSetIntersection {}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {}
+
+ @NonNull
+ public static final Parcelable.Creator<StringSetIntersection> CREATOR =
+ new Parcelable.Creator<StringSetIntersection>() {
+ @Override
+ public StringSetIntersection createFromParcel(Parcel source) {
+ return new StringSetIntersection();
+ }
+
+ @Override
+ public StringSetIntersection[] newArray(int size) {
+ return new StringSetIntersection[size];
+ }
+ };
+}
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 572bffe..b87ef70 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -412,3 +412,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "use_policy_intersection_for_permitted_input_methods"
+ namespace: "enterprise"
+ description: "When deciding on permitted input methods, use policy intersection instead of last recorded policy."
+ bug: "340914586"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/app/wallpaper.aconfig b/core/java/android/app/wallpaper.aconfig
index 7aba172..4a15a72 100644
--- a/core/java/android/app/wallpaper.aconfig
+++ b/core/java/android/app/wallpaper.aconfig
@@ -24,6 +24,16 @@
}
flag {
+ name: "notify_keyguard_events"
+ namespace: "systemui"
+ description: "Send keyguard showing/hiding/going-away events to wallpaper as wallpaper commands (guarded by permission)"
+ bug: "395897130"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "accurate_wallpaper_downsampling"
namespace: "systemui"
description: "Accurate downsampling of wallpaper bitmap for high resolution images"
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 53203eb..c5412a9 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -494,10 +494,14 @@
// TODO(b/142482943): Make this logic more specific and customizable. (canHaveProfile(userType))
/* @hide */
public boolean canHaveProfile() {
- if (isProfile() || isGuest() || isRestricted()) {
+ if (!isFull() || isProfile() || isGuest() || isRestricted() || isDemo()) {
return false;
}
- return isMain();
+ // NOTE: profiles used to be restricted just to the system user (and later to the main
+ // user), but from the framework point of view there is no need for such restriction, hence
+ // it's lifted
+ // TODO(b/374832167): check value of config_supportProfilesOnNonMainUser
+ return isMain() || android.multiuser.Flags.profilesForAll();
}
// TODO(b/142482943): Get rid of this (after removing it from all tests) if feasible.
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 3411a48..3dbd5b2 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -646,3 +646,10 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "profiles_for_all"
+ namespace: "multiuser"
+ description: "Allows any regular user to have profiles"
+ bug: "374832167"
+}
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index a7fbce5..7dc6afb 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -188,24 +188,6 @@
int BIOMETRIC_ERROR_CONTENT_VIEW_MORE_OPTIONS_BUTTON = 22;
/**
- * The error code returned after lock out error happens, the error dialog shows, and the users
- * dismisses the dialog. This is a placeholder that is currently only used by the support
- * library.
- *
- * @hide
- */
- int BIOMETRIC_ERROR_LOCKOUT_ERROR_DIALOG_DISMISSED = 23;
-
- /**
- * The error code returned after biometric hardware error happens, the error dialog shows, and
- * the users dismisses the dialog.This is a placeholder that is currently only used by the
- * support library.
- *
- * @hide
- */
- int BIOMETRIC_ERROR_BIOMETRIC_HARDWARE_ERROR_DIALOG_DISMISSED = 24;
-
- /**
* This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
* because the authentication attempt was unsuccessful.
* @hide
@@ -237,8 +219,6 @@
BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE,
BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS,
BIOMETRIC_ERROR_CONTENT_VIEW_MORE_OPTIONS_BUTTON,
- BIOMETRIC_ERROR_LOCKOUT_ERROR_DIALOG_DISMISSED,
- BIOMETRIC_ERROR_BIOMETRIC_HARDWARE_ERROR_DIALOG_DISMISSED,
BIOMETRIC_PAUSED_REJECTED})
@Retention(RetentionPolicy.SOURCE)
@interface Errors {}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 2e7bc6d..84d96bd 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -3504,6 +3504,10 @@
mInlineSuggestionSessionController.notifyOnStartInputView();
onStartInputView(mInputEditorInfo, restarting);
startExtractingText(true);
+ // Back callback is typically registered in {@link #showWindow()}, but it's possible
+ // for {@link #doStartInput()} to be called without {@link #showWindow()} so we also
+ // register here.
+ registerDefaultOnBackInvokedCallback();
} else if (mCandidatesVisibility == View.VISIBLE) {
if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
mCandidatesViewStarted = true;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ee62dea..6b1e918 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -149,6 +149,11 @@
private static volatile boolean sStackTrackingEnabled = false;
/**
+ * The extension binder object
+ */
+ private IBinder mExtension = null;
+
+ /**
* Enable Binder IPC stack tracking. If enabled, every binder transaction will be logged to
* {@link TransactionTracker}.
*
@@ -1237,7 +1242,9 @@
/** @hide */
@Override
- public final native @Nullable IBinder getExtension();
+ public final @Nullable IBinder getExtension() {
+ return mExtension;
+ }
/**
* Set the binder extension.
@@ -1245,7 +1252,12 @@
*
* @hide
*/
- public final native void setExtension(@Nullable IBinder extension);
+ public final void setExtension(@Nullable IBinder extension) {
+ mExtension = extension;
+ setExtensionNative(extension);
+ }
+
+ private final native void setExtensionNative(@Nullable IBinder extension);
/**
* Default implementation rewinds the parcels and calls onTransact. On
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 6cb49b3..4a99285 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -20,6 +20,7 @@
import static java.util.Objects.requireNonNull;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,6 +28,7 @@
import android.annotation.TestApi;
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Flags;
import android.ravenwood.annotation.RavenwoodClassLoadHook;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodReplace;
@@ -837,9 +839,8 @@
* @param buffer The ByteBuffer to write the data to.
* @throws ReadOnlyBufferException if the buffer is read-only.
* @throws BufferOverflowException if the buffer is too small.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_PARCEL_MARSHALL_BYTEBUFFER)
public final void marshall(@NonNull ByteBuffer buffer) {
if (buffer == null) {
throw new NullPointerException();
@@ -875,9 +876,8 @@
* Fills the raw bytes of this Parcel with data from the supplied buffer.
*
* @param buffer will read buffer.remaining() bytes from the buffer.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_PARCEL_MARSHALL_BYTEBUFFER)
public final void unmarshall(@NonNull ByteBuffer buffer) {
if (buffer == null) {
throw new NullPointerException();
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 86acb2b..0150d17 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -354,6 +354,15 @@
flag {
namespace: "system_performance"
+ name: "parcel_marshall_bytebuffer"
+ is_exported: true
+ description: "Parcel marshal/unmarshall APIs that use ByteBuffer."
+ is_fixed_read_only: true
+ bug: "401362825"
+}
+
+flag {
+ namespace: "system_performance"
name: "perfetto_sdk_tracing"
description: "Tracing using Perfetto SDK."
bug: "303199244"
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 2ed9c3a..8f7f941 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -16,6 +16,7 @@
package android.window;
+import static android.app.Instrumentation.DEBUG_START_ACTIVITY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
@@ -32,6 +33,7 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
+import android.app.Instrumentation;
import android.app.PendingIntent;
import android.app.WindowConfiguration;
import android.app.WindowConfiguration.WindowingMode;
@@ -45,6 +47,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
+import android.util.Log;
import android.view.InsetsFrameProvider;
import android.view.InsetsSource;
import android.view.SurfaceControl;
@@ -642,6 +645,10 @@
*/
@NonNull
public WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) {
+ if (DEBUG_START_ACTIVITY) {
+ Log.d(Instrumentation.TAG, "WCT.startTask: taskId=" + taskId
+ + " options=" + options, new Throwable());
+ }
mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options));
return this;
}
@@ -655,11 +662,15 @@
*/
@NonNull
public WindowContainerTransaction sendPendingIntent(@Nullable PendingIntent sender,
- @Nullable Intent intent, @Nullable Bundle options) {
+ @Nullable Intent fillInIntent, @Nullable Bundle options) {
+ if (DEBUG_START_ACTIVITY) {
+ Log.d(Instrumentation.TAG, "WCT.sendPendingIntent: sender=" + sender.getIntent()
+ + " fillInIntent=" + fillInIntent + " options=" + options, new Throwable());
+ }
mHierarchyOps.add(new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT)
.setLaunchOptions(options)
.setPendingIntent(sender)
- .setActivityIntent(intent)
+ .setActivityIntent(fillInIntent)
.build());
return this;
}
@@ -674,6 +685,10 @@
@NonNull
public WindowContainerTransaction startShortcut(@NonNull String callingPackage,
@NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options) {
+ if (DEBUG_START_ACTIVITY) {
+ Log.d(Instrumentation.TAG, "WCT.startShortcut: shortcutInfo=" + shortcutInfo
+ + " options=" + options, new Throwable());
+ }
mHierarchyOps.add(HierarchyOp.createForStartShortcut(
callingPackage, shortcutInfo, options));
return this;
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 8151429..f1c47a7 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -760,9 +760,20 @@
break;
}
- if (fragment.monotonicTimeMs >= startTimeMs && fragment != mActiveFragment) {
- containers.add(new BatteryHistoryParcelContainer(fragment));
+ if (fragment.monotonicTimeMs >= mHistoryBufferStartTime) {
+ // Do not include the backup of the current buffer, which is explicitly
+ // included later
+ continue;
}
+
+ if (i < fragments.size() - 1
+ && fragments.get(i + 1).monotonicTimeMs < startTimeMs) {
+ // Since fragments are ordered, an early start of next fragment implies an
+ // early end for this one.
+ continue;
+ }
+
+ containers.add(new BatteryHistoryParcelContainer(fragment));
}
}
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
index 3472d68..f6e2a4d 100644
--- a/core/java/com/android/internal/widget/NotificationProgressBar.java
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -78,6 +78,13 @@
@Nullable
private List<DrawablePart> mProgressDrawableParts = null;
+ /** @see R.styleable#NotificationProgressBar_segMinWidth */
+ private final float mSegMinWidth;
+ /** @see R.styleable#NotificationProgressBar_segSegGap */
+ private final float mSegSegGap;
+ /** @see R.styleable#NotificationProgressBar_segPointGap */
+ private final float mSegPointGap;
+
@Nullable
private Drawable mTracker = null;
private boolean mHasTrackerIcon = false;
@@ -128,6 +135,10 @@
Log.e(TAG, "Can't get NotificationProgressDrawable", ex);
}
+ mSegMinWidth = a.getDimension(R.styleable.NotificationProgressBar_segMinWidth, 0f);
+ mSegSegGap = a.getDimension(R.styleable.NotificationProgressBar_segSegGap, 0f);
+ mSegPointGap = a.getDimension(R.styleable.NotificationProgressBar_segPointGap, 0f);
+
// Supports setting the tracker in xml, but ProgressStyle notifications set/override it
// via {@code #setProgressTrackerIcon}.
final Drawable tracker = a.getDrawable(R.styleable.NotificationProgressBar_tracker);
@@ -444,30 +455,26 @@
return;
}
- final float segSegGap = mNotificationProgressDrawable.getSegSegGap();
- final float segPointGap = mNotificationProgressDrawable.getSegPointGap();
final float pointRadius = mNotificationProgressDrawable.getPointRadius();
mProgressDrawableParts = processPartsAndConvertToDrawableParts(
mParts,
width,
- segSegGap,
- segPointGap,
+ mSegSegGap,
+ mSegPointGap,
pointRadius,
mHasTrackerIcon,
mTrackerDrawWidth
);
- final float segmentMinWidth = mNotificationProgressDrawable.getSegmentMinWidth();
final float progressFraction = getProgressFraction();
final boolean isStyledByProgress = mProgressModel.isStyledByProgress();
- final float progressGap =
- mHasTrackerIcon ? 0F : mNotificationProgressDrawable.getSegSegGap();
+ final float progressGap = mHasTrackerIcon ? 0F : mSegSegGap;
Pair<List<DrawablePart>, Float> p = null;
try {
p = maybeStretchAndRescaleSegments(
mParts,
mProgressDrawableParts,
- segmentMinWidth,
+ mSegMinWidth,
pointRadius,
progressFraction,
isStyledByProgress,
@@ -492,11 +499,11 @@
mProgressModel.getProgress(),
getMax(),
width,
- segSegGap,
- segPointGap,
+ mSegSegGap,
+ mSegPointGap,
pointRadius,
mHasTrackerIcon,
- segmentMinWidth,
+ mSegMinWidth,
isStyledByProgress,
mTrackerDrawWidth);
} catch (NotEnoughWidthToFitAllPartsException ex) {
@@ -521,11 +528,11 @@
mProgressModel.getProgress(),
getMax(),
width,
- segSegGap,
- segPointGap,
+ mSegSegGap,
+ mSegPointGap,
pointRadius,
mHasTrackerIcon,
- segmentMinWidth,
+ mSegMinWidth,
isStyledByProgress,
mTrackerDrawWidth);
} catch (NotEnoughWidthToFitAllPartsException ex) {
diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
index b109610..32b283a 100644
--- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java
+++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
@@ -84,27 +84,6 @@
}
/**
- * Returns the gap between two segments.
- */
- public float getSegSegGap() {
- return mState.mSegSegGap;
- }
-
- /**
- * Returns the gap between a segment and a point.
- */
- public float getSegPointGap() {
- return mState.mSegPointGap;
- }
-
- /**
- * Returns the gap between a segment and a point.
- */
- public float getSegmentMinWidth() {
- return mState.mSegmentMinWidth;
- }
-
- /**
* Returns the radius for the points.
*/
public float getPointRadius() {
@@ -241,11 +220,6 @@
mState.setDensity(resolveDensity(r, 0));
- final TypedArray a = obtainAttributes(r, theme, attrs,
- R.styleable.NotificationProgressDrawable);
- updateStateFromTypedArray(a);
- a.recycle();
-
inflateChildElements(r, parser, attrs, theme);
updateLocalState();
@@ -262,13 +236,6 @@
state.setDensity(resolveDensity(t.getResources(), 0));
- if (state.mThemeAttrs != null) {
- final TypedArray a = t.resolveAttributes(
- state.mThemeAttrs, R.styleable.NotificationProgressDrawable);
- updateStateFromTypedArray(a);
- a.recycle();
- }
-
applyThemeChildElements(t);
updateLocalState();
@@ -279,21 +246,6 @@
return (mState.canApplyTheme()) || super.canApplyTheme();
}
- private void updateStateFromTypedArray(TypedArray a) {
- final State state = mState;
-
- // Account for any configuration changes.
- state.mChangingConfigurations |= a.getChangingConfigurations();
-
- // Extract the theme attributes, if any.
- state.mThemeAttrs = a.extractThemeAttrs();
-
- state.mSegSegGap = a.getDimension(R.styleable.NotificationProgressDrawable_segSegGap,
- state.mSegSegGap);
- state.mSegPointGap = a.getDimension(R.styleable.NotificationProgressDrawable_segPointGap,
- state.mSegPointGap);
- }
-
private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
TypedArray a;
@@ -357,8 +309,6 @@
// Extract the theme attributes, if any.
state.mThemeAttrsSegments = a.extractThemeAttrs();
- state.mSegmentMinWidth = a.getDimension(
- R.styleable.NotificationProgressDrawableSegments_minWidth, state.mSegmentMinWidth);
state.mSegmentHeight = a.getDimension(
R.styleable.NotificationProgressDrawableSegments_height, state.mSegmentHeight);
state.mFadedSegmentHeight = a.getDimension(
@@ -588,9 +538,6 @@
static final class State extends ConstantState {
@Config
int mChangingConfigurations;
- float mSegSegGap = 0.0f;
- float mSegPointGap = 0.0f;
- float mSegmentMinWidth = 0.0f;
float mSegmentHeight;
float mFadedSegmentHeight;
float mSegmentCornerRadius;
@@ -610,9 +557,6 @@
State(@NonNull State orig, @Nullable Resources res) {
mChangingConfigurations = orig.mChangingConfigurations;
- mSegSegGap = orig.mSegSegGap;
- mSegPointGap = orig.mSegPointGap;
- mSegmentMinWidth = orig.mSegmentMinWidth;
mSegmentHeight = orig.mSegmentHeight;
mFadedSegmentHeight = orig.mFadedSegmentHeight;
mSegmentCornerRadius = orig.mSegmentCornerRadius;
@@ -631,18 +575,6 @@
}
private void applyDensityScaling(int sourceDensity, int targetDensity) {
- if (mSegSegGap > 0) {
- mSegSegGap = scaleFromDensity(
- mSegSegGap, sourceDensity, targetDensity);
- }
- if (mSegPointGap > 0) {
- mSegPointGap = scaleFromDensity(
- mSegPointGap, sourceDensity, targetDensity);
- }
- if (mSegmentMinWidth > 0) {
- mSegmentMinWidth = scaleFromDensity(
- mSegmentMinWidth, sourceDensity, targetDensity);
- }
if (mSegmentHeight > 0) {
mSegmentHeight = scaleFromDensity(
mSegmentHeight, sourceDensity, targetDensity);
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index a0c8f30..36bda61 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -73,6 +73,7 @@
jmethodID mExecTransact;
jmethodID mGetInterfaceDescriptor;
jmethodID mTransactionCallback;
+ jmethodID mGetExtension;
// Object state.
jfieldID mObject;
@@ -488,8 +489,12 @@
if (mVintf) {
::android::internal::Stability::markVintf(b.get());
}
- if (mExtension != nullptr) {
- b.get()->setExtension(mExtension);
+ if (mSetExtensionCalled) {
+ jobject javaIBinderObject = env->CallObjectMethod(obj, gBinderOffsets.mGetExtension);
+ sp<IBinder> extensionFromJava = ibinderForJavaObject(env, javaIBinderObject);
+ if (extensionFromJava != nullptr) {
+ b.get()->setExtension(extensionFromJava);
+ }
}
mBinder = b;
ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%" PRId32 "\n",
@@ -515,21 +520,12 @@
mVintf = false;
}
- sp<IBinder> getExtension() {
- AutoMutex _l(mLock);
- sp<JavaBBinder> b = mBinder.promote();
- if (b != nullptr) {
- return b.get()->getExtension();
- }
- return mExtension;
- }
-
void setExtension(const sp<IBinder>& extension) {
AutoMutex _l(mLock);
- mExtension = extension;
+ mSetExtensionCalled = true;
sp<JavaBBinder> b = mBinder.promote();
if (b != nullptr) {
- b.get()->setExtension(mExtension);
+ b.get()->setExtension(extension);
}
}
@@ -541,8 +537,7 @@
// is too much binder state here, we can think about making JavaBBinder an
// sp here (avoid recreating it)
bool mVintf = false;
-
- sp<IBinder> mExtension;
+ bool mSetExtensionCalled = false;
};
// ----------------------------------------------------------------------------
@@ -1254,10 +1249,6 @@
return IPCThreadState::self()->blockUntilThreadAvailable();
}
-static jobject android_os_Binder_getExtension(JNIEnv* env, jobject obj) {
- JavaBBinderHolder* jbh = (JavaBBinderHolder*) env->GetLongField(obj, gBinderOffsets.mObject);
- return javaObjectForIBinder(env, jbh->getExtension());
-}
static void android_os_Binder_setExtension(JNIEnv* env, jobject obj, jobject extensionObject) {
JavaBBinderHolder* jbh = (JavaBBinderHolder*) env->GetLongField(obj, gBinderOffsets.mObject);
@@ -1300,8 +1291,7 @@
{ "getNativeBBinderHolder", "()J", (void*)android_os_Binder_getNativeBBinderHolder },
{ "getNativeFinalizer", "()J", (void*)android_os_Binder_getNativeFinalizer },
{ "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable },
- { "getExtension", "()Landroid/os/IBinder;", (void*)android_os_Binder_getExtension },
- { "setExtension", "(Landroid/os/IBinder;)V", (void*)android_os_Binder_setExtension },
+ { "setExtensionNative", "(Landroid/os/IBinder;)V", (void*)android_os_Binder_setExtension },
};
// clang-format on
@@ -1318,6 +1308,8 @@
gBinderOffsets.mTransactionCallback =
GetStaticMethodIDOrDie(env, clazz, "transactionCallback", "(IIII)V");
gBinderOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
+ gBinderOffsets.mGetExtension = GetMethodIDOrDie(env, clazz, "getExtension",
+ "()Landroid/os/IBinder;");
return RegisterMethodsOrDie(
env, kBinderPathName,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e16ce98..9e02004 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5394,13 +5394,13 @@
corresponding permission such as {@link #HEAD_TRACKING} or
{@link #FACE_TRACKING} for the data being accessed.
- <p>Protection level: normal|appop
+ <p>Protection level: signature|privileged
@SystemApi
@FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES)
@hide -->
<permission android:name="android.permission.XR_TRACKING_IN_BACKGROUND"
- android:protectionLevel="normal|appop"
+ android:protectionLevel="signature|privileged"
android:description="@string/permdesc_xr_tracking_in_background"
android:label="@string/permlab_xr_tracking_in_background"
android:featureFlag="android.xr.xr_manifest_entries" />
diff --git a/core/res/res/drawable/notification_progress.xml b/core/res/res/drawable/notification_progress.xml
index ff5450e..92a0a6a 100644
--- a/core/res/res/drawable/notification_progress.xml
+++ b/core/res/res/drawable/notification_progress.xml
@@ -19,12 +19,9 @@
android:gravity="center_vertical|fill_horizontal">
<com.android.internal.widget.NotificationProgressDrawable
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:segSegGap="@dimen/notification_progress_segSeg_gap"
- android:segPointGap="@dimen/notification_progress_segPoint_gap">
+ android:layout_height="wrap_content">
<segments
android:color="?attr/colorProgressBackgroundNormal"
- android:minWidth="@dimen/notification_progress_segments_min_width"
android:height="@dimen/notification_progress_segments_height"
android:fadedHeight="@dimen/notification_progress_segments_faded_height"
android:cornerRadius="@dimen/notification_progress_segments_corner_radius"/>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d2c993ae..647e3dc 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5573,6 +5573,14 @@
<!-- @hide internal use only -->
<declare-styleable name="NotificationProgressBar">
+ <!-- Minimum required drawing width for segments. The drawing width refers to the width
+ after the original segments have been adjusted for the neighboring Points and gaps.
+ This is enforced by stretching the segments that are too short. -->
+ <attr name="segMinWidth" format="dimension" />
+ <!-- The gap between two segments. -->
+ <attr name="segSegGap" format="dimension" />
+ <!-- The gap between a segment and a point. -->
+ <attr name="segPointGap" format="dimension" />
<!-- Draws the tracker on a NotificationProgressBar. -->
<attr name="tracker" format="reference" />
<!-- Height of the tracker. -->
@@ -7580,25 +7588,9 @@
<!-- NotificationProgressDrawable class -->
<!-- ================================== -->
- <!-- Drawable used to render a notification progress bar, with segments and points. -->
- <!-- @hide internal use only -->
- <declare-styleable name="NotificationProgressDrawable">
- <!-- The gap between two segments. -->
- <attr name="segSegGap" format="dimension" />
- <!-- The gap between a segment and a point. -->
- <attr name="segPointGap" format="dimension" />
- </declare-styleable>
-
<!-- Used to config the segments of a NotificationProgressDrawable. -->
<!-- @hide internal use only -->
<declare-styleable name="NotificationProgressDrawableSegments">
- <!-- TODO: b/390196782 - maybe move this to NotificationProgressBar, because that's the only
- place this is used actually. Same for NotificationProgressDrawable.segSegGap/segPointGap
- above. -->
- <!-- Minimum required drawing width. The drawing width refers to the width after
- the original segments have been adjusted for the neighboring Points and gaps. This is
- enforced by stretching the segments that are too short. -->
- <attr name="minWidth" />
<!-- Height of the solid segments. -->
<attr name="height" />
<!-- Height of the faded segments. -->
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 73681d2..8f13ee1 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -503,6 +503,9 @@
<style name="Widget.Material.Notification.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
<style name="Widget.Material.Notification.NotificationProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal">
+ <item name="segMinWidth">@dimen/notification_progress_segments_min_width</item>
+ <item name="segSegGap">@dimen/notification_progress_segSeg_gap</item>
+ <item name="segPointGap">@dimen/notification_progress_segPoint_gap</item>
<item name="progressDrawable">@drawable/notification_progress</item>
<item name="trackerHeight">@dimen/notification_progress_tracker_height</item>
</style>
diff --git a/core/tests/coretests/src/android/content/pm/UserInfoTest.java b/core/tests/coretests/src/android/content/pm/UserInfoTest.java
index edeea6d..c84c215 100644
--- a/core/tests/coretests/src/android/content/pm/UserInfoTest.java
+++ b/core/tests/coretests/src/android/content/pm/UserInfoTest.java
@@ -16,19 +16,44 @@
package android.content.pm;
+import static android.content.pm.UserInfo.FLAG_DEMO;
+import static android.content.pm.UserInfo.FLAG_FULL;
+import static android.content.pm.UserInfo.FLAG_GUEST;
+import static android.content.pm.UserInfo.FLAG_MAIN;
+import static android.content.pm.UserInfo.FLAG_PROFILE;
+import static android.content.pm.UserInfo.FLAG_SYSTEM;
+import static android.os.UserManager.USER_TYPE_FULL_RESTRICTED;
+import static android.os.UserManager.USER_TYPE_FULL_SYSTEM;
+import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
+
import static com.google.common.truth.Truth.assertThat;
+import android.content.pm.UserInfo.UserInfoFlag;
import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.google.common.truth.Expect;
+
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class UserInfoTest {
+public final class UserInfoTest {
+
+ @Rule
+ public final SetFlagsRule flags =
+ new SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
+
+ @Rule public final Expect expect = Expect.create();
+
@Test
public void testSimple() throws Exception {
final UserInfo ui = new UserInfo(10, "Test", UserInfo.FLAG_GUEST);
@@ -56,9 +81,6 @@
assertThat(ui.isInitialized()).isEqualTo(false);
assertThat(ui.isFull()).isEqualTo(false);
assertThat(ui.isMain()).isEqualTo(false);
-
- // Derived dynamically
- assertThat(ui.canHaveProfile()).isEqualTo(false);
}
@Test
@@ -68,4 +90,64 @@
assertThat(ui.toString()).isNotEmpty();
assertThat(ui.toFullString()).isNotEmpty();
}
+
+ @Test
+ @DisableFlags(android.multiuser.Flags.FLAG_PROFILES_FOR_ALL)
+ public void testCanHaveProfile_flagProfilesForAllDisabled() {
+ expectCannotHaveProfile("non-full user", createTestUserInfo(/* flags= */ 0));
+ expectCannotHaveProfile("guest user", createTestUserInfo(FLAG_FULL | FLAG_GUEST));
+ expectCanHaveProfile("main user", createTestUserInfo(FLAG_FULL | FLAG_MAIN));
+ expectCannotHaveProfile("non-main user", createTestUserInfo(FLAG_FULL));
+ expectCannotHaveProfile("demo user", createTestUserInfo(FLAG_FULL | FLAG_DEMO));
+ expectCannotHaveProfile("restricted user",
+ createTestUserInfo(USER_TYPE_FULL_RESTRICTED, FLAG_FULL));
+ expectCannotHaveProfile("profile user", createTestUserInfo(FLAG_PROFILE));
+ expectCanHaveProfile("(full) system user that's also main user",
+ createTestUserInfo(USER_TYPE_FULL_SYSTEM, FLAG_FULL | FLAG_SYSTEM | FLAG_MAIN));
+ expectCannotHaveProfile("headless system user that's not main user",
+ createTestUserInfo(USER_TYPE_SYSTEM_HEADLESS, FLAG_SYSTEM));
+ }
+
+ @Test
+ @EnableFlags(android.multiuser.Flags.FLAG_PROFILES_FOR_ALL)
+ public void testCanHaveProfile_flagProfilesForAllEnabled() {
+ expectCannotHaveProfile("non-full user", createTestUserInfo(/* flags= */ 0));
+ expectCannotHaveProfile("guest user", createTestUserInfo(FLAG_FULL | FLAG_GUEST));
+ expectCanHaveProfile("main user", createTestUserInfo(FLAG_FULL | FLAG_MAIN));
+ expectCanHaveProfile("non-main user", createTestUserInfo(FLAG_FULL));
+ expectCannotHaveProfile("demo user", createTestUserInfo(FLAG_FULL | FLAG_DEMO));
+ expectCannotHaveProfile("restricted user",
+ createTestUserInfo(USER_TYPE_FULL_RESTRICTED, FLAG_FULL));
+ expectCannotHaveProfile("profile user", createTestUserInfo(FLAG_PROFILE));
+ expectCanHaveProfile("(full) system user that's also main user",
+ createTestUserInfo(USER_TYPE_FULL_SYSTEM, FLAG_FULL | FLAG_SYSTEM | FLAG_MAIN));
+ expectCannotHaveProfile("headless system user that's not main user",
+ createTestUserInfo(USER_TYPE_SYSTEM_HEADLESS, FLAG_SYSTEM));
+ }
+
+ /**
+ * Creates a new {@link UserInfo} with id {@code 10}, name {@code Test}, and the given
+ * {@code flags}.
+ */
+ private UserInfo createTestUserInfo(@UserInfoFlag int flags) {
+ return new UserInfo(10, "Test", flags);
+ }
+
+ /**
+ * Creates a new {@link UserInfo} with id {@code 10}, name {@code Test}, and the given
+ * {@code userType} and {@code flags}.
+ */
+ private UserInfo createTestUserInfo(String userType, @UserInfoFlag int flags) {
+ return new UserInfo(10, "Test", /* iconPath= */ null, flags, userType);
+ }
+
+ private void expectCanHaveProfile(String description, UserInfo user) {
+ expect.withMessage("canHaveProfile() on %s (%s)", description, user)
+ .that(user.canHaveProfile()).isTrue();
+ }
+
+ private void expectCannotHaveProfile(String description, UserInfo user) {
+ expect.withMessage("canHaveProfile() on %s (%s)", description, user)
+ .that(user.canHaveProfile()).isFalse();
+ }
}
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index bb05910..3e652010 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -29,8 +29,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import java.nio.BufferOverflowException;
-import java.nio.ByteBuffer;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -418,63 +416,4 @@
int binderEndPos = pA.dataPosition();
assertTrue(pA.hasBinders(binderStartPos, binderEndPos - binderStartPos));
}
-
- private static final byte[] TEST_DATA = new byte[] {4, 8, 15, 16, 23, 42};
-
- // Allow for some Parcel overhead
- private static final int TEST_DATA_LENGTH = TEST_DATA.length + 100;
-
- @Test
- public void testMarshall_ByteBuffer_wrapped() {
- ByteBuffer bb = ByteBuffer.allocate(TEST_DATA_LENGTH);
- testMarshall_ByteBuffer(bb);
- }
-
- @Test
- public void testMarshall_DirectByteBuffer() {
- ByteBuffer bb = ByteBuffer.allocateDirect(TEST_DATA_LENGTH);
- testMarshall_ByteBuffer(bb);
- }
-
- private void testMarshall_ByteBuffer(ByteBuffer bb) {
- // Ensure that Parcel respects the starting offset by not starting at 0
- bb.position(1);
- bb.mark();
-
- // Parcel test data, then marshall into the ByteBuffer
- Parcel p1 = Parcel.obtain();
- p1.writeByteArray(TEST_DATA);
- p1.marshall(bb);
- p1.recycle();
-
- assertTrue(bb.position() > 1);
- bb.reset();
-
- // Unmarshall test data into a new Parcel
- Parcel p2 = Parcel.obtain();
- bb.reset();
- p2.unmarshall(bb);
- assertTrue(bb.position() > 1);
- p2.setDataPosition(0);
- byte[] marshalled = p2.marshall();
-
- bb.reset();
- for (int i = 0; i < TEST_DATA.length; i++) {
- assertEquals(bb.get(), marshalled[i]);
- }
-
- byte[] testDataCopy = new byte[TEST_DATA.length];
- p2.setDataPosition(0);
- p2.readByteArray(testDataCopy);
- for (int i = 0; i < TEST_DATA.length; i++) {
- assertEquals(TEST_DATA[i], testDataCopy[i]);
- }
-
- // Test that overflowing the buffer throws an exception
- bb.reset();
- // Leave certainly not enough room for the test data
- bb.limit(bb.position() + TEST_DATA.length - 1);
- assertThrows(BufferOverflowException.class, () -> p2.marshall(bb));
- p2.recycle();
- }
}
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index b6a1501..19455a3 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -211,3 +211,10 @@
description: "Makes the split divider snap 'magnetically' to available snap points during drag"
bug: "383631946"
}
+
+flag {
+ name: "enable_dynamic_insets_for_app_launch"
+ namespace: "multitasking"
+ description: "Enables dynamic insets for app launch so the window is properly cropped"
+ bug: "336511494"
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
index 14c152102..90011f4 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
@@ -24,6 +24,9 @@
import android.graphics.Rect
import android.os.Handler
import android.os.UserManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.view.IWindowManager
import android.view.MotionEvent
import android.view.View
@@ -36,6 +39,7 @@
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.ProtoLog
import com.android.internal.statusbar.IStatusBarService
+import com.android.wm.shell.Flags
import com.android.wm.shell.R
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.bubbles.Bubble
@@ -64,6 +68,10 @@
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.bubbles.DeviceConfig
+import com.android.wm.shell.shared.bubbles.DragZone
+import com.android.wm.shell.shared.bubbles.DragZoneFactory
+import com.android.wm.shell.shared.bubbles.DragZoneFactory.SplitScreenModeChecker.SplitScreenMode
+import com.android.wm.shell.shared.bubbles.DraggedObject
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
@@ -88,6 +96,8 @@
const val SCREEN_HEIGHT = 1000
}
+ @get:Rule val setFlagsRule = SetFlagsRule()
+
@get:Rule val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this)
private val context = ApplicationProvider.getApplicationContext<Context>()
@@ -101,6 +111,7 @@
private lateinit var bgExecutor: TestShellExecutor
private lateinit var bubbleLogger: BubbleLogger
private lateinit var testBubblesList: MutableList<Bubble>
+ private lateinit var dragZoneFactory: DragZoneFactory
@Before
fun setUp() {
@@ -134,6 +145,10 @@
whenever(bubbleData.bubbles).thenReturn(testBubblesList)
whenever(bubbleData.hasBubbles()).thenReturn(!testBubblesList.isEmpty())
+ dragZoneFactory = DragZoneFactory(context, deviceConfig,
+ { SplitScreenMode.UNSUPPORTED },
+ { false })
+
bubbleController =
createBubbleController(
bubbleData,
@@ -280,6 +295,7 @@
assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
}
+ @DisableFlags(Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_BUBBLE_ANYTHING)
@Test
fun testEventLogging_dragExpandedViewLeft() {
val bubble = createBubble("first")
@@ -287,7 +303,7 @@
getInstrumentation().runOnMainSync {
bubbleBarLayerView.showExpandedView(bubble)
- bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true)
+ bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true /* visible */)
}
waitForExpandedViewAnimation()
@@ -305,6 +321,7 @@
assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
}
+ @DisableFlags(Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_BUBBLE_ANYTHING)
@Test
fun testEventLogging_dragExpandedViewRight() {
val bubble = createBubble("first")
@@ -312,7 +329,7 @@
getInstrumentation().runOnMainSync {
bubbleBarLayerView.showExpandedView(bubble)
- bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true)
+ bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true /* visible */)
}
waitForExpandedViewAnimation()
@@ -330,6 +347,76 @@
assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
}
+ @EnableFlags(Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE)
+ @Test
+ fun testEventLogging_dragExpandedViewLeft_bubbleAnything() {
+ val bubble = createBubble("first")
+ bubblePositioner.bubbleBarLocation = BubbleBarLocation.RIGHT
+
+ getInstrumentation().runOnMainSync {
+ bubbleBarLayerView.showExpandedView(bubble)
+ bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true /* visible */)
+ }
+ waitForExpandedViewAnimation()
+
+ val handleView = bubbleBarLayerView.findViewById<View>(R.id.bubble_bar_handle_view)
+ assertThat(handleView).isNotNull()
+
+ val dragZones = dragZoneFactory.createSortedDragZones(
+ DraggedObject.ExpandedView(BubbleBarLocation.RIGHT))
+ val rightDragZone = dragZones.filterIsInstance<DragZone.Bubble.Right>().first()
+ val rightPoint = PointF(rightDragZone.bounds.centerX().toFloat(),
+ rightDragZone.bounds.centerY().toFloat())
+ val leftDragZone = dragZones.filterIsInstance<DragZone.Bubble.Left>().first()
+ val leftPoint = PointF(leftDragZone.bounds.centerX().toFloat(),
+ leftDragZone.bounds.centerY().toFloat())
+
+ // Drag from right to left
+ handleView.dispatchTouchEvent(0L, MotionEvent.ACTION_DOWN, rightPoint)
+ handleView.dispatchTouchEvent(10L, MotionEvent.ACTION_MOVE, leftPoint)
+ handleView.dispatchTouchEvent(20L, MotionEvent.ACTION_UP, leftPoint)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.logs[0].eventId)
+ .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_EXP_VIEW.id)
+ assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE)
+ @Test
+ fun testEventLogging_dragExpandedViewRight_bubbleAnything() {
+ val bubble = createBubble("first")
+ bubblePositioner.bubbleBarLocation = BubbleBarLocation.LEFT
+
+ getInstrumentation().runOnMainSync {
+ bubbleBarLayerView.showExpandedView(bubble)
+ bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true /* visible */)
+ }
+ waitForExpandedViewAnimation()
+
+ val handleView = bubbleBarLayerView.findViewById<View>(R.id.bubble_bar_handle_view)
+ assertThat(handleView).isNotNull()
+
+ val dragZones = dragZoneFactory.createSortedDragZones(
+ DraggedObject.ExpandedView(BubbleBarLocation.LEFT))
+ val rightDragZone = dragZones.filterIsInstance<DragZone.Bubble.Right>().first()
+ val rightPoint = PointF(rightDragZone.bounds.centerX().toFloat(),
+ rightDragZone.bounds.centerY().toFloat())
+ val leftDragZone = dragZones.filterIsInstance<DragZone.Bubble.Left>().first()
+ val leftPoint = PointF(leftDragZone.bounds.centerX().toFloat(),
+ leftDragZone.bounds.centerY().toFloat())
+
+ // Drag from left to right
+ handleView.dispatchTouchEvent(0L, MotionEvent.ACTION_DOWN, leftPoint)
+ handleView.dispatchTouchEvent(10L, MotionEvent.ACTION_MOVE, rightPoint)
+ handleView.dispatchTouchEvent(20L, MotionEvent.ACTION_UP, rightPoint)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.logs[0].eventId)
+ .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_EXP_VIEW.id)
+ assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
+ }
+
@Test
fun testUpdateExpandedView_updateLocation() {
bubblePositioner.bubbleBarLocation = BubbleBarLocation.RIGHT
@@ -385,7 +472,7 @@
bubbleLogger,
)
// Mark visible so we don't wait for task view before animations can start
- bubbleBarExpandedView.onContentVisibilityChanged(true)
+ bubbleBarExpandedView.onContentVisibilityChanged(true /* visible */)
val viewInfo = FakeBubbleFactory.createViewInfo(bubbleBarExpandedView)
return FakeBubbleFactory.createChatBubble(context, key, viewInfo).also {
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
index 9bb51a8..ef30d89 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
@@ -34,6 +34,7 @@
android:src="@drawable/bubble_ic_settings"/>
<TextView
+ android:id="@+id/education_manage_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/bubble_popup_text_margin"
@@ -45,6 +46,7 @@
android:text="@string/bubble_bar_education_manage_title"/>
<TextView
+ android:id="@+id/education_manage_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/bubble_popup_text_margin"
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
index 1616707..9076d6a 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
@@ -34,6 +34,7 @@
android:src="@drawable/ic_floating_landscape"/>
<TextView
+ android:id="@+id/education_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/bubble_popup_text_margin"
@@ -45,6 +46,7 @@
android:text="@string/bubble_bar_education_stack_title"/>
<TextView
+ android:id="@+id/education_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/bubble_popup_text_margin"
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index 225303b..17ebac9 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -40,6 +40,7 @@
android:tint="@color/bubbles_icon_tint"/>
<TextView
+ android:id="@+id/manage_dismiss"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
@@ -67,6 +68,7 @@
android:tint="@color/bubbles_icon_tint"/>
<TextView
+ android:id="@+id/manage_dont_bubble"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 426c3ee..290ef16 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -27,6 +27,7 @@
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
+import static com.android.wm.shell.shared.TypefaceUtils.setTypeface;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
@@ -71,6 +72,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.common.AlphaOptimizedButton;
import com.android.wm.shell.shared.TriangleShape;
+import com.android.wm.shell.shared.TypefaceUtils;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.taskview.TaskView;
@@ -551,6 +553,7 @@
mManageButton = (AlphaOptimizedButton) LayoutInflater.from(ctw).inflate(
R.layout.bubble_manage_button, this /* parent */, false /* attach */);
addView(mManageButton);
+ setTypeface(mManageButton, TypefaceUtils.FontFamily.GSF_LABEL_LARGE);
mManageButton.setVisibility(visibility);
setManageClickListener();
post(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index da6948d..92007a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -50,6 +50,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.shared.TriangleShape;
+import com.android.wm.shell.shared.TypefaceUtils;
/**
* Flyout view that appears as a 'chat bubble' alongside the bubble stack. The flyout can visually
@@ -165,8 +166,10 @@
LayoutInflater.from(context).inflate(R.layout.bubble_flyout, this, true);
mFlyoutTextContainer = findViewById(R.id.bubble_flyout_text_container);
mSenderText = findViewById(R.id.bubble_flyout_name);
+ TypefaceUtils.setTypeface(mSenderText, TypefaceUtils.FontFamily.GSF_LABEL_LARGE);
mSenderAvatar = findViewById(R.id.bubble_flyout_avatar);
mMessageText = mFlyoutTextContainer.findViewById(R.id.bubble_flyout_text);
+ TypefaceUtils.setTypeface(mMessageText, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM);
final Resources res = getResources();
mFlyoutPadding = res.getDimensionPixelSize(R.dimen.bubble_flyout_padding_x);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index 64f54b8..e901e0c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -46,6 +46,7 @@
import com.android.internal.util.ContrastColorUtil;
import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
+import com.android.wm.shell.shared.TypefaceUtils;
import java.util.ArrayList;
import java.util.List;
@@ -234,6 +235,10 @@
setBackgroundColor(bgColor);
mEmptyStateTitle.setTextColor(textColor);
mEmptyStateSubtitle.setTextColor(textColor);
+ TypefaceUtils.setTypeface(mEmptyStateTitle,
+ TypefaceUtils.FontFamily.GSF_BODY_MEDIUM_EMPHASIZED);
+ TypefaceUtils.setTypeface(mEmptyStateSubtitle, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM);
+
}
public void updateFontSize() {
@@ -322,6 +327,7 @@
TextView viewName = overflowView.findViewById(R.id.bubble_view_name);
viewName.setTextColor(textColor);
+ TypefaceUtils.setTypeface(viewName, TypefaceUtils.FontFamily.GSF_LABEL_LARGE);
return new ViewHolder(overflowView, mPositioner);
}
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 dd5a23a..3dce456 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
@@ -89,6 +89,8 @@
import com.android.wm.shell.bubbles.animation.StackAnimationController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
@@ -1397,6 +1399,14 @@
// The menu itself should respect locale direction so the icons are on the correct side.
mManageMenu.setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
addView(mManageMenu);
+
+ // Doesn't seem to work unless view is added; so set font after.
+ TypefaceUtils.setTypeface(findViewById(R.id.manage_dismiss), FontFamily.GSF_LABEL_LARGE);
+ TypefaceUtils.setTypeface(findViewById(R.id.manage_dont_bubble),
+ FontFamily.GSF_LABEL_LARGE);
+ TypefaceUtils.setTypeface(mManageSettingsText, FontFamily.GSF_LABEL_LARGE);
+ TypefaceUtils.setTypeface(findViewById(R.id.bubble_manage_menu_fullscreen_title),
+ FontFamily.GSF_LABEL_LARGE);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
index 39a2a7b..d2ad708 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
@@ -27,6 +27,7 @@
import android.widget.LinearLayout
import com.android.internal.R.color.system_neutral1_900
import com.android.wm.shell.R
+import com.android.wm.shell.shared.TypefaceUtils
import com.android.wm.shell.shared.animation.Interpolators
/**
@@ -53,6 +54,12 @@
init {
LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this)
+ TypefaceUtils.setTypeface(findViewById(R.id.user_education_title),
+ TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+ TypefaceUtils.setTypeface(findViewById(R.id.user_education_description),
+ TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
+ TypefaceUtils.setTypeface(manageButton, TypefaceUtils.FontFamily.GSF_LABEL_LARGE_EMPHASIZED)
+ TypefaceUtils.setTypeface(gotItButton, TypefaceUtils.FontFamily.GSF_LABEL_LARGE_EMPHASIZED)
visibility = View.GONE
elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index 1660619..9ac05989 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -26,6 +26,7 @@
import android.widget.TextView
import com.android.internal.util.ContrastColorUtil
import com.android.wm.shell.R
+import com.android.wm.shell.shared.TypefaceUtils
import com.android.wm.shell.shared.animation.Interpolators
/**
@@ -59,6 +60,9 @@
init {
LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this)
+ TypefaceUtils.setTypeface(titleTextView,
+ TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+ TypefaceUtils.setTypeface(descTextView, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
visibility = View.GONE
elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index 9d4f904..3543556 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -203,7 +203,11 @@
draggedObject: MagnetizedObject<*>,
) {
dragListener.onReleased(inDismiss = true)
- pinController.onDragEnd()
+ if (dropTargetManager != null) {
+ dropTargetManager.onDragEnded()
+ } else {
+ pinController.onDragEnd()
+ }
dismissView.hide()
}
}
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 3997412..2cc9387 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
@@ -147,15 +147,23 @@
Log.w(TAG, "dropped invalid bubble: " + mExpandedBubble);
return;
}
+
+ final boolean isBubbleLeft = zone instanceof DragZone.Bubble.Left;
+ final boolean isBubbleRight = zone instanceof DragZone.Bubble.Right;
+ if (!isBubbleLeft && !isBubbleRight) {
+ // If we didn't finish the "change" animation make sure to animate
+ // it back to the right spot
+ locationChangeListener.onChange(mInitialLocation);
+ }
if (zone instanceof DragZone.FullScreen) {
((Bubble) mExpandedBubble).getTaskView().moveToFullscreen();
// Make sure location change listener is updated with the initial
// location -- even if we "switched sides" during the drag, since
// we've ended up in fullscreen, the location shouldn't change.
locationChangeListener.onRelease(mInitialLocation);
- } else if (zone instanceof DragZone.Bubble.Left) {
+ } else if (isBubbleLeft) {
locationChangeListener.onRelease(BubbleBarLocation.LEFT);
- } else if (zone instanceof DragZone.Bubble.Right) {
+ } else if (isBubbleRight) {
locationChangeListener.onRelease(BubbleBarLocation.RIGHT);
}
}
@@ -189,7 +197,7 @@
@NonNull
@Override
public SplitScreenMode getSplitScreenMode() {
- return SplitScreenMode.NONE;
+ return SplitScreenMode.UNSUPPORTED;
}
},
new DragZoneFactory.DesktopWindowModeChecker() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
index 6c14d83..bccc6dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
@@ -25,6 +25,7 @@
import android.widget.TextView;
import com.android.wm.shell.R;
+import com.android.wm.shell.shared.TypefaceUtils;
/**
* Bubble bar expanded view menu item view to display menu action details
@@ -55,6 +56,7 @@
super.onFinishInflate();
mImageView = findViewById(R.id.bubble_bar_menu_item_icon);
mTextView = findViewById(R.id.bubble_bar_menu_item_title);
+ TypefaceUtils.setTypeface(mTextView, TypefaceUtils.FontFamily.GSF_TITLE_MEDIUM);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
index dfbf655..7c0f8e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
@@ -33,6 +33,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
+import com.android.wm.shell.shared.TypefaceUtils;
import java.util.ArrayList;
@@ -75,6 +76,7 @@
mActionsSectionView = findViewById(R.id.bubble_bar_manage_menu_actions_section);
mBubbleIconView = findViewById(R.id.bubble_bar_manage_menu_bubble_icon);
mBubbleTitleView = findViewById(R.id.bubble_bar_manage_menu_bubble_title);
+ TypefaceUtils.setTypeface(mBubbleTitleView, TypefaceUtils.FontFamily.GSF_TITLE_MEDIUM);
mBubbleDismissIconView = findViewById(R.id.bubble_bar_manage_menu_dismiss_icon);
updateThemeColors();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
index 7adec39..0bd3a54 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -35,6 +35,7 @@
import com.android.wm.shell.bubbles.BubbleEducationController
import com.android.wm.shell.bubbles.BubbleViewProvider
import com.android.wm.shell.bubbles.setup
+import com.android.wm.shell.shared.TypefaceUtils
import com.android.wm.shell.shared.animation.PhysicsAnimator
import com.android.wm.shell.shared.bubbles.BubblePopupDrawable
import com.android.wm.shell.shared.bubbles.BubblePopupView
@@ -108,6 +109,10 @@
root.getBoundsOnScreen(rootBounds)
educationView =
createEducationView(R.layout.bubble_bar_stack_education, root).apply {
+ TypefaceUtils.setTypeface(findViewById(R.id.education_title),
+ TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+ TypefaceUtils.setTypeface(findViewById(R.id.education_text),
+ TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
setArrowDirection(BubblePopupDrawable.ArrowDirection.DOWN)
updateEducationPosition(view = this, position, rootBounds)
val arrowToEdgeOffset = popupDrawable?.config?.cornerRadius ?: 0f
@@ -153,6 +158,10 @@
educationView =
createEducationView(R.layout.bubble_bar_manage_education, root).apply {
+ TypefaceUtils.setTypeface(findViewById(R.id.education_manage_title),
+ TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+ TypefaceUtils.setTypeface(findViewById(R.id.education_manage_text),
+ TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
pivotY = 0f
doOnLayout { it.pivotX = it.width / 2f }
setOnClickListener { hideEducation(animated = true) }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
index dd5827a..320de2a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
@@ -142,8 +142,8 @@
## Tracing activity starts & finishes in the app process
It's sometimes useful to know when to see a stack trace of when an activity starts in the app code
-(ie. if you are repro'ing a bug related to activity starts). You can enable this system property to
-get this trace:
+or via a `WindowContainerTransaction` (ie. if you are repro'ing a bug related to activity starts).
+You can enable this system property to get this trace:
```shell
# Enabling
adb shell setprop persist.wm.debug.start_activity true
@@ -168,6 +168,21 @@
adb reboot
```
+## Tracing transition requests in the Shell
+
+To trace where a new WM transition is started in the Shell, you can enable this system property:
+```shell
+# Enabling
+adb shell setprop persist.wm.debug.start_shell_transition true
+adb reboot
+adb logcat -s "ShellTransitions"
+
+# Disabling
+adb shell setprop persist.wm.debug.start_shell_transition \"\"
+adb reboot
+```
+
+
## Dumps
Because the Shell library is built as a part of SystemUI, dumping the state is currently done as a
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index cef18f5..c58bb6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -40,7 +40,6 @@
import android.view.WindowManager;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
-import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
@@ -341,23 +340,6 @@
return false;
}
- /**
- * @return a change representing a config-at-end activity for a given parent.
- */
- @Nullable
- public TransitionInfo.Change getDeferConfigActivityChange(TransitionInfo info,
- @android.annotation.NonNull WindowContainerToken parent) {
- for (TransitionInfo.Change change : info.getChanges()) {
- if (change.getTaskInfo() == null
- && change.hasFlags(TransitionInfo.FLAG_CONFIG_AT_END)
- && change.getParent() != null && change.getParent().equals(parent)) {
- return change;
- }
- }
- return null;
- }
-
-
/** Whether a particular package is same as current pip package. */
public boolean isPackageActiveInPip(@Nullable String packageName) {
// No-op, to be handled differently in PIP1 and PIP2
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index 880e143..92f36d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -43,6 +43,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.StringJoiner;
/**
* A Task Listener implementation used only for CUJs and trigger paths that cannot be initiated via
@@ -114,6 +115,17 @@
// Set the new params but make sure mPictureInPictureParams is not null.
mPictureInPictureParams = params == null
? new PictureInPictureParams.Builder().build() : params;
+ logRemoteActions(mPictureInPictureParams);
+ }
+
+ private void logRemoteActions(@android.annotation.NonNull PictureInPictureParams params) {
+ StringJoiner sj = new StringJoiner("|", "[", "]");
+ if (params.hasSetActions()) {
+ params.getActions().forEach((action) -> sj.add(action.getTitle()));
+ }
+
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "PIP remote actions=%s", sj.toString());
}
/** Add a PipParamsChangedCallback listener. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index cfcd563..5d8d8b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -76,6 +76,7 @@
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
import com.android.wm.shell.pip2.animation.PipEnterAnimator;
import com.android.wm.shell.pip2.phone.transition.PipExpandHandler;
+import com.android.wm.shell.pip2.phone.transition.PipTransitionUtils;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellInit;
@@ -387,8 +388,8 @@
mFinishCallback = finishCallback;
// We expect the PiP activity as a separate change in a config-at-end transition;
// only flings are not using config-at-end for resize bounds changes
- TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
- pipChange.getTaskInfo().getToken());
+ TransitionInfo.Change pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
+ info, pipChange.getTaskInfo().getToken());
if (pipActivityChange != null) {
// Transform calculations use PiP params by default, so make sure they are null to
// default to using bounds for scaling calculations instead.
@@ -427,8 +428,8 @@
}
// We expect the PiP activity as a separate change in a config-at-end transition.
- TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
- pipChange.getTaskInfo().getToken());
+ TransitionInfo.Change pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
+ info, pipChange.getTaskInfo().getToken());
if (pipActivityChange == null) {
return false;
}
@@ -497,8 +498,8 @@
}
// We expect the PiP activity as a separate change in a config-at-end transition.
- TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
- pipChange.getTaskInfo().getToken());
+ TransitionInfo.Change pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
+ info, pipChange.getTaskInfo().getToken());
if (pipActivityChange == null) {
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java
index 01cda6c..e562f33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java
@@ -67,6 +67,36 @@
}
/**
+ * @return a change representing a config-at-end activity for ancestor.
+ */
+ @Nullable
+ public static TransitionInfo.Change getDeferConfigActivityChange(TransitionInfo info,
+ @NonNull WindowContainerToken ancestor) {
+ final TransitionInfo.Change ancestorChange =
+ PipTransitionUtils.getChangeByToken(info, ancestor);
+ if (ancestorChange == null) return null;
+
+ // Iterate through changes bottom-to-top, going up the parent chain starting with ancestor.
+ TransitionInfo.Change lastPipChildChange = ancestorChange;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (change == ancestorChange) continue;
+
+ if (change.getParent() != null
+ && change.getParent().equals(lastPipChildChange.getContainer())) {
+ // Found a child of the last cached child along the ancestral chain.
+ lastPipChildChange = change;
+ if (change.getTaskInfo() == null
+ && change.hasFlags(TransitionInfo.FLAG_CONFIG_AT_END)) {
+ // If this is a config-at-end activity change, then we found the chain leaf.
+ return change;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
* @return the leash to interact with the container this change represents.
* @throws NullPointerException if the leash is null.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
index 1853ffa..320a63a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
@@ -34,6 +34,7 @@
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip2.phone.transition.PipTransitionUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.StageCoordinator;
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
@@ -132,7 +133,7 @@
TransitionInfo.Change pipActivityChange = null;
if (pipChange != null) {
- pipActivityChange = mPipHandler.getDeferConfigActivityChange(
+ pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
info, pipChange.getContainer());
everythingElse.getChanges().remove(pipActivityChange);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index e28a7fa..003ef1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -39,6 +39,7 @@
import static com.android.systemui.shared.Flags.returnAnimationFrameworkLongLived;
import static com.android.window.flags.Flags.ensureWallpaperInTransitions;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
import static com.android.wm.shell.shared.TransitionUtil.FLAG_IS_DESKTOP_WALLPAPER_ACTIVITY;
import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
@@ -52,6 +53,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -138,6 +140,10 @@
ShellCommandHandler.ShellCommandActionHandler {
static final String TAG = "ShellTransitions";
+ // If set, will print the stack trace for transition starts within the process
+ static final boolean DEBUG_START_TRANSITION = Build.IS_DEBUGGABLE &&
+ SystemProperties.getBoolean("persist.wm.debug.start_shell_transition", false);
+
/** Set to {@code true} to enable shell transitions. */
public static final boolean ENABLE_SHELL_TRANSITIONS = getShellTransitEnabled();
public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
@@ -346,10 +352,10 @@
mShellController = shellController;
// The very last handler (0 in the list) should be the default one.
mHandlers.add(mDefaultTransitionHandler);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Default");
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "addHandler: Default");
// Next lowest priority is remote transitions.
mHandlers.add(mRemoteTransitionHandler);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "addHandler: Remote");
shellInit.addInitCallback(this::onInit, this);
mHomeTransitionObserver = homeTransitionObserver;
mFocusTransitionObserver = focusTransitionObserver;
@@ -439,7 +445,7 @@
mHandlers.add(handler);
// Set initial scale settings.
handler.setAnimScaleSetting(mTransitionAnimationScaleSetting);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: %s",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "addHandler: %s",
handler.getClass().getSimpleName());
}
@@ -691,7 +697,7 @@
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
info.setUnreleasedWarningCallSiteForAllSurfaces("Transitions.onTransitionReady");
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s",
info.getDebugId(), transitionToken, info.toString(" " /* prefix */));
int activeIdx = findByToken(mPendingTransitions, transitionToken);
if (activeIdx < 0) {
@@ -753,7 +759,7 @@
if (tr.isIdle()) continue;
hadPreceding = true;
// Sleep starts a process of forcing all prior transitions to finish immediately
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ ProtoLog.v(WM_SHELL_TRANSITIONS,
"Start finish-for-sync track %d", i);
finishForSync(active.mToken, i, null /* forceFinish */);
}
@@ -797,7 +803,7 @@
if (info.getRootCount() == 0 && !KeyguardTransitionHandler.handles(info)) {
// No root-leashes implies that the transition is empty/no-op, so just do
// housekeeping and return.
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "No transition roots in %s so"
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "No transition roots in %s so"
+ " abort", active);
onAbort(active);
return true;
@@ -839,7 +845,7 @@
&& allOccluded)) {
// Treat this as an abort since we are bypassing any merge logic and effectively
// finishing immediately.
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ ProtoLog.v(WM_SHELL_TRANSITIONS,
"Non-visible anim so abort: %s", active);
onAbort(active);
return true;
@@ -873,7 +879,7 @@
void processReadyQueue(Track track) {
if (track.mReadyTransitions.isEmpty()) {
if (track.mActiveTransition == null) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Track %d became idle",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Track %d became idle",
mTracks.indexOf(track));
if (areTracksIdle()) {
if (!mReadyDuringSync.isEmpty()) {
@@ -885,7 +891,7 @@
if (!success) break;
}
} else if (mPendingTransitions.isEmpty()) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "All active transition "
+ "animations finished");
mKnownTransitions.clear();
// Run all runnables from the run-when-idle queue.
@@ -926,7 +932,7 @@
onMerged(playingToken, readyToken);
return;
}
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition %s ready while"
+ " %s is still animating. Notify the animating transition"
+ " in case they can be merged", ready, playing);
mTransitionTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId());
@@ -955,7 +961,7 @@
}
final Track track = mTracks.get(playing.getTrack());
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s into %s",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition was merged: %s into %s",
merged, playing);
int readyIdx = 0;
if (track.mReadyTransitions.isEmpty() || track.mReadyTransitions.get(0) != merged) {
@@ -996,7 +1002,7 @@
}
private void playTransition(@NonNull ActiveTransition active) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
final var token = active.mToken;
for (int i = 0; i < mObservers.size(); ++i) {
@@ -1007,12 +1013,12 @@
// If a handler already chose to run this animation, try delegating to it first.
if (active.mHandler != null) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " try firstHandler %s",
active.mHandler);
boolean consumed = active.mHandler.startAnimation(token, active.mInfo,
active.mStartT, active.mFinishT, (wct) -> onFinish(token, wct));
if (consumed) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " animated by firstHandler");
mTransitionTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler);
if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
Trace.instant(TRACE_TAG_WINDOW_MANAGER,
@@ -1042,14 +1048,14 @@
) {
for (int i = mHandlers.size() - 1; i >= 0; --i) {
if (mHandlers.get(i) == skip) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " skip handler %s",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " skip handler %s",
mHandlers.get(i));
continue;
}
boolean consumed = mHandlers.get(i).startAnimation(transition, info, startT, finishT,
finishCB);
if (consumed) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " animated by %s",
mHandlers.get(i));
mTransitionTracer.logDispatched(info.getDebugId(), mHandlers.get(i));
if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
@@ -1155,7 +1161,7 @@
for (int i = 0; i < mObservers.size(); ++i) {
mObservers.get(i).onTransitionFinished(active.mToken, active.mAborted);
}
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition animation finished "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition animation finished "
+ "(aborted=%b), notifying core %s", active.mAborted, active);
if (active.mStartT != null) {
// Applied by now, so clear immediately to remove any references. Do not set to null
@@ -1209,7 +1215,7 @@
void requestStartTransition(@NonNull IBinder transitionToken,
@Nullable TransitionRequestInfo request) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s",
request.getDebugId(), transitionToken, request);
if (mKnownTransitions.containsKey(transitionToken)) {
throw new RuntimeException("Transition already started " + transitionToken);
@@ -1228,6 +1234,8 @@
if (requestResult != null) {
active.mHandler = requestResult.first;
wct = requestResult.second;
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition (#%d): request handled by %s",
+ request.getDebugId(), active.mHandler.getClass().getSimpleName());
}
if (request.getDisplayChange() != null) {
TransitionRequestInfo.DisplayChange change = request.getDisplayChange();
@@ -1273,8 +1281,12 @@
*/
public IBinder startTransition(@WindowManager.TransitionType int type,
@NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Directly starting a new transition "
+ "type=%s wct=%s handler=%s", transitTypeToString(type), wct, handler);
+ if (DEBUG_START_TRANSITION) {
+ Log.d(TAG, "startTransition: type=" + transitTypeToString(type)
+ + " wct=" + wct + " handler=" + handler.getClass().getName(), new Throwable());
+ }
final ActiveTransition active =
new ActiveTransition(mOrganizer.startNewTransition(type, wct));
active.mHandler = handler;
@@ -1362,7 +1374,7 @@
}
// Attempt to merge a SLEEP info to signal that the playing transition needs to
// fast-forward.
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt to merge sync %s"
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " Attempt to merge sync %s"
+ " into %s via a SLEEP proxy", nextSync, playing);
playing.mHandler.mergeAnimation(nextSync.mToken, dummyInfo, dummyT, dummyT,
playing.mToken, (wct) -> {});
@@ -1598,7 +1610,7 @@
public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
throws RemoteException {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady(transaction=%d)",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "onTransitionReady(transaction=%d)",
t.getId());
mMainExecutor.execute(() -> Transitions.this.onTransitionReady(
iBinder, transitionInfo, t, finishT));
@@ -1784,8 +1796,9 @@
pw.println(prefix + TAG);
final String innerPrefix = prefix + " ";
- pw.println(prefix + "Handlers:");
- for (TransitionHandler handler : mHandlers) {
+ pw.println(prefix + "Handlers (ordered by priority):");
+ for (int i = mHandlers.size() - 1; i >= 0; i--) {
+ final TransitionHandler handler = mHandlers.get(i);
pw.print(innerPrefix);
pw.print(handler.getClass().getSimpleName());
pw.println(" (" + Integer.toHexString(System.identityHashCode(handler)) + ")");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 5e8c1fe..e08ef58 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -49,6 +49,7 @@
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import android.window.DesktopExperienceFlags;
import android.window.DesktopModeFlags;
import android.window.WindowContainerTransaction;
@@ -218,11 +219,17 @@
relayoutParams.mRunningTaskInfo = taskInfo;
relayoutParams.mLayoutResId = R.layout.caption_window_decor;
relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode());
- relayoutParams.mShadowRadius = hasGlobalFocus
- ? context.getResources().getDimensionPixelSize(
- R.dimen.freeform_decor_shadow_focused_thickness)
- : context.getResources().getDimensionPixelSize(
- R.dimen.freeform_decor_shadow_unfocused_thickness);
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ relayoutParams.mShadowRadiusId = hasGlobalFocus
+ ? R.dimen.freeform_decor_shadow_focused_thickness
+ : R.dimen.freeform_decor_shadow_unfocused_thickness;
+ } else {
+ relayoutParams.mShadowRadius = hasGlobalFocus
+ ? context.getResources().getDimensionPixelSize(
+ R.dimen.freeform_decor_shadow_focused_thickness)
+ : context.getResources().getDimensionPixelSize(
+ R.dimen.freeform_decor_shadow_unfocused_thickness);
+ }
relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop;
relayoutParams.mIsCaptionVisible = taskInfo.isFreeform()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index bcf9396..e8019e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -72,6 +72,7 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.widget.ImageButton;
+import android.window.DesktopExperienceFlags;
import android.window.DesktopModeFlags;
import android.window.TaskSnapshot;
import android.window.WindowContainerTransaction;
@@ -719,7 +720,8 @@
.getScaledTouchSlop();
final Resources res = mResult.mRootView.getResources();
final DragResizeWindowGeometry newGeometry = new DragResizeWindowGeometry(
- mRelayoutParams.mCornerRadius,
+ DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()
+ ? mResult.mCornerRadius : mRelayoutParams.mCornerRadius,
new Size(mResult.mWidth, mResult.mHeight),
getResizeEdgeHandleSize(res), getResizeHandleEdgeInset(res),
getFineResizeCornerSize(res), getLargeResizeCornerSize(res),
@@ -1072,13 +1074,23 @@
}
if (isAppHeader
&& DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ hasGlobalFocus)) {
- relayoutParams.mShadowRadius = hasGlobalFocus
- ? context.getResources().getDimensionPixelSize(
- R.dimen.freeform_decor_shadow_focused_thickness)
- : context.getResources().getDimensionPixelSize(
- R.dimen.freeform_decor_shadow_unfocused_thickness);
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ relayoutParams.mShadowRadiusId = hasGlobalFocus
+ ? R.dimen.freeform_decor_shadow_focused_thickness
+ : R.dimen.freeform_decor_shadow_unfocused_thickness;
+ } else {
+ relayoutParams.mShadowRadius = hasGlobalFocus
+ ? context.getResources().getDimensionPixelSize(
+ R.dimen.freeform_decor_shadow_focused_thickness)
+ : context.getResources().getDimensionPixelSize(
+ R.dimen.freeform_decor_shadow_unfocused_thickness);
+ }
} else {
- relayoutParams.mShadowRadius = INVALID_SHADOW_RADIUS;
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ relayoutParams.mShadowRadiusId = Resources.ID_NULL;
+ } else {
+ relayoutParams.mShadowRadius = INVALID_SHADOW_RADIUS;
+ }
}
relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop;
@@ -1104,8 +1116,13 @@
relayoutParams.mWindowDecorConfig = windowDecorConfig;
if (DesktopModeStatus.useRoundedCorners()) {
- relayoutParams.mCornerRadius = shouldIgnoreCornerRadius ? INVALID_CORNER_RADIUS :
- getCornerRadius(context, relayoutParams.mLayoutResId);
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ relayoutParams.mCornerRadiusId = shouldIgnoreCornerRadius ? Resources.ID_NULL :
+ getCornerRadiusId(relayoutParams.mLayoutResId);
+ } else {
+ relayoutParams.mCornerRadius = shouldIgnoreCornerRadius ? INVALID_CORNER_RADIUS :
+ getCornerRadius(context, relayoutParams.mLayoutResId);
+ }
}
// Set opaque background for all freeform tasks to prevent freeform tasks below
// from being visible if freeform task window above is translucent.
@@ -1113,6 +1130,7 @@
relayoutParams.mShouldSetBackground = DesktopModeStatus.shouldSetBackground(taskInfo);
}
+ @Deprecated
private static int getCornerRadius(@NonNull Context context, int layoutResId) {
if (layoutResId == R.layout.desktop_mode_app_header) {
return loadDimensionPixelSize(context.getResources(),
@@ -1122,6 +1140,14 @@
return INVALID_CORNER_RADIUS;
}
+ private static int getCornerRadiusId(int layoutResId) {
+ if (layoutResId == R.layout.desktop_mode_app_header) {
+ return com.android.wm.shell.shared.R.dimen
+ .desktop_windowing_freeform_rounded_corner_radius;
+ }
+ return Resources.ID_NULL;
+ }
+
/**
* If task has focused window decor, return the caption id of the fullscreen caption size
* resource. Otherwise, return ID_NULL and caption width be set to task width.
@@ -1756,8 +1782,10 @@
mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
disposeResizeVeil();
disposeStatusBarInputLayer();
- mWindowDecorViewHolder.close();
- mWindowDecorViewHolder = null;
+ if (mWindowDecorViewHolder != null) {
+ mWindowDecorViewHolder.close();
+ mWindowDecorViewHolder = null;
+ }
if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
notifyNoCaptionHandle();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 91a899c..6fd963f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -47,6 +47,7 @@
import android.view.View;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
+import android.window.DesktopExperienceFlags;
import android.window.SurfaceSyncGroup;
import android.window.TaskConstants;
import android.window.WindowContainerToken;
@@ -286,6 +287,14 @@
outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2;
outResult.mCaptionY = 0;
outResult.mCaptionTopPadding = params.mCaptionTopPadding;
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ outResult.mCornerRadius = params.mCornerRadiusId == Resources.ID_NULL
+ ? INVALID_CORNER_RADIUS : loadDimensionPixelSize(resources,
+ params.mCornerRadiusId);
+ outResult.mShadowRadius = params.mShadowRadiusId == Resources.ID_NULL
+ ? INVALID_SHADOW_RADIUS : loadDimensionPixelSize(resources,
+ params.mShadowRadiusId);
+ }
Trace.beginSection("relayout-createViewHostIfNeeded");
createViewHostIfNeeded(mDecorWindowContext, mDisplay);
@@ -497,9 +506,16 @@
.setPosition(mTaskSurface, taskPosition.x, taskPosition.y);
}
- if (params.mShadowRadius != INVALID_SHADOW_RADIUS) {
- startT.setShadowRadius(mTaskSurface, params.mShadowRadius);
- finishT.setShadowRadius(mTaskSurface, params.mShadowRadius);
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ if (outResult.mShadowRadius != INVALID_SHADOW_RADIUS) {
+ startT.setShadowRadius(mTaskSurface, outResult.mShadowRadius);
+ finishT.setShadowRadius(mTaskSurface, outResult.mShadowRadius);
+ }
+ } else {
+ if (params.mShadowRadius != INVALID_SHADOW_RADIUS) {
+ startT.setShadowRadius(mTaskSurface, params.mShadowRadius);
+ finishT.setShadowRadius(mTaskSurface, params.mShadowRadius);
+ }
}
if (params.mSetTaskVisibilityPositionAndCrop) {
@@ -517,9 +533,16 @@
startT.unsetColor(mTaskSurface);
}
- if (params.mCornerRadius != INVALID_CORNER_RADIUS) {
- startT.setCornerRadius(mTaskSurface, params.mCornerRadius);
- finishT.setCornerRadius(mTaskSurface, params.mCornerRadius);
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ if (outResult.mCornerRadius != INVALID_CORNER_RADIUS) {
+ startT.setCornerRadius(mTaskSurface, outResult.mCornerRadius);
+ finishT.setCornerRadius(mTaskSurface, outResult.mCornerRadius);
+ }
+ } else {
+ if (params.mCornerRadius != INVALID_CORNER_RADIUS) {
+ startT.setCornerRadius(mTaskSurface, params.mCornerRadius);
+ finishT.setCornerRadius(mTaskSurface, params.mCornerRadius);
+ }
}
}
@@ -824,9 +847,14 @@
@InsetsSource.Flags int mInsetSourceFlags;
final Region mDisplayExclusionRegion = Region.obtain();
+ @Deprecated
int mShadowRadius = INVALID_SHADOW_RADIUS;
+ @Deprecated
int mCornerRadius = INVALID_CORNER_RADIUS;
+ int mShadowRadiusId = Resources.ID_NULL;
+ int mCornerRadiusId = Resources.ID_NULL;
+
int mCaptionTopPadding;
boolean mIsCaptionVisible;
@@ -849,9 +877,13 @@
mIsInsetSource = true;
mInsetSourceFlags = 0;
mDisplayExclusionRegion.setEmpty();
-
- mShadowRadius = INVALID_SHADOW_RADIUS;
- mCornerRadius = INVALID_SHADOW_RADIUS;
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ mShadowRadiusId = Resources.ID_NULL;
+ mCornerRadiusId = Resources.ID_NULL;
+ } else {
+ mShadowRadius = INVALID_SHADOW_RADIUS;
+ mCornerRadius = INVALID_SHADOW_RADIUS;
+ }
mCaptionTopPadding = 0;
mIsCaptionVisible = false;
@@ -893,6 +925,8 @@
int mWidth;
int mHeight;
T mRootView;
+ int mCornerRadius;
+ int mShadowRadius;
void reset() {
mWidth = 0;
@@ -904,6 +938,10 @@
mCaptionTopPadding = 0;
mCustomizableCaptionRegion.setEmpty();
mRootView = null;
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ mCornerRadius = INVALID_CORNER_RADIUS;
+ mShadowRadius = INVALID_SHADOW_RADIUS;
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt
index 509f4f2..8e1cf167 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt
@@ -254,6 +254,16 @@
}
}
+/**
+ * Checks that surfaces are still within the expected region after snapping to a snap point.
+ *
+ * @param component The component we are checking (should be one of the two split apps)
+ * @param landscapePosLeft If [true], and device is in left/right split, app is on the left side of
+ * the screen. Has no meaning if device is in top/bottom split.
+ * @param portraitPosTop If [true], and device is in top/bottom split, app is on the top side of
+ * the screen. Has no meaning if device is in left/right split.
+ * @param rotation The rotation state of the display.
+ */
fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider(
component: IComponentMatcher,
landscapePosLeft: Boolean,
@@ -268,10 +278,12 @@
visibleRegion(component).isNotEmpty()
visibleRegion(component)
.coversAtMost(
+ // TODO (b/403082705): Should use the new method for determining left/right split.
if (displayBounds.width() > displayBounds.height()) {
if (landscapePosLeft) {
Region(
- 0,
+ // TODO (b/403304310): Check if we're in an offscreen-enabled mode.
+ -displayBounds.right, // the receding app can go offscreen
0,
(dividerRegion.left + dividerRegion.right) / 2,
displayBounds.bottom
@@ -280,7 +292,7 @@
Region(
(dividerRegion.left + dividerRegion.right) / 2,
0,
- displayBounds.right,
+ displayBounds.right * 2, // the receding app can go offscreen
displayBounds.bottom
)
}
@@ -288,7 +300,7 @@
if (portraitPosTop) {
Region(
0,
- 0,
+ -displayBounds.bottom, // the receding app can go offscreen
displayBounds.right,
(dividerRegion.top + dividerRegion.bottom) / 2
)
@@ -297,7 +309,7 @@
0,
(dividerRegion.top + dividerRegion.bottom) / 2,
displayBounds.right,
- displayBounds.bottom
+ displayBounds.bottom * 2 // the receding app can go offscreen
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
index 49d6877..e4183f1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
@@ -310,12 +310,18 @@
}
}
+ /**
+ * Drags the divider, then releases, making it snap to a new snap point.
+ */
fun dragDividerToResizeAndWait(device: UiDevice, wmHelper: WindowManagerStateHelper) {
+ // Find the first display that is turned on (making the assumption that there is only one).
val displayBounds =
- wmHelper.currentState.layerState.displays.firstOrNull { !it.isVirtual }?.layerStackSpace
- ?: error("Display not found")
+ wmHelper.currentState.layerState.displays.firstOrNull { !it.isVirtual && it.isOn }
+ ?.layerStackSpace ?: error("Display not found")
val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
- dividerBar.drag(Point(displayBounds.width() * 1 / 3, displayBounds.height() * 2 / 3), 200)
+ // Drag to a point on the lower left of the screen -- this will cause the divider to snap
+ // to the left- or bottom-side snap point, shrinking the "primary" test app.
+ dividerBar.drag(Point(displayBounds.width() * 1 / 4, displayBounds.height() * 3 / 4), 200)
wmHelper
.StateSyncBuilder()
diff --git a/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml b/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml
index aa1b241..33ea0ba 100644
--- a/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml
@@ -18,6 +18,8 @@
<!-- Resources used in WindowDecorationTests -->
<dimen name="test_freeform_decor_caption_height">32dp</dimen>
<dimen name="test_freeform_decor_caption_menu_width">216dp</dimen>
+ <dimen name="test_freeform_shadow_radius">20dp</dimen>
+ <dimen name="test_freeform_corner_radius">16dp</dimen>
<dimen name="test_window_decor_left_outset">10dp</dimen>
<dimen name="test_window_decor_top_outset">20dp</dimen>
<dimen name="test_window_decor_right_outset">30dp</dimen>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index f37f2fb..f7b9c335 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -59,6 +59,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Point;
import android.graphics.Rect;
@@ -341,7 +342,8 @@
}
@Test
- public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreSetForFreeform() {
+ @DisableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreSetForFreeform_dynamicDisabled() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
RelayoutParams relayoutParams = new RelayoutParams();
@@ -353,7 +355,8 @@
}
@Test
- public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForFullscreen() {
+ @DisableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForFullscreen_dynamicDisabled() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
RelayoutParams relayoutParams = new RelayoutParams();
@@ -364,7 +367,8 @@
}
@Test
- public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForSplit() {
+ @DisableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForSplit_dynamicDisabled() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
RelayoutParams relayoutParams = new RelayoutParams();
@@ -375,7 +379,8 @@
}
@Test
- public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersSetForFreeform() {
+ @DisableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersSetForFreeform_dynamicDisabled() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
fillRoundedCornersResources(/* fillValue= */ 30);
@@ -387,7 +392,8 @@
}
@Test
- public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersNotSetForFullscreen() {
+ @DisableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersNotSetForFullscreen_dynamicDisabled() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
fillRoundedCornersResources(/* fillValue= */ 30);
@@ -399,7 +405,8 @@
}
@Test
- public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersNotSetForSplit() {
+ @DisableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersNotSetForSplit_dynamicDisabled() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
fillRoundedCornersResources(/* fillValue= */ 30);
@@ -411,7 +418,8 @@
}
@Test
- public void updateRelayoutParams_shouldIgnoreCornerRadius_roundedCornersNotSet() {
+ @DisableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_shouldIgnoreCornerRadius_roundedCornersNotSet_dynamicDisabled() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
fillRoundedCornersResources(/* fillValue= */ 30);
@@ -440,6 +448,107 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreSetForFreeform() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ updateRelayoutParams(relayoutParams, taskInfo);
+
+ assertThat(relayoutParams.mShadowRadiusId).isNotEqualTo(Resources.ID_NULL);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForFullscreen() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ updateRelayoutParams(relayoutParams, taskInfo);
+
+ assertThat(relayoutParams.mShadowRadiusId).isEqualTo(Resources.ID_NULL);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForSplit() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ updateRelayoutParams(relayoutParams, taskInfo);
+
+ assertThat(relayoutParams.mShadowRadiusId).isEqualTo(Resources.ID_NULL);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersSetForFreeform() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ updateRelayoutParams(relayoutParams, taskInfo);
+
+ assertThat(relayoutParams.mShadowRadiusId).isNotEqualTo(Resources.ID_NULL);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersNotSetForFullscreen() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ updateRelayoutParams(relayoutParams, taskInfo);
+
+ assertThat(relayoutParams.mCornerRadiusId).isEqualTo(Resources.ID_NULL);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersNotSetForSplit() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ updateRelayoutParams(relayoutParams, taskInfo);
+
+ assertThat(relayoutParams.mCornerRadiusId).isEqualTo(Resources.ID_NULL);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_shouldIgnoreCornerRadius_roundedCornersNotSet() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ mMockSplitScreenController,
+ DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
+ DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
+ DEFAULT_IS_STATUSBAR_VISIBLE,
+ DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
+ DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+ DEFAULT_IS_DRAGGING,
+ new InsetsState(),
+ DEFAULT_HAS_GLOBAL_FOCUS,
+ mExclusionRegion,
+ /* shouldIgnoreCornerRadius= */ true,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
+
+ assertThat(relayoutParams.mCornerRadiusId).isEqualTo(Resources.ID_NULL);
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_APP_HEADER_WITH_TASK_DENSITY)
public void updateRelayoutParams_appHeader_usesTaskDensity() {
final int systemDensity = mTestableContext.getOrCreateTestableResources().getResources()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 2e95a97..c691dc7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -61,7 +61,8 @@
import android.graphics.Region;
import android.os.Handler;
import android.os.LocaleList;
-import android.testing.AndroidTestingRunner;
+import android.platform.test.annotations.UsesFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.util.DisplayMetrics;
import android.view.AttachedSurfaceControl;
import android.view.Display;
@@ -78,6 +79,7 @@
import androidx.test.filters.SmallTest;
+import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
@@ -96,6 +98,9 @@
import org.mockito.Mock;
import org.mockito.Mockito;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -108,7 +113,8 @@
* atest WMShellUnitTests:WindowDecorationTests
*/
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(ParameterizedAndroidJunit4.class)
+@UsesFlags(com.android.window.flags.Flags.class)
public class WindowDecorationTests extends ShellTestCase {
private static final Rect TASK_BOUNDS = new Rect(100, 300, 400, 400);
private static final Point TASK_POSITION_IN_PARENT = new Point(40, 60);
@@ -116,6 +122,12 @@
private static final int SHADOW_RADIUS = 10;
private static final int STATUS_BAR_INSET_SOURCE_ID = 0;
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(
+ Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX);
+ }
+
private final WindowDecoration.RelayoutResult<TestView> mRelayoutResult =
new WindowDecoration.RelayoutResult<>();
@@ -156,6 +168,10 @@
private WindowDecoration.RelayoutParams mRelayoutParams = new WindowDecoration.RelayoutParams();
private int mCaptionMenuWidthId;
+ public WindowDecorationTests(FlagsParameterization flags) {
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() {
mMockSurfaceControlStartT = createMockSurfaceControlTransaction();
@@ -165,8 +181,13 @@
mRelayoutParams.mLayoutResId = 0;
mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height;
mCaptionMenuWidthId = R.dimen.test_freeform_decor_caption_menu_width;
- mRelayoutParams.mShadowRadius = SHADOW_RADIUS;
- mRelayoutParams.mCornerRadius = CORNER_RADIUS;
+ if (Flags.enableDynamicRadiusComputationBugfix()) {
+ mRelayoutParams.mShadowRadiusId = R.dimen.test_freeform_shadow_radius;
+ mRelayoutParams.mCornerRadiusId = R.dimen.test_freeform_corner_radius;
+ } else {
+ mRelayoutParams.mShadowRadius = SHADOW_RADIUS;
+ mRelayoutParams.mCornerRadius = CORNER_RADIUS;
+ }
when(mMockDisplayController.getDisplay(Display.DEFAULT_DISPLAY))
.thenReturn(mock(Display.class));
@@ -282,9 +303,21 @@
any(),
anyInt());
- verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
- verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
- verify(mMockSurfaceControlStartT).setShadowRadius(mMockTaskSurface, SHADOW_RADIUS);
+ if (Flags.enableDynamicRadiusComputationBugfix()) {
+ final int cornerRadius = WindowDecoration.loadDimensionPixelSize(
+ windowDecor.mDecorWindowContext.getResources(),
+ mRelayoutParams.mCornerRadiusId);
+ verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, cornerRadius);
+ verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, cornerRadius);
+ final int shadowRadius = WindowDecoration.loadDimensionPixelSize(
+ windowDecor.mDecorWindowContext.getResources(),
+ mRelayoutParams.mShadowRadiusId);
+ verify(mMockSurfaceControlStartT).setShadowRadius(mMockTaskSurface, shadowRadius);
+ } else {
+ verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
+ verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
+ verify(mMockSurfaceControlStartT).setShadowRadius(mMockTaskSurface, SHADOW_RADIUS);
+ }
assertEquals(300, mRelayoutResult.mWidth);
assertEquals(100, mRelayoutResult.mHeight);
@@ -1198,7 +1231,8 @@
}
@Override
- public void setTaskFocusState(boolean focused) {}
+ public void setTaskFocusState(boolean focused) {
+ }
}
private class TestWindowDecoration extends WindowDecoration<TestView> {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index a892e88..ab1be7e 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -139,6 +139,7 @@
"libandroidfw",
"libcrypto",
"libsync",
+ "libgui",
"libui",
"aconfig_text_flags_c_lib",
"aconfig_view_accessibility_flags_c_lib",
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index 9d16ee8..7e1eb70 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -16,6 +16,7 @@
#include "WebViewFunctorManager.h"
+#include <gui/SurfaceComposerClient.h>
#include <log/log.h>
#include <private/hwui/WebViewFunctor.h>
#include <utils/Trace.h>
@@ -43,7 +44,7 @@
static ASurfaceControl* getSurfaceControl() {
ALOG_ASSERT(sCurrentFunctor);
- return sCurrentFunctor->getSurfaceControl();
+ return reinterpret_cast<ASurfaceControl*>(sCurrentFunctor->getSurfaceControl());
}
static void mergeTransaction(ASurfaceTransaction* transaction) {
ALOG_ASSERT(sCurrentFunctor);
@@ -129,12 +130,12 @@
renderthread::CanvasContext* activeContext = renderthread::CanvasContext::getActiveContext();
if (!activeContext) return false;
- ASurfaceControl* rootSurfaceControl = activeContext->getSurfaceControl();
+ sp<SurfaceControl> rootSurfaceControl = activeContext->getSurfaceControl();
if (!rootSurfaceControl) return false;
int32_t rgid = activeContext->getSurfaceControlGenerationId();
if (mParentSurfaceControlGenerationId != rgid) {
- reparentSurfaceControl(rootSurfaceControl);
+ reparentSurfaceControl(reinterpret_cast<ASurfaceControl*>(rootSurfaceControl.get()));
mParentSurfaceControlGenerationId = rgid;
}
@@ -210,33 +211,35 @@
mCallbacks.removeOverlays(mFunctor, mData, currentFunctor.mergeTransaction);
if (mSurfaceControl) {
reparentSurfaceControl(nullptr);
- auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions();
- funcs.releaseFunc(mSurfaceControl);
mSurfaceControl = nullptr;
}
}
ASurfaceControl* WebViewFunctor::getSurfaceControl() {
ATRACE_NAME("WebViewFunctor::getSurfaceControl");
- if (mSurfaceControl != nullptr) return mSurfaceControl;
+ if (mSurfaceControl != nullptr) {
+ return reinterpret_cast<ASurfaceControl*>(mSurfaceControl.get());
+ }
renderthread::CanvasContext* activeContext = renderthread::CanvasContext::getActiveContext();
LOG_ALWAYS_FATAL_IF(activeContext == nullptr, "Null active canvas context!");
- ASurfaceControl* rootSurfaceControl = activeContext->getSurfaceControl();
+ sp<SurfaceControl> rootSurfaceControl = activeContext->getSurfaceControl();
LOG_ALWAYS_FATAL_IF(rootSurfaceControl == nullptr, "Null root surface control!");
- auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions();
mParentSurfaceControlGenerationId = activeContext->getSurfaceControlGenerationId();
- mSurfaceControl = funcs.createFunc(rootSurfaceControl, "Webview Overlay SurfaceControl");
- ASurfaceTransaction* transaction = funcs.transactionCreateFunc();
+
+ SurfaceComposerClient* client = rootSurfaceControl->getClient().get();
+ mSurfaceControl = client->createSurface(
+ String8("Webview Overlay SurfaceControl"), 0 /* width */, 0 /* height */,
+ // Format is only relevant for buffer queue layers.
+ PIXEL_FORMAT_UNKNOWN /* format */, ISurfaceComposerClient::eFXSurfaceBufferState,
+ rootSurfaceControl->getHandle());
+
activeContext->prepareSurfaceControlForWebview();
- funcs.transactionSetZOrderFunc(transaction, mSurfaceControl, -1);
- funcs.transactionSetVisibilityFunc(transaction, mSurfaceControl,
- ASURFACE_TRANSACTION_VISIBILITY_SHOW);
- funcs.transactionApplyFunc(transaction);
- funcs.transactionDeleteFunc(transaction);
- return mSurfaceControl;
+ SurfaceComposerClient::Transaction transaction;
+ transaction.setLayer(mSurfaceControl, -1).show(mSurfaceControl).apply();
+ return reinterpret_cast<ASurfaceControl*>(mSurfaceControl.get());
}
void WebViewFunctor::mergeTransaction(ASurfaceTransaction* transaction) {
@@ -249,8 +252,7 @@
done = activeContext->mergeTransaction(transaction, mSurfaceControl);
}
if (!done) {
- auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions();
- funcs.transactionApplyFunc(transaction);
+ reinterpret_cast<SurfaceComposerClient::Transaction*>(transaction)->apply();
}
}
@@ -258,11 +260,10 @@
ATRACE_NAME("WebViewFunctor::reparentSurfaceControl");
if (mSurfaceControl == nullptr) return;
- auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions();
- ASurfaceTransaction* transaction = funcs.transactionCreateFunc();
- funcs.transactionReparentFunc(transaction, mSurfaceControl, parent);
- mergeTransaction(transaction);
- funcs.transactionDeleteFunc(transaction);
+ SurfaceComposerClient::Transaction transaction;
+ transaction.reparent(mSurfaceControl, sp<SurfaceControl>::fromExisting(
+ reinterpret_cast<SurfaceControl*>(parent)));
+ mergeTransaction(reinterpret_cast<ASurfaceTransaction*>(&transaction));
}
void WebViewFunctor::reportRenderingThreads(const pid_t* thread_ids, size_t size) {
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
index ec17640..ac16f91 100644
--- a/libs/hwui/WebViewFunctorManager.h
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -25,7 +25,11 @@
#include <mutex>
#include <vector>
-namespace android::uirenderer {
+namespace android {
+
+class SurfaceControl;
+
+namespace uirenderer {
class WebViewFunctorManager;
@@ -100,7 +104,9 @@
bool mHasContext = false;
bool mCreatedHandle = false;
int32_t mParentSurfaceControlGenerationId = 0;
- ASurfaceControl* mSurfaceControl = nullptr;
+#ifdef __ANDROID__
+ sp<SurfaceControl> mSurfaceControl = nullptr;
+#endif
std::vector<pid_t> mRenderingThreads;
};
@@ -126,4 +132,5 @@
std::vector<sp<WebViewFunctor::Handle>> mActiveFunctors;
};
-} // namespace android::uirenderer
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index d3fc91b..b3badd0 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -203,4 +203,11 @@
description: "Initialize GraphicBufferAllocater on ViewRootImpl init, to avoid blocking on init during buffer allocation, improving app launch latency."
bug: "389908734"
is_fixed_read_only: true
+}
+
+flag {
+ name: "bitmap_parcel_ashmem_as_immutable"
+ namespace: "system_performance"
+ description: "Whether to parcel implicit copies of bitmaps to ashmem as immutable"
+ bug: "400807118"
}
\ No newline at end of file
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 27d4ac7..104ece6 100644
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -28,8 +28,18 @@
#include "SkRefCnt.h"
#include "SkStream.h"
#include "SkTypes.h"
+#include "android/binder_parcel.h"
#include "android_nio_utils.h"
+#ifdef __ANDROID__
+#include <com_android_graphics_hwui_flags.h>
+namespace hwui_flags = com::android::graphics::hwui::flags;
+#else
+namespace hwui_flags {
+constexpr bool bitmap_parcel_ashmem_as_immutable() { return false; }
+}
+#endif
+
#define DEBUG_PARCEL 0
static jclass gBitmap_class;
@@ -841,6 +851,23 @@
#endif
}
+// Returns whether this bitmap should be written to the parcel as mutable.
+static bool shouldParcelAsMutable(SkBitmap& bitmap, AParcel* parcel) {
+ // If the bitmap is immutable, then parcel as immutable.
+ if (bitmap.isImmutable()) {
+ return false;
+ }
+
+ if (!hwui_flags::bitmap_parcel_ashmem_as_immutable()) {
+ return true;
+ }
+
+ // If we're going to copy the bitmap to ashmem and write that to the parcel,
+ // then parcel as immutable, since we won't be mutating the bitmap after
+ // writing it to the parcel.
+ return !shouldUseAshmem(parcel, bitmap.computeByteSize());
+}
+
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, jlong bitmapHandle, jint density,
jobject parcel) {
#ifdef __linux__ // Only Linux support parcel
@@ -855,7 +882,7 @@
auto bitmapWrapper = reinterpret_cast<BitmapWrapper*>(bitmapHandle);
bitmapWrapper->getSkBitmap(&bitmap);
- p.writeInt32(!bitmap.isImmutable());
+ p.writeInt32(shouldParcelAsMutable(bitmap, p.get()));
p.writeInt32(bitmap.colorType());
p.writeInt32(bitmap.alphaType());
SkColorSpace* colorSpace = bitmap.colorSpace();
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index cfec24b..009974b 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -53,6 +53,7 @@
#include <src/image/SkImage_Base.h>
#include <thread/CommonPool.h>
#ifdef __ANDROID__
+#include <gui/SurfaceControl.h>
#include <ui/GraphicBufferAllocator.h>
#endif
#include <utils/Color.h>
@@ -217,9 +218,11 @@
static void android_view_ThreadedRenderer_setSurfaceControl(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlong surfaceControlPtr) {
+#ifdef __ANDROID__
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
- ASurfaceControl* surfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControlPtr);
- proxy->setSurfaceControl(surfaceControl);
+ SurfaceControl* surfaceControl = reinterpret_cast<SurfaceControl*>(surfaceControlPtr);
+ proxy->setSurfaceControl(sp<SurfaceControl>::fromExisting(surfaceControl));
+#endif
}
static jboolean android_view_ThreadedRenderer_pause(JNIEnv* env, jobject clazz,
@@ -684,7 +687,7 @@
class CopyRequestAdapter : public CopyRequest {
public:
- CopyRequestAdapter(JavaVM* vm, jobject jCopyRequest, Rect srcRect)
+ CopyRequestAdapter(JavaVM* vm, jobject jCopyRequest, ::android::uirenderer::Rect srcRect)
: CopyRequest(srcRect), mRefHolder(vm, jCopyRequest) {}
virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override {
@@ -710,8 +713,9 @@
jobject jCopyRequest) {
JavaVM* vm = nullptr;
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
- auto copyRequest = std::make_shared<CopyRequestAdapter>(vm, env->NewGlobalRef(jCopyRequest),
- Rect(left, top, right, bottom));
+ auto copyRequest = std::make_shared<CopyRequestAdapter>(
+ vm, env->NewGlobalRef(jCopyRequest),
+ ::android::uirenderer::Rect(left, top, right, bottom));
ANativeWindow* window = fromSurface(env, jsurface);
RenderProxy::copySurfaceInto(window, std::move(copyRequest));
ANativeWindow_release(window);
diff --git a/libs/hwui/platform/host/WebViewFunctorManager.cpp b/libs/hwui/platform/host/WebViewFunctorManager.cpp
index 4ba206b..66646b2 100644
--- a/libs/hwui/platform/host/WebViewFunctorManager.cpp
+++ b/libs/hwui/platform/host/WebViewFunctorManager.cpp
@@ -45,7 +45,7 @@
void WebViewFunctor::removeOverlays() {}
ASurfaceControl* WebViewFunctor::getSurfaceControl() {
- return mSurfaceControl;
+ return nullptr;
}
void WebViewFunctor::mergeTransaction(ASurfaceTransaction* transaction) {}
diff --git a/libs/hwui/platform/host/renderthread/RenderThread.cpp b/libs/hwui/platform/host/renderthread/RenderThread.cpp
index f9d0f47..ece4530 100644
--- a/libs/hwui/platform/host/renderthread/RenderThread.cpp
+++ b/libs/hwui/platform/host/renderthread/RenderThread.cpp
@@ -27,8 +27,6 @@
static bool gHasRenderThreadInstance = false;
static JVMAttachHook gOnStartHook = nullptr;
-ASurfaceControlFunctions::ASurfaceControlFunctions() {}
-
bool RenderThread::hasInstance() {
return gHasRenderThreadInstance;
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b248c4b..d5ac993 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -18,6 +18,12 @@
#include <apex/window.h>
#include <fcntl.h>
+
+#ifdef __ANDROID__
+#include <gui/ITransactionCompletedListener.h>
+#include <gui/SurfaceComposerClient.h>
+#endif
+
#include <gui/TraceUtils.h>
#include <strings.h>
#include <sys/stat.h>
@@ -165,7 +171,9 @@
stopDrawing();
setHardwareBuffer(nullptr);
setSurface(nullptr);
+#ifdef __ANDROID__
setSurfaceControl(nullptr);
+#endif
freePrefetchedLayers();
destroyHardwareResources();
mAnimationContext->destroy();
@@ -220,10 +228,15 @@
setupPipelineSurface();
}
-void CanvasContext::setSurfaceControl(ASurfaceControl* surfaceControl) {
- if (surfaceControl == mSurfaceControl) return;
+#ifdef __ANDROID__
+sp<SurfaceControl> CanvasContext::getSurfaceControl() const {
+ return mSurfaceControl;
+}
+#endif
- auto funcs = mRenderThread.getASurfaceControlFunctions();
+void CanvasContext::setSurfaceControl(sp<SurfaceControl> surfaceControl) {
+#ifdef __ANDROID__
+ if (surfaceControl == mSurfaceControl) return;
if (surfaceControl == nullptr) {
setASurfaceTransactionCallback(nullptr);
@@ -231,17 +244,23 @@
}
if (mSurfaceControl != nullptr) {
- funcs.unregisterListenerFunc(this, &onSurfaceStatsAvailable);
- funcs.releaseFunc(mSurfaceControl);
+ TransactionCompletedListener::getInstance()->removeSurfaceStatsListener(
+ this, reinterpret_cast<void*>(onSurfaceStatsAvailable));
}
- mSurfaceControl = surfaceControl;
+
+ mSurfaceControl = std::move(surfaceControl);
mSurfaceControlGenerationId++;
- mExpectSurfaceStats = surfaceControl != nullptr;
+ mExpectSurfaceStats = mSurfaceControl != nullptr;
if (mExpectSurfaceStats) {
- funcs.acquireFunc(mSurfaceControl);
- funcs.registerListenerFunc(surfaceControl, mSurfaceControlGenerationId, this,
- &onSurfaceStatsAvailable);
+ SurfaceStatsCallback callback = [generationId = mSurfaceControlGenerationId](
+ void* callback_context, nsecs_t, const sp<Fence>&,
+ const SurfaceStats& surfaceStats) {
+ onSurfaceStatsAvailable(callback_context, generationId, surfaceStats);
+ };
+ TransactionCompletedListener::getInstance()->addSurfaceStatsListener(
+ this, reinterpret_cast<void*>(onSurfaceStatsAvailable), mSurfaceControl, callback);
}
+#endif
}
void CanvasContext::setupPipelineSurface() {
@@ -896,17 +915,26 @@
}
void CanvasContext::onSurfaceStatsAvailable(void* context, int32_t surfaceControlId,
- ASurfaceControlStats* stats) {
+ const SurfaceStats& stats) {
+#ifdef __ANDROID__
auto* instance = static_cast<CanvasContext*>(context);
- const ASurfaceControlFunctions& functions =
- instance->mRenderThread.getASurfaceControlFunctions();
+ nsecs_t gpuCompleteTime = -1L;
+ if (const auto* fence = std::get_if<sp<Fence>>(&stats.acquireTimeOrFence)) {
+ // We got a fence instead of the acquire time due to latching unsignaled.
+ // Ideally the client could just get the acquire time directly from
+ // the fence instead of calling this function which needs to block.
+ (*fence)->waitForever("acquireFence");
+ gpuCompleteTime = (*fence)->getSignalTime();
+ } else {
+ gpuCompleteTime = std::get<int64_t>(stats.acquireTimeOrFence);
+ }
- nsecs_t gpuCompleteTime = functions.getAcquireTimeFunc(stats);
if (gpuCompleteTime == Fence::SIGNAL_TIME_PENDING) {
gpuCompleteTime = -1;
}
- uint64_t frameNumber = functions.getFrameNumberFunc(stats);
+
+ uint64_t frameNumber = stats.eventStats.frameNumber;
FrameInfo* frameInfo = instance->getFrameInfoFromLastFew(frameNumber, surfaceControlId);
@@ -919,6 +947,7 @@
instance->mJankTracker.finishFrame(*frameInfo, instance->mFrameMetricsReporter, frameNumber,
surfaceControlId);
}
+#endif
}
// Called by choreographer to do an RT-driven animation
@@ -1140,10 +1169,11 @@
return ScopedActiveContext::getActiveContext();
}
-bool CanvasContext::mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control) {
+bool CanvasContext::mergeTransaction(ASurfaceTransaction* transaction,
+ const sp<SurfaceControl>& control) {
if (!mASurfaceTransactionCallback) return false;
return std::invoke(mASurfaceTransactionCallback, reinterpret_cast<int64_t>(transaction),
- reinterpret_cast<int64_t>(control), getFrameNumber());
+ reinterpret_cast<int64_t>(control.get()), getFrameNumber());
}
void CanvasContext::prepareSurfaceControlForWebview() {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 3de8e05..655aeba 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -50,6 +50,9 @@
#include "utils/RingBuffer.h"
namespace android {
+
+class SurfaceStats;
+
namespace uirenderer {
class AnimationContext;
@@ -121,7 +124,9 @@
*/
GrDirectContext* getGrContext() const { return mRenderThread.getGrContext(); }
- ASurfaceControl* getSurfaceControl() const { return mSurfaceControl; }
+#ifdef __ANDROID__
+ sp<SurfaceControl> getSurfaceControl() const;
+#endif
int32_t getSurfaceControlGenerationId() const { return mSurfaceControlGenerationId; }
// Won't take effect until next EGLSurface creation
@@ -129,7 +134,7 @@
void setHardwareBuffer(AHardwareBuffer* buffer);
void setSurface(ANativeWindow* window, bool enableTimeout = true);
- void setSurfaceControl(ASurfaceControl* surfaceControl);
+ void setSurfaceControl(sp<SurfaceControl> surfaceControl);
bool pauseSurface();
void setStopped(bool stopped);
bool isStopped() { return mStopped || !hasOutputTarget(); }
@@ -207,7 +212,7 @@
// Called when SurfaceStats are available.
static void onSurfaceStatsAvailable(void* context, int32_t surfaceControlId,
- ASurfaceControlStats* stats);
+ const SurfaceStats& stats);
void setASurfaceTransactionCallback(
const std::function<bool(int64_t, int64_t, int64_t)>& callback) {
@@ -218,7 +223,7 @@
mBufferParams = params;
}
- bool mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control);
+ bool mergeTransaction(ASurfaceTransaction* transaction, const sp<SurfaceControl>& control);
void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback) {
mPrepareSurfaceControlForWebviewCallback = callback;
@@ -286,7 +291,9 @@
std::unique_ptr<ReliableSurface> mNativeSurface;
// The SurfaceControl reference is passed from ViewRootImpl, can be set to
// NULL to remove the reference
- ASurfaceControl* mSurfaceControl = nullptr;
+#ifdef __ANDROID__
+ sp<SurfaceControl> mSurfaceControl = nullptr;
+#endif
// id to track surface control changes and WebViewFunctor uses it to determine
// whether reparenting is needed also used by FrameMetricsReporter to determine
// if a frame is from an "old" surface (i.e. one that existed before the
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index ebfd8fde9..e4be5fa 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -21,6 +21,11 @@
#include <SkPicture.h>
#include <gui/TraceUtils.h>
#include <pthread.h>
+
+#ifdef __ANDROID__
+#include <gui/SurfaceControl.h>
+#endif
+
#include <ui/GraphicBufferAllocator.h>
#include "DeferredLayerUpdater.h"
@@ -115,17 +120,12 @@
});
}
-void RenderProxy::setSurfaceControl(ASurfaceControl* surfaceControl) {
- auto funcs = mRenderThread.getASurfaceControlFunctions();
- if (surfaceControl) {
- funcs.acquireFunc(surfaceControl);
- }
- mRenderThread.queue().post([this, control = surfaceControl, funcs]() mutable {
- mContext->setSurfaceControl(control);
- if (control) {
- funcs.releaseFunc(control);
- }
+void RenderProxy::setSurfaceControl(sp<SurfaceControl> surfaceControl) {
+#ifdef __ANDROID__
+ mRenderThread.queue().post([this, control = std::move(surfaceControl)]() mutable {
+ mContext->setSurfaceControl(std::move(control));
});
+#endif
}
void RenderProxy::allocateBuffers() {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index ad6d54b..23b3ebd 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -20,7 +20,6 @@
#include <SkRefCnt.h>
#include <android/hardware_buffer.h>
#include <android/native_window.h>
-#include <android/surface_control.h>
#include <cutils/compiler.h>
#include <utils/Functor.h>
@@ -39,6 +38,7 @@
namespace android {
class GraphicBuffer;
+class SurfaceControl;
class Surface;
namespace uirenderer {
@@ -80,7 +80,7 @@
void setName(const char* name);
void setHardwareBuffer(AHardwareBuffer* buffer);
void setSurface(ANativeWindow* window, bool enableTimeout = true);
- void setSurfaceControl(ASurfaceControl* surfaceControl);
+ void setSurfaceControl(sp<SurfaceControl> surfaceControl);
void allocateBuffers();
bool pause();
void setStopped(bool stopped);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 6ab8e4e..5e40424 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -55,66 +55,6 @@
static JVMAttachHook gOnStartHook = nullptr;
-ASurfaceControlFunctions::ASurfaceControlFunctions() {
- void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
- createFunc = (ASC_create)dlsym(handle_, "ASurfaceControl_create");
- LOG_ALWAYS_FATAL_IF(createFunc == nullptr,
- "Failed to find required symbol ASurfaceControl_create!");
-
- acquireFunc = (ASC_acquire) dlsym(handle_, "ASurfaceControl_acquire");
- LOG_ALWAYS_FATAL_IF(acquireFunc == nullptr,
- "Failed to find required symbol ASurfaceControl_acquire!");
-
- releaseFunc = (ASC_release) dlsym(handle_, "ASurfaceControl_release");
- LOG_ALWAYS_FATAL_IF(releaseFunc == nullptr,
- "Failed to find required symbol ASurfaceControl_release!");
-
- registerListenerFunc = (ASC_registerSurfaceStatsListener) dlsym(handle_,
- "ASurfaceControl_registerSurfaceStatsListener");
- LOG_ALWAYS_FATAL_IF(registerListenerFunc == nullptr,
- "Failed to find required symbol ASurfaceControl_registerSurfaceStatsListener!");
-
- unregisterListenerFunc = (ASC_unregisterSurfaceStatsListener) dlsym(handle_,
- "ASurfaceControl_unregisterSurfaceStatsListener");
- LOG_ALWAYS_FATAL_IF(unregisterListenerFunc == nullptr,
- "Failed to find required symbol ASurfaceControl_unregisterSurfaceStatsListener!");
-
- getAcquireTimeFunc = (ASCStats_getAcquireTime) dlsym(handle_,
- "ASurfaceControlStats_getAcquireTime");
- LOG_ALWAYS_FATAL_IF(getAcquireTimeFunc == nullptr,
- "Failed to find required symbol ASurfaceControlStats_getAcquireTime!");
-
- getFrameNumberFunc = (ASCStats_getFrameNumber) dlsym(handle_,
- "ASurfaceControlStats_getFrameNumber");
- LOG_ALWAYS_FATAL_IF(getFrameNumberFunc == nullptr,
- "Failed to find required symbol ASurfaceControlStats_getFrameNumber!");
-
- transactionCreateFunc = (AST_create)dlsym(handle_, "ASurfaceTransaction_create");
- LOG_ALWAYS_FATAL_IF(transactionCreateFunc == nullptr,
- "Failed to find required symbol ASurfaceTransaction_create!");
-
- transactionDeleteFunc = (AST_delete)dlsym(handle_, "ASurfaceTransaction_delete");
- LOG_ALWAYS_FATAL_IF(transactionDeleteFunc == nullptr,
- "Failed to find required symbol ASurfaceTransaction_delete!");
-
- transactionApplyFunc = (AST_apply)dlsym(handle_, "ASurfaceTransaction_apply");
- LOG_ALWAYS_FATAL_IF(transactionApplyFunc == nullptr,
- "Failed to find required symbol ASurfaceTransaction_apply!");
-
- transactionReparentFunc = (AST_reparent)dlsym(handle_, "ASurfaceTransaction_reparent");
- LOG_ALWAYS_FATAL_IF(transactionReparentFunc == nullptr,
- "Failed to find required symbol transactionReparentFunc!");
-
- transactionSetVisibilityFunc =
- (AST_setVisibility)dlsym(handle_, "ASurfaceTransaction_setVisibility");
- LOG_ALWAYS_FATAL_IF(transactionSetVisibilityFunc == nullptr,
- "Failed to find required symbol ASurfaceTransaction_setVisibility!");
-
- transactionSetZOrderFunc = (AST_setZOrder)dlsym(handle_, "ASurfaceTransaction_setZOrder");
- LOG_ALWAYS_FATAL_IF(transactionSetZOrderFunc == nullptr,
- "Failed to find required symbol ASurfaceTransaction_setZOrder!");
-}
-
void RenderThread::extendedFrameCallback(const AChoreographerFrameCallbackData* cbData,
void* data) {
RenderThread* rt = reinterpret_cast<RenderThread*>(data);
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 86fddba..f733c7c 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -77,49 +77,6 @@
virtual ~VsyncSource() {}
};
-typedef ASurfaceControl* (*ASC_create)(ASurfaceControl* parent, const char* debug_name);
-typedef void (*ASC_acquire)(ASurfaceControl* control);
-typedef void (*ASC_release)(ASurfaceControl* control);
-
-typedef void (*ASC_registerSurfaceStatsListener)(ASurfaceControl* control, int32_t id,
- void* context,
- ASurfaceControl_SurfaceStatsListener func);
-typedef void (*ASC_unregisterSurfaceStatsListener)(void* context,
- ASurfaceControl_SurfaceStatsListener func);
-
-typedef int64_t (*ASCStats_getAcquireTime)(ASurfaceControlStats* stats);
-typedef uint64_t (*ASCStats_getFrameNumber)(ASurfaceControlStats* stats);
-
-typedef ASurfaceTransaction* (*AST_create)();
-typedef void (*AST_delete)(ASurfaceTransaction* transaction);
-typedef void (*AST_apply)(ASurfaceTransaction* transaction);
-typedef void (*AST_reparent)(ASurfaceTransaction* aSurfaceTransaction,
- ASurfaceControl* aSurfaceControl,
- ASurfaceControl* newParentASurfaceControl);
-typedef void (*AST_setVisibility)(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, int8_t visibility);
-typedef void (*AST_setZOrder)(ASurfaceTransaction* transaction, ASurfaceControl* surface_control,
- int32_t z_order);
-
-struct ASurfaceControlFunctions {
- ASurfaceControlFunctions();
-
- ASC_create createFunc;
- ASC_acquire acquireFunc;
- ASC_release releaseFunc;
- ASC_registerSurfaceStatsListener registerListenerFunc;
- ASC_unregisterSurfaceStatsListener unregisterListenerFunc;
- ASCStats_getAcquireTime getAcquireTimeFunc;
- ASCStats_getFrameNumber getFrameNumberFunc;
-
- AST_create transactionCreateFunc;
- AST_delete transactionDeleteFunc;
- AST_apply transactionApplyFunc;
- AST_reparent transactionReparentFunc;
- AST_setVisibility transactionSetVisibilityFunc;
- AST_setZOrder transactionSetZOrderFunc;
-};
-
class ChoreographerSource;
class DummyVsyncSource;
@@ -166,10 +123,6 @@
void preload();
- const ASurfaceControlFunctions& getASurfaceControlFunctions() {
- return mASurfaceControlFunctions;
- }
-
void trimMemory(TrimLevel level);
void trimCaches(CacheTrimLevel level);
@@ -244,7 +197,6 @@
CacheManager* mCacheManager;
sp<VulkanManager> mVkManager;
- ASurfaceControlFunctions mASurfaceControlFunctions;
std::mutex mJankDataMutex;
};
diff --git a/media/java/android/media/AudioDeviceVolumeManager.java b/media/java/android/media/AudioDeviceVolumeManager.java
index 56d3df3..311d64f 100644
--- a/media/java/android/media/AudioDeviceVolumeManager.java
+++ b/media/java/android/media/AudioDeviceVolumeManager.java
@@ -29,6 +29,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
@@ -52,6 +53,127 @@
private static final String TAG = "AudioDeviceVolumeManager";
+ /**
+ * @hide
+ * Volume behavior for an audio device that has no particular volume behavior set. Invalid as
+ * an argument to {@link #setDeviceVolumeBehavior(AudioDeviceAttributes, int)} and should not
+ * be returned by {@link #getDeviceVolumeBehavior(AudioDeviceAttributes)}.
+ */
+ public static final int DEVICE_VOLUME_BEHAVIOR_UNSET = -1;
+ /**
+ * @hide
+ * Volume behavior for an audio device where a software attenuation is applied
+ * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0;
+ /**
+ * @hide
+ * Volume behavior for an audio device where the volume is always set to provide no attenuation
+ * nor gain (e.g. unit gain).
+ * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1;
+ /**
+ * @hide
+ * Volume behavior for an audio device where the volume is either set to muted, or to provide
+ * no attenuation nor gain (e.g. unit gain).
+ * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2;
+ /**
+ * @hide
+ * Volume behavior for an audio device where no software attenuation is applied, and
+ * the volume is kept synchronized between the host and the device itself through a
+ * device-specific protocol such as BT AVRCP.
+ * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3;
+ /**
+ * @hide
+ * Volume behavior for an audio device where no software attenuation is applied, and
+ * the volume is kept synchronized between the host and the device itself through a
+ * device-specific protocol (such as for hearing aids), based on the audio mode (e.g.
+ * normal vs in phone call).
+ * @see AudioManager#setMode(int)
+ * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4;
+
+ /**
+ * @hide
+ * A variant of {@link #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE} where the host cannot reliably set
+ * the volume percentage of the audio device. Specifically, {@link AudioManager#setStreamVolume}
+ * will have no effect, or an unreliable effect.
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5;
+
+ /** @hide */
+ @IntDef({
+ DEVICE_VOLUME_BEHAVIOR_VARIABLE,
+ DEVICE_VOLUME_BEHAVIOR_FULL,
+ DEVICE_VOLUME_BEHAVIOR_FIXED,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeviceVolumeBehavior {}
+
+ /** @hide */
+ @IntDef({
+ DEVICE_VOLUME_BEHAVIOR_UNSET,
+ DEVICE_VOLUME_BEHAVIOR_VARIABLE,
+ DEVICE_VOLUME_BEHAVIOR_FULL,
+ DEVICE_VOLUME_BEHAVIOR_FIXED,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeviceVolumeBehaviorState {}
+
+ /**
+ * Variants of absolute volume behavior that are set in for absolute volume management.
+ * @hide
+ */
+ @IntDef({
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AbsoluteDeviceVolumeBehavior {}
+
+ /**
+ * @hide
+ * Throws IAE on an invalid volume behavior value
+ * @param volumeBehavior behavior value to check
+ */
+ public static void enforceValidVolumeBehavior(int volumeBehavior) {
+ switch (volumeBehavior) {
+ case DEVICE_VOLUME_BEHAVIOR_VARIABLE:
+ case DEVICE_VOLUME_BEHAVIOR_FULL:
+ case DEVICE_VOLUME_BEHAVIOR_FIXED:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
+ return;
+ default:
+ throw new IllegalArgumentException("Illegal volume behavior " + volumeBehavior);
+ }
+ }
+
/** @hide
* Indicates no special treatment in the handling of the volume adjustment */
public static final int ADJUST_MODE_NORMAL = 0;
@@ -158,7 +280,7 @@
android.Manifest.permission.BLUETOOTH_PRIVILEGED })
public void register(boolean register, @NonNull AudioDeviceAttributes device,
@NonNull List<VolumeInfo> volumes, boolean handlesVolumeAdjustment,
- @AudioManager.AbsoluteDeviceVolumeBehavior int behavior) {
+ @AbsoluteDeviceVolumeBehavior int behavior) {
try {
getService().registerDeviceVolumeDispatcherForAbsoluteVolume(register,
this, mPackageName,
@@ -204,6 +326,94 @@
/**
* @hide
+ * Sets the volume behavior for an audio output device.
+ * @see #DEVICE_VOLUME_BEHAVIOR_VARIABLE
+ * @see #DEVICE_VOLUME_BEHAVIOR_FULL
+ * @see #DEVICE_VOLUME_BEHAVIOR_FIXED
+ * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE
+ * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE
+ * @param device the device to be affected
+ * @param deviceVolumeBehavior one of the device behaviors
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ })
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
+ @DeviceVolumeBehavior int deviceVolumeBehavior) {
+ // verify arguments (validity of device type is enforced in server)
+ Objects.requireNonNull(device);
+ enforceValidVolumeBehavior(deviceVolumeBehavior);
+ // communicate with service
+ final IAudioService service = getService();
+ try {
+ service.setDeviceVolumeBehavior(device, deviceVolumeBehavior, mPackageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Returns the volume device behavior for the given audio device
+ * @param device the audio device
+ * @return the volume behavior for the device
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE,
+ Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ })
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public @DeviceVolumeBehavior int getDeviceVolumeBehavior(
+ @NonNull AudioDeviceAttributes device) {
+ // verify arguments (validity of device type is enforced in server)
+ Objects.requireNonNull(device);
+ // communicate with service
+ final IAudioService service = getService();
+ try {
+ return service.getDeviceVolumeBehavior(device);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Returns {@code true} if the volume device behavior is {@link #DEVICE_VOLUME_BEHAVIOR_FULL}.
+ */
+ @TestApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE,
+ Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ })
+ @SuppressWarnings("UnflaggedApi") // @TestApi without associated feature.
+ public boolean isFullVolumeDevice() {
+ final AudioAttributes attributes = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA)
+ .build();
+ List<AudioDeviceAttributes> devices;
+ final IAudioService service = getService();
+ try {
+ devices = service.getDevicesForAttributes(attributes);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ for (AudioDeviceAttributes device : devices) {
+ if (getDeviceVolumeBehavior(device) == DEVICE_VOLUME_BEHAVIOR_FULL) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @hide
* Configures a device to use absolute volume model, and registers a listener for receiving
* volume updates to apply on that device
* @param device the audio device set to absolute volume mode
@@ -297,7 +507,7 @@
@NonNull @CallbackExecutor Executor executor,
@NonNull OnAudioDeviceVolumeChangedListener vclistener) {
baseSetDeviceAbsoluteMultiVolumeBehavior(device, volumes, executor, vclistener,
- handlesVolumeAdjustment, AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+ handlesVolumeAdjustment, DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
/**
@@ -355,12 +565,12 @@
@NonNull @CallbackExecutor Executor executor,
@NonNull OnAudioDeviceVolumeChangedListener vclistener) {
baseSetDeviceAbsoluteMultiVolumeBehavior(device, volumes, executor, vclistener,
- handlesVolumeAdjustment, AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ handlesVolumeAdjustment, DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
}
/**
* Base method for configuring a device to use absolute volume behavior, or one of its variants.
- * See {@link AudioManager.AbsoluteDeviceVolumeBehavior} for a list of allowed behaviors.
+ * See {@link AbsoluteDeviceVolumeBehavior} for a list of allowed behaviors.
*
* @param behavior the variant of absolute device volume behavior to adopt
*/
@@ -372,7 +582,7 @@
@NonNull @CallbackExecutor Executor executor,
@NonNull OnAudioDeviceVolumeChangedListener vclistener,
boolean handlesVolumeAdjustment,
- @AudioManager.AbsoluteDeviceVolumeBehavior int behavior) {
+ @AbsoluteDeviceVolumeBehavior int behavior) {
Objects.requireNonNull(device);
Objects.requireNonNull(volumes);
Objects.requireNonNull(executor);
@@ -417,7 +627,7 @@
*/
void onDeviceVolumeBehaviorChanged(
@NonNull AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int volumeBehavior);
+ @DeviceVolumeBehavior int volumeBehavior);
}
/**
@@ -580,19 +790,19 @@
* @param behavior one of the volume behaviors defined in AudioManager
* @return a string for the given behavior
*/
- public static String volumeBehaviorName(@AudioManager.DeviceVolumeBehavior int behavior) {
+ public static String volumeBehaviorName(@DeviceVolumeBehavior int behavior) {
switch (behavior) {
- case AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE:
+ case DEVICE_VOLUME_BEHAVIOR_VARIABLE:
return "DEVICE_VOLUME_BEHAVIOR_VARIABLE";
- case AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL:
+ case DEVICE_VOLUME_BEHAVIOR_FULL:
return "DEVICE_VOLUME_BEHAVIOR_FULL";
- case AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED:
+ case DEVICE_VOLUME_BEHAVIOR_FIXED:
return "DEVICE_VOLUME_BEHAVIOR_FIXED";
- case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE";
- case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE";
- case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY";
default:
return "invalid volume behavior " + behavior;
@@ -611,7 +821,7 @@
@Override
public void dispatchDeviceVolumeBehaviorChanged(@NonNull AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int volumeBehavior) {
+ @DeviceVolumeBehavior int volumeBehavior) {
mDeviceVolumeBehaviorChangedListenerMgr.callListeners((listener) ->
listener.onDeviceVolumeBehaviorChanged(device, volumeBehavior));
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 32af7c6..4aba491 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.FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT;
import static android.media.audio.Flags.autoPublicVolumeApiHardening;
import static android.media.audio.Flags.cacheGetStreamMinMaxVolume;
import static android.media.audio.Flags.cacheGetStreamVolume;
@@ -6659,24 +6660,30 @@
* @hide
* Volume behavior for an audio device where a software attenuation is applied
* @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ * @deprecated use {@link AudioDeviceVolumeManager#DEVICE_VOLUME_BEHAVIOR_VARIABLE} instead
*/
@SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0;
/**
* @hide
* Volume behavior for an audio device where the volume is always set to provide no attenuation
* nor gain (e.g. unit gain).
* @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ * @deprecated use {@link AudioDeviceVolumeManager#DEVICE_VOLUME_BEHAVIOR_FULL} instead
*/
@SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1;
/**
* @hide
* Volume behavior for an audio device where the volume is either set to muted, or to provide
* no attenuation nor gain (e.g. unit gain).
* @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ * @deprecated use {@link AudioDeviceVolumeManager#DEVICE_VOLUME_BEHAVIOR_FIXED} instead
*/
@SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2;
/**
* @hide
@@ -6684,8 +6691,10 @@
* the volume is kept synchronized between the host and the device itself through a
* device-specific protocol such as BT AVRCP.
* @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ * @deprecated use {@link AudioDeviceVolumeManager#DEVICE_VOLUME_BEHAVIOR_ABSOLUTE} instead
*/
@SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3;
/**
* @hide
@@ -6695,8 +6704,11 @@
* normal vs in phone call).
* @see #setMode(int)
* @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ * @deprecated use {@link AudioDeviceVolumeManager#DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE}
+ * instead
*/
@SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4;
/**
@@ -6704,8 +6716,11 @@
* A variant of {@link #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE} where the host cannot reliably set
* the volume percentage of the audio device. Specifically, {@link #setStreamVolume} will have
* no effect, or an unreliable effect.
+ * @deprecated use {@link AudioDeviceVolumeManager#DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY}
+ * instead
*/
@SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5;
/** @hide */
@@ -6720,49 +6735,6 @@
@Retention(RetentionPolicy.SOURCE)
public @interface DeviceVolumeBehavior {}
- /** @hide */
- @IntDef({
- DEVICE_VOLUME_BEHAVIOR_UNSET,
- DEVICE_VOLUME_BEHAVIOR_VARIABLE,
- DEVICE_VOLUME_BEHAVIOR_FULL,
- DEVICE_VOLUME_BEHAVIOR_FIXED,
- DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
- DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
- DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface DeviceVolumeBehaviorState {}
-
- /**
- * Variants of absolute volume behavior that are set in {@link AudioDeviceVolumeManager}.
- * @hide
- */
- @IntDef({
- DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
- DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface AbsoluteDeviceVolumeBehavior {}
-
- /**
- * @hide
- * Throws IAE on an invalid volume behavior value
- * @param volumeBehavior behavior value to check
- */
- public static void enforceValidVolumeBehavior(int volumeBehavior) {
- switch (volumeBehavior) {
- case DEVICE_VOLUME_BEHAVIOR_VARIABLE:
- case DEVICE_VOLUME_BEHAVIOR_FULL:
- case DEVICE_VOLUME_BEHAVIOR_FIXED:
- case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
- case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
- case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
- return;
- default:
- throw new IllegalArgumentException("Illegal volume behavior " + volumeBehavior);
- }
- }
-
/**
* @hide
* Sets the volume behavior for an audio output device.
@@ -6773,17 +6745,21 @@
* @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE
* @param device the device to be affected
* @param deviceVolumeBehavior one of the device behaviors
+ *
+ * @deprecated use
+ * {@link AudioDeviceVolumeManager#setDeviceVolumeBehavior(AudioDeviceAttributes, int)} instead
*/
@SystemApi
@RequiresPermission(anyOf = {
Manifest.permission.MODIFY_AUDIO_ROUTING,
Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
})
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
@DeviceVolumeBehavior int deviceVolumeBehavior) {
// verify arguments (validity of device type is enforced in server)
Objects.requireNonNull(device);
- enforceValidVolumeBehavior(deviceVolumeBehavior);
+ AudioDeviceVolumeManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
// communicate with service
final IAudioService service = getService();
try {
@@ -6810,6 +6786,8 @@
* Returns the volume device behavior for the given audio device
* @param device the audio device
* @return the volume behavior for the device
+ * @deprecated use
+ * {@link AudioDeviceVolumeManager#getDeviceVolumeBehavior(AudioDeviceAttributes)} instead
*/
@SystemApi
@RequiresPermission(anyOf = {
@@ -6817,6 +6795,7 @@
Manifest.permission.QUERY_AUDIO_STATE,
Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
})
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public @DeviceVolumeBehavior
int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
// verify arguments (validity of device type is enforced in server)
@@ -6836,29 +6815,6 @@
}
/**
- * @hide
- * Returns {@code true} if the volume device behavior is {@link #DEVICE_VOLUME_BEHAVIOR_FULL}.
- */
- @TestApi
- @RequiresPermission(anyOf = {
- Manifest.permission.MODIFY_AUDIO_ROUTING,
- Manifest.permission.QUERY_AUDIO_STATE,
- Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
- })
- public boolean isFullVolumeDevice() {
- final AudioAttributes attributes = new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_MEDIA)
- .build();
- final List<AudioDeviceAttributes> devices = getDevicesForAttributes(attributes);
- for (AudioDeviceAttributes device : devices) {
- if (getDeviceVolumeBehavior(device) == DEVICE_VOLUME_BEHAVIOR_FULL) {
- return true;
- }
- }
- return false;
- }
-
- /**
* Indicate wired accessory connection state change.
* @param device type of device connected/disconnected (AudioManager.DEVICE_OUT_xxx)
* @param state new connection state: 1 connected, 0 disconnected
diff --git a/packages/SettingsLib/StatusBannerPreference/res/drawable/settingslib_expressive_icon_status_level_off.xml b/packages/SettingsLib/StatusBannerPreference/res/drawable/settingslib_expressive_icon_status_level_off.xml
new file mode 100644
index 0000000..6b534aa
--- /dev/null
+++ b/packages/SettingsLib/StatusBannerPreference/res/drawable/settingslib_expressive_icon_status_level_off.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="34dp"
+ android:height="42dp"
+ android:viewportWidth="34"
+ android:viewportHeight="42">
+ <path
+ android:pathData="M0.856,17.569C0.887,19.083 1.004,20.593 1.206,22.094C2.166,28.584 5.804,35.937 15.774,41.089C16.171,41.293 16.61,41.4 17.056,41.4C17.503,41.4 17.942,41.293 18.339,41.089C28.309,35.936 31.947,28.583 32.907,22.093C33.109,20.593 33.226,19.083 33.256,17.569V8.605C33.257,7.919 33.046,7.25 32.652,6.688C32.259,6.127 31.703,5.7 31.059,5.467L18.191,0.8C17.458,0.534 16.655,0.534 15.922,0.8L3.054,5.467C2.41,5.7 1.854,6.127 1.461,6.688C1.067,7.25 0.856,7.919 0.856,8.605V17.569Z"
+ android:fillColor="#D1C2CB"/>
+ <path
+ android:pathData="M15.067,24.333V10.733H18.933V24.333H15.067ZM15.067,31.267V27.433H18.933V31.267H15.067Z"
+ android:fillColor="@color/settingslib_materialColorSurfaceContainerLowest"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml b/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml
index 54860d4..deda258 100644
--- a/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml
+++ b/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml
@@ -21,6 +21,7 @@
<enum name="low" value="1"/>
<enum name="medium" value="2"/>
<enum name="high" value="3"/>
+ <enum name="off" value="4"/>
</attr>
<attr name="buttonLevel" format="enum">
<enum name="generic" value="0"/>
diff --git a/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml b/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml
index 19181dd..abc458b 100644
--- a/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml
+++ b/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml
@@ -22,4 +22,5 @@
<color name="settingslib_expressive_color_status_level_medium">#FCBD00</color>
<!-- static palette red50 -->
<color name="settingslib_expressive_color_status_level_high">#DB372D</color>
+ <color name="settingslib_expressive_color_status_level_off">#D1C2CB</color>
</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt b/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt
index 1f8cfb5..eda281c 100644
--- a/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt
+++ b/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt
@@ -40,7 +40,8 @@
GENERIC,
LOW,
MEDIUM,
- HIGH
+ HIGH,
+ OFF
}
var iconLevel: BannerStatus = BannerStatus.GENERIC
set(value) {
@@ -87,6 +88,7 @@
1 -> BannerStatus.LOW
2 -> BannerStatus.MEDIUM
3 -> BannerStatus.HIGH
+ 4 -> BannerStatus.OFF
else -> BannerStatus.GENERIC
}
@@ -104,7 +106,10 @@
}
(holder.findViewById(R.id.status_banner_button) as? MaterialButton)?.apply {
- setBackgroundColor(getBackgroundColor(buttonLevel))
+ setBackgroundColor(
+ if (buttonLevel == BannerStatus.OFF) getBackgroundColor(BannerStatus.GENERIC)
+ else getBackgroundColor(buttonLevel)
+ )
text = buttonText
setOnClickListener(listener)
visibility = if (listener != null) View.VISIBLE else View.GONE
@@ -143,6 +148,11 @@
R.color.settingslib_expressive_color_status_level_high
)
+ BannerStatus.OFF -> ContextCompat.getColor(
+ context,
+ R.color.settingslib_expressive_color_status_level_off
+ )
+
else -> ContextCompat.getColor(
context,
com.android.settingslib.widget.theme.R.color.settingslib_materialColorPrimary
@@ -167,6 +177,11 @@
R.drawable.settingslib_expressive_icon_status_level_high
)
+ BannerStatus.OFF -> ContextCompat.getDrawable(
+ context,
+ R.drawable.settingslib_expressive_icon_status_level_off
+ )
+
else -> null
}
}
@@ -188,6 +203,7 @@
R.drawable.settingslib_expressive_background_level_high
)
+ // GENERIC and OFF are using the same background drawable.
else -> ContextCompat.getDrawable(
context,
R.drawable.settingslib_expressive_background_generic
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 55f7317..758ad79 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -1015,6 +1015,10 @@
<uses-permission android:name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE" />
<uses-permission android:name="android.permission.READ_COLOR_ZONES" />
+ <!-- Permissions required for CTS test - CtsModernMediaProviderTests -->
+ <uses-permission android:name="com.android.providers.media.permission.ACCESS_OEM_METADATA" />
+ <uses-permission android:name="com.android.providers.media.permission.UPDATE_OEM_METADATA" />
+
<!-- Permission required for trade-in mode testing -->
<uses-permission android:name="android.permission.ENTER_TRADE_IN_MODE" />
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 1362ffe..86559fd 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -1,22 +1,6 @@
{
- // Curious where your @Scenario tests are running?
- //
- // @Ignore: Will not run in any configuration
- //
- // @FlakyTest: Tests that don't block pre/postsubmit but are staged to run known failures.
- // Tests will run in postsubmit on sysui-e2e-staged suite.
- //
- //
- // @PlatinumTest: Marking your test with this annotation will put your tests in presubmit.
- // Please DO NOT annotate new or old tests with @PlatinumTest annotation
- // without discussing with mdb:android-platinum
- //
- // @Postsubmit: Do not use this annotation for e2e tests. This won't have any affect.
-
- // For all other e2e tests which are not platinum, they run in sysui-silver suite,that
- // primarily runs in postsubmit with an exception to e2e test related changes.
- // If you want to see one shot place to monitor all e2e tests, look for
- // sysui-e2e-staged suite.
+ // Test mappings for SystemUI unit tests.
+ // For e2e mappings, see go/sysui-e2e-test-mapping
// v2/android-virtual-infra/test_mapping/presubmit-avd
"presubmit": [
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 4693377..436e92b 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -398,13 +398,6 @@
}
flag {
- name: "light_reveal_migration"
- namespace: "systemui"
- description: "Move LightRevealScrim to recommended architecture"
- bug: "281655028"
-}
-
-flag {
name: "theme_overlay_controller_wakefulness_deprecation"
namespace: "systemui"
description: "Replacing WakefulnessLifecycle by KeyguardTransitionInteractor in "
@@ -2136,3 +2129,14 @@
description: "Enables return animations for status bar chips"
bug: "202516970"
}
+
+flag {
+ name: "media_projection_grey_error_text"
+ namespace: "systemui"
+ description: "Set the error text color to grey when app sharing is hidden by the requesting app"
+ bug: "390624334"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index 82e5f5b..cd9fcef 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -127,6 +127,8 @@
*
* @sample com.android.systemui.compose.gallery.ActivityLaunchScreen
* @sample com.android.systemui.compose.gallery.DialogLaunchScreen
+ * @param defaultMinSize true if a default minimum size should be enforced even if this Expandable
+ * isn't currently clickable and false otherwise.
*/
@Composable
fun Expandable(
@@ -140,6 +142,7 @@
// TODO(b/285250939): Default this to true then remove once the Compose QS expandables have
// proven that the new implementation is robust.
useModifierBasedImplementation: Boolean = false,
+ defaultMinSize: Boolean = true,
transitionControllerFactory: ComposableControllerFactory? = null,
content: @Composable (Expandable) -> Unit,
) {
@@ -155,6 +158,7 @@
onClick,
interactionSource,
useModifierBasedImplementation,
+ defaultMinSize,
content,
)
}
@@ -182,6 +186,8 @@
*
* @sample com.android.systemui.compose.gallery.ActivityLaunchScreen
* @sample com.android.systemui.compose.gallery.DialogLaunchScreen
+ * @param defaultMinSize true if a default minimum size should be enforced even if this Expandable
+ * isn't currently clickable and false otherwise.
*/
@Composable
fun Expandable(
@@ -192,6 +198,7 @@
// TODO(b/285250939): Default this to true then remove once the Compose QS expandables have
// proven that the new implementation is robust.
useModifierBasedImplementation: Boolean = false,
+ defaultMinSize: Boolean = true,
content: @Composable (Expandable) -> Unit,
) {
val controller = controller as ExpandableControllerImpl
@@ -209,7 +216,12 @@
if (useModifierBasedImplementation) {
Box(modifier.expandable(controller, onClick, interactionSource)) {
- WrappedContent(controller.expandable, controller.contentColor, content)
+ WrappedContent(
+ controller.expandable,
+ controller.contentColor,
+ defaultMinSize = defaultMinSize,
+ content,
+ )
}
return
}
@@ -221,7 +233,7 @@
val wrappedContent =
remember(content) {
movableContentOf { expandable: Expandable ->
- WrappedContent(expandable, contentColor, content)
+ WrappedContent(expandable, contentColor, defaultMinSize = defaultMinSize, content)
}
}
@@ -306,21 +318,24 @@
private fun WrappedContent(
expandable: Expandable,
contentColor: Color,
+ defaultMinSize: Boolean,
content: @Composable (Expandable) -> Unit,
) {
val minSizeContent =
@Composable {
- // We make sure that the content itself (wrapped by the background) is at least 40.dp,
- // which is the same as the M3 buttons. This applies even if onClick is null, to make it
- // easier to write expandables that are sometimes clickable and sometimes not. There
- // shouldn't be any Expandable smaller than 40dp because if the expandable is not
- // clickable directly, then something in its content should be (and with a size >=
- // 40dp).
- val minSize = 40.dp
- Box(
- Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
- contentAlignment = Alignment.Center,
- ) {
+ if (defaultMinSize) {
+ // We make sure that the content itself (wrapped by the background) is at
+ // least 40.dp, which is the same as the M3 buttons. This applies even if
+ // onClick is null, to make it easier to write expandables that are
+ // sometimes clickable and sometimes not.
+ val minSize = 40.dp
+ Box(
+ modifier = Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
+ contentAlignment = Alignment.Center,
+ ) {
+ content(expandable)
+ }
+ } else {
content(expandable)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 7782705..336f9e1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -38,6 +38,7 @@
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
@@ -81,6 +82,7 @@
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel
@@ -388,6 +390,7 @@
}
/** A larger button with an icon, some text and an optional dot (to indicate new changes). */
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun TextButton(
icon: Icon,
@@ -422,10 +425,13 @@
Text(
text,
Modifier.weight(1f),
- style = MaterialTheme.typography.bodyMedium,
- // TODO(b/242040009): Remove this letter spacing. We should only use the M3 text
- // styles without modifying them.
- letterSpacing = 0.01.em,
+ style =
+ if (QsInCompose.isEnabled) {
+ MaterialTheme.typography.labelLarge
+ } else {
+ MaterialTheme.typography.bodyMedium
+ },
+ letterSpacing = if (QsInCompose.isEnabled) 0.em else 0.01.em,
color = colorAttr(R.attr.onShadeInactiveVariant),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index 4b3ebc2..da54cb8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -58,6 +58,7 @@
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.disabled
import androidx.compose.ui.semantics.progressBarRangeInfo
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.setProgress
import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.unit.dp
@@ -106,7 +107,10 @@
return
}
- Column(modifier = modifier.animateContentSize(), verticalArrangement = Arrangement.Top) {
+ Column(
+ modifier = modifier.animateContentSize().semantics(true) {},
+ verticalArrangement = Arrangement.Top,
+ ) {
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier.fillMaxWidth().height(40.dp),
@@ -123,7 +127,7 @@
text = state.label,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface,
- modifier = Modifier.weight(1f),
+ modifier = Modifier.weight(1f).clearAndSetSemantics {},
)
button?.invoke()
}
@@ -134,12 +138,11 @@
onValueChanged = onValueChange,
onValueChangeFinished = { onValueChangeFinished?.invoke() },
isEnabled = state.isEnabled,
- stepDistance = state.a11yStep,
+ stepDistance = state.step,
accessibilityParams =
AccessibilityParams(
- label = state.label,
- disabledMessage = state.disabledMessage,
- currentStateDescription = state.a11yStateDescription,
+ contentDescription = state.a11yContentDescription,
+ stateDescription = state.a11yStateDescription,
),
haptics =
hapticsViewModelFactory?.let {
@@ -169,7 +172,7 @@
text = disabledMessage,
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.labelSmall,
- modifier = Modifier.basicMarquee(),
+ modifier = Modifier.basicMarquee().clearAndSetSemantics {},
)
}
}
@@ -229,7 +232,7 @@
}
val newValue =
- (value + targetDirection * state.a11yStep).coerceIn(
+ (value + targetDirection * state.step).coerceIn(
state.valueRange.start,
state.valueRange.endInclusive,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt
index 1d42424..8d50cdc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt
@@ -19,13 +19,17 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.FakeQSTile
import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.shade.domain.interactor.disableDualShade
+import com.android.systemui.shade.domain.interactor.enableDualShade
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -34,6 +38,7 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
+@EnableSceneContainer
class DetailsViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private lateinit var underTest: DetailsViewModel
@@ -45,10 +50,12 @@
underTest = kosmos.detailsViewModel
}
+ @OptIn(ExperimentalCoroutinesApi::class)
@Test
- fun changeTileDetailsViewModel() =
+ fun changeTileDetailsViewModelWithDualShadeEnabled() =
with(kosmos) {
testScope.runTest {
+ kosmos.enableDualShade()
val specs = listOf(spec, specNoDetails)
tileSpecRepository.setTiles(0, specs)
runCurrent()
@@ -85,4 +92,36 @@
assertThat(underTest.onTileClicked(null)).isFalse()
}
}
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun ignoreChangingTileDetailsViewModelWithDualShadeDisabled() =
+ with(kosmos) {
+ testScope.runTest {
+ kosmos.disableDualShade()
+ val specs = listOf(spec, specNoDetails)
+ tileSpecRepository.setTiles(0, specs)
+ runCurrent()
+
+ val tiles = currentTilesInteractor.currentTiles.value
+
+ assertThat(currentTilesInteractor.currentTilesSpecs.size).isEqualTo(2)
+ assertThat(tiles[1].spec).isEqualTo(specNoDetails)
+ (tiles[1].tile as FakeQSTile).hasDetailsViewModel = false
+
+ assertThat(underTest.activeTileDetails).isNull()
+
+ // Click on the tile who has the `spec`.
+ assertThat(underTest.onTileClicked(spec)).isFalse()
+ assertThat(underTest.activeTileDetails).isNull()
+
+ // Click on a tile who dose not have a valid spec.
+ assertThat(underTest.onTileClicked(null)).isFalse()
+ assertThat(underTest.activeTileDetails).isNull()
+
+ // Click on a tile who dose not have a detailed view.
+ assertThat(underTest.onTileClicked(specNoDetails)).isFalse()
+ assertThat(underTest.activeTileDetails).isNull()
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt
index 9faab58..1018748 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt
@@ -127,7 +127,7 @@
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isBubble() {
- assertThat(underTest.isBubbleCapable).isFalse()
+ assertThat(underTest.isBubble).isFalse()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
index 12ade62..7449064 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
@@ -110,7 +110,7 @@
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getRow_adapter() {
- val row = Mockito.mock(ExpandableNotificationRow::class.java)
+ val row = mock(ExpandableNotificationRow::class.java)
val notification: Notification =
Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
@@ -128,7 +128,7 @@
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isGroupRoot_adapter_groupSummary() {
- val row = Mockito.mock(ExpandableNotificationRow::class.java)
+ val row = mock(ExpandableNotificationRow::class.java)
val notification: Notification =
Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
@@ -175,7 +175,7 @@
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isClearable_adapter() {
- val row = Mockito.mock(ExpandableNotificationRow::class.java)
+ val row = mock(ExpandableNotificationRow::class.java)
val notification: Notification =
Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
@@ -193,7 +193,7 @@
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getSummarization_adapter() {
- val row = Mockito.mock(ExpandableNotificationRow::class.java)
+ val row = mock(ExpandableNotificationRow::class.java)
val notification: Notification =
Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
@@ -213,7 +213,7 @@
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getIcons_adapter() {
- val row = Mockito.mock(ExpandableNotificationRow::class.java)
+ val row = mock(ExpandableNotificationRow::class.java)
val notification: Notification =
Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
@@ -258,7 +258,7 @@
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun canDragAndDrop() {
- val pi = Mockito.mock(PendingIntent::class.java)
+ val pi = mock(PendingIntent::class.java)
Mockito.`when`(pi.isActivity).thenReturn(true)
val notification: Notification =
Notification.Builder(mContext, "")
@@ -284,7 +284,7 @@
val entry = NotificationEntryBuilder().setNotification(notification).build()
underTest = factory.create(entry) as NotificationEntryAdapter
- assertThat(underTest.isBubbleCapable).isEqualTo(entry.isBubble)
+ assertThat(underTest.isBubble).isEqualTo(entry.isBubble)
}
@Test
@@ -350,7 +350,7 @@
val notification: Notification =
Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
- .setFullScreenIntent(Mockito.mock(PendingIntent::class.java), true)
+ .setFullScreenIntent(mock(PendingIntent::class.java), true)
.build()
val entry =
@@ -399,7 +399,7 @@
val notification: Notification =
Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
- .addAction(Mockito.mock(Notification.Action::class.java))
+ .addAction(mock(Notification.Action::class.java))
.build()
val entry = NotificationEntryBuilder().setNotification(notification).build()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
index 893c179..4c099b3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
@@ -455,6 +455,7 @@
assertThat(content).isNotNull()
assertThat(content?.style).isEqualTo(Style.Call)
+ assertThat(content?.title).isEqualTo(TEST_PERSON_NAME)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index d306a5b..0d45335 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -51,6 +51,7 @@
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController.BUBBLES_SETTING_URI
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -318,7 +319,7 @@
val notification = Notification.Builder(mContext).build()
val sbn =
SbnBuilder().setNotification(notification).setUser(UserHandle.of(USER_ALL)).build()
- whenever(view.entry)
+ whenever(view.entryLegacy)
.thenReturn(
NotificationEntryBuilder().setSbn(sbn).setUser(UserHandle.of(USER_ALL)).build()
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 41cca19..6ec1f91 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -129,7 +129,7 @@
whenever(notificationShelf.viewState).thenReturn(ExpandableViewState())
whenever(notificationRow.key).thenReturn("key")
whenever(notificationRow.viewState).thenReturn(ExpandableViewState())
- whenever(notificationRow.entry).thenReturn(notificationEntry)
+ whenever(notificationRow.entryLegacy).thenReturn(notificationEntry)
whenever(notificationRow.entryAdapter).thenReturn(notificationEntryAdapter)
whenever(notificationRow.roundableState)
.thenReturn(RoundableState(notificationRow, notificationRow, 0f))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index c23e0e7..1cc2911 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -221,6 +221,7 @@
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.getEntryAdapter()).thenReturn(enrEntryAdapter);
when(enr.isChildInGroup()).thenReturn(true);
when(enr.areChildrenExpanded()).thenReturn(false);
@@ -251,6 +252,7 @@
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.isChildInGroup()).thenReturn(true);
when(enr.areChildrenExpanded()).thenReturn(true);
@@ -277,6 +279,7 @@
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.isChildInGroup()).thenReturn(false);
when(enr.isPinned()).thenReturn(false);
when(enr.isExpanded()).thenReturn(false);
@@ -305,6 +308,7 @@
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.isChildInGroup()).thenReturn(false);
when(enr.isPinned()).thenReturn(false);
when(enr.isExpanded()).thenReturn(true);
@@ -333,6 +337,7 @@
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.isChildInGroup()).thenReturn(false);
when(enr.isPinned()).thenReturn(true);
when(enr.isPinnedAndExpanded()).thenReturn(false);
@@ -361,6 +366,7 @@
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.isChildInGroup()).thenReturn(false);
when(enr.isPinned()).thenReturn(true);
when(enr.isPinnedAndExpanded()).thenReturn(true);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt
index 04ab988..b1a3caf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
import android.bluetooth.BluetoothDevice
+import android.graphics.drawable.TestStubDrawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.CachedBluetoothDevice
@@ -63,6 +64,12 @@
assertThat(audioSharingSlider!!.label).isEqualTo("my headset 2")
assertThat(audioSharingSlider!!.icon)
- .isEqualTo(Icon.Resource(R.drawable.ic_volume_media_bt, null))
+ .isEqualTo(
+ Icon.Loaded(
+ drawable = TestStubDrawable(),
+ res = R.drawable.ic_volume_media_bt,
+ contentDescription = null,
+ )
+ )
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
index 9e8cde3..ffe8e92 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
@@ -18,6 +18,7 @@
import android.app.Flags
import android.app.NotificationManager.INTERRUPTION_FILTER_NONE
+import android.graphics.drawable.TestStubDrawable
import android.media.AudioManager
import android.platform.test.annotations.EnableFlags
import android.service.notification.ZenPolicy
@@ -28,7 +29,6 @@
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.collectLastValue
@@ -39,8 +39,6 @@
import com.android.systemui.testKosmos
import com.android.systemui.volume.data.repository.audioSharingRepository
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
@@ -173,6 +171,12 @@
assertThat(mediaSlider!!.label).isEqualTo("my headset 1")
assertThat(mediaSlider!!.icon)
- .isEqualTo(Icon.Resource(R.drawable.ic_volume_media_bt, null))
+ .isEqualTo(
+ Icon.Loaded(
+ drawable = TestStubDrawable(),
+ res = R.drawable.ic_volume_media_bt,
+ contentDescription = null,
+ )
+ )
}
}
diff --git a/packages/SystemUI/res/drawable/hearing_device_ambient_expand_icon_background.xml b/packages/SystemUI/res/drawable/hearing_device_ambient_expand_icon_background.xml
new file mode 100644
index 0000000..2173641
--- /dev/null
+++ b/packages/SystemUI/res/drawable/hearing_device_ambient_expand_icon_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 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.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/hearing_device_ambient_icon_background"
+ android:insetTop="10dp"
+ android:insetBottom="10dp"/>
diff --git a/packages/SystemUI/res/drawable/hearing_device_ambient_icon_background.xml b/packages/SystemUI/res/drawable/hearing_device_ambient_icon_background.xml
new file mode 100644
index 0000000..81ef3d7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/hearing_device_ambient_icon_background.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHighest" />
+ <corners
+ android:bottomLeftRadius="?android:attr/dialogCornerRadius"
+ android:topLeftRadius="?android:attr/dialogCornerRadius"
+ android:bottomRightRadius="?android:attr/dialogCornerRadius"
+ android:topRightRadius="?android:attr/dialogCornerRadius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml b/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml
index 73704f8..f17cc96 100644
--- a/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml
+++ b/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml
@@ -21,7 +21,7 @@
android:height="2dp"
android:width="@dimen/rear_display_progress_width">
<shape android:shape="rectangle">
- <solid android:color="@androidprv:color/materialColorSurfaceContainer" />
+ <solid android:color="?android:attr/colorAccent"/>
</shape>
</item>
<item
@@ -29,4 +29,4 @@
android:gravity="center_vertical|fill_horizontal">
<com.android.systemui.util.RoundedCornerProgressDrawable android:drawable="@drawable/rear_display_dialog_seekbar_progress" />
</item>
-</layer-list>
\ No newline at end of file
+</layer-list>
diff --git a/packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml b/packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml
index fd409a5..d3e9db1 100644
--- a/packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml
+++ b/packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml
@@ -36,7 +36,8 @@
android:padding="12dp"
android:contentDescription="@string/hearing_devices_ambient_unmute"
android:src="@drawable/ic_ambient_volume"
- android:tint="@androidprv:color/materialColorOnSurface" />
+ android:tint="@androidprv:color/materialColorOnSurface"
+ android:background="@drawable/hearing_device_ambient_icon_background"/>
<TextView
android:id="@+id/ambient_title"
android:layout_width="0dp"
@@ -56,7 +57,8 @@
android:padding="10dp"
android:contentDescription="@string/hearing_devices_ambient_expand_controls"
android:src="@drawable/ic_hearing_device_expand"
- android:tint="@androidprv:color/materialColorOnSurface" />
+ android:tint="@androidprv:color/materialColorOnSurface"
+ android:background="@drawable/hearing_device_ambient_expand_icon_background"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ambient_control_container"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 15519ff..7c6a1b1 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -146,7 +146,8 @@
<color name="smart_reply_button_stroke">@*android:color/accent_device_default</color>
<!-- Magic Action colors -->
- <color name="magic_action_button_text_color">@androidprv:color/materialColorOnSurfaceVariant</color>
+ <color name="magic_action_button_text_color">@androidprv:color/materialColorOnSurface</color>
+ <color name="magic_action_button_stroke_color">@androidprv:color/materialColorOnSurface</color>
<!-- Biometric dialog colors -->
<color name="biometric_dialog_gray">#ff757575</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3fdb98b..b627bdf 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1483,6 +1483,8 @@
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen">Share screen</string>
<!-- 1P/3P apps disabled the single app projection option. [CHAR LIMIT=NONE] -->
<string name="media_projection_entry_app_permission_dialog_single_app_disabled"><xliff:g id="app_name" example="Meet">%1$s</xliff:g> has disabled this option</string>
+ <!-- Explanation that the app requesting the projection does not support single app sharing[CHAR LIMIT=70] -->
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported">Not supported by the app</string>
<!-- Title of the activity that allows users to select an app to share to a 1P/3P app [CHAR LIMIT=70] -->
<string name="media_projection_entry_share_app_selector_title">Choose app to share</string>
@@ -2558,6 +2560,9 @@
<!-- Button to edit the tile ordering of quick settings [CHAR LIMIT=60] -->
<string name="qs_edit">Edit</string>
+ <!-- Title for QS Edit mode screen [CHAR LIMIT=30] -->
+ <string name="qs_edit_tiles">Edit tiles</string>
+
<!-- SysUI Tuner: Options for how clock is displayed [CHAR LIMIT=NONE] -->
<string name="tuner_time">Time</string>
diff --git a/packages/SystemUI/shared/res/values/bools.xml b/packages/SystemUI/shared/res/values/bools.xml
index 98e5cea..a7ad48d 100644
--- a/packages/SystemUI/shared/res/values/bools.xml
+++ b/packages/SystemUI/shared/res/values/bools.xml
@@ -22,7 +22,4 @@
<resources>
<!-- Whether to add padding at the bottom of the complication clock -->
<bool name="dream_overlay_complication_clock_bottom_padding">false</bool>
-
- <!-- Whether to mark tasks that are present in the UI as perceptible tasks. -->
- <bool name="config_usePerceptibleTasks">false</bool>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 487d1ce..b981f98 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -46,8 +46,6 @@
import android.window.TaskSnapshot;
import com.android.internal.app.IVoiceInteractionManagerService;
-import com.android.server.am.Flags;
-import com.android.systemui.shared.R;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -324,14 +322,6 @@
}
/**
- * Returns true if tasks with a presence in the UI should be marked as perceptible tasks.
- */
- public static boolean usePerceptibleTasks(Context context) {
- return Flags.perceptibleTasks()
- && context.getResources().getBoolean(R.bool.config_usePerceptibleTasks);
- }
-
- /**
* Returns true if the running task represents the home task
*/
public static boolean isHomeTask(RunningTaskInfo info) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
index bd3dfe0..8025d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
@@ -317,14 +317,14 @@
public static float avoidVerticalDisplayCutout(
float y, float menuHeight, Rect bounds, Rect cutout) {
if (cutout.top > y + menuHeight || cutout.bottom < y) {
- return y;
+ return clampVerticalPosition(y, menuHeight, bounds.top, bounds.bottom);
}
boolean topAvailable = cutout.top - bounds.top >= menuHeight;
boolean bottomAvailable = bounds.bottom - cutout.bottom >= menuHeight;
boolean topOrBottom;
if (!topAvailable && !bottomAvailable) {
- return y;
+ return clampVerticalPosition(y, menuHeight, bounds.top, bounds.bottom);
} else if (topAvailable && !bottomAvailable) {
topOrBottom = true;
} else if (!topAvailable && bottomAvailable) {
@@ -332,7 +332,16 @@
} else {
topOrBottom = y + menuHeight * 0.5f < cutout.centerY();
}
- return (topOrBottom) ? cutout.top - menuHeight : cutout.bottom;
+
+ float finalPosition = (topOrBottom) ? cutout.top - menuHeight : cutout.bottom;
+ return clampVerticalPosition(finalPosition, menuHeight, bounds.top, bounds.bottom);
+ }
+
+ private static float clampVerticalPosition(
+ float position, float height, float min, float max) {
+ position = Float.max(min + height / 2, position);
+ position = Float.min(max - height / 2, position);
+ return position;
}
boolean isMenuOnLeftSide() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 22d2aaf..87e9784 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -32,7 +32,6 @@
import com.android.keyguard.logging.KeyguardLogger
import com.android.settingslib.Utils
import com.android.systemui.CoreStartable
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.dagger.SysUISingleton
@@ -43,6 +42,7 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
+import com.android.systemui.shared.Flags.ambientAod
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LiftReveal
import com.android.systemui.statusbar.LightRevealEffect
@@ -196,7 +196,7 @@
// This code path is not used if the KeyguardTransitionRepository is managing the light
// reveal scrim.
- if (!lightRevealMigration()) {
+ if (!ambientAod()) {
if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
circleReveal?.let {
lightRevealScrim.revealAmount = 0f
@@ -213,7 +213,7 @@
}
override fun onKeyguardFadingAwayChanged() {
- if (lightRevealMigration()) {
+ if (ambientAod()) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
index 2adaec2..6792f31 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
@@ -19,6 +19,7 @@
import android.annotation.DrawableRes
import android.graphics.drawable.Drawable
import androidx.compose.runtime.Stable
+import com.android.systemui.common.shared.model.Icon.Loaded
/**
* Models an icon, that can either be already [loaded][Icon.Loaded] or be a [reference]
@@ -33,8 +34,37 @@
constructor(
val drawable: Drawable,
override val contentDescription: ContentDescription?,
+ /**
+ * Serves as an id to compare two instances. When provided this is used alongside
+ * [contentDescription] to determine equality. This is useful when comparing icons
+ * representing the same UI, but with different [drawable] instances.
+ */
@DrawableRes val res: Int? = null,
- ) : Icon()
+ ) : Icon() {
+
+ override fun equals(other: Any?): Boolean {
+ val that = other as? Loaded ?: return false
+
+ if (this.res != null && that.res != null) {
+ return this.res == that.res && this.contentDescription == that.contentDescription
+ }
+
+ return this.res == that.res &&
+ this.drawable == that.drawable &&
+ this.contentDescription == that.contentDescription
+ }
+
+ override fun hashCode(): Int {
+ var result = contentDescription?.hashCode() ?: 0
+ result =
+ if (res != null) {
+ 31 * result + res.hashCode()
+ } else {
+ 31 * result + drawable.hashCode()
+ }
+ return result
+ }
+ }
data class Resource(
@DrawableRes val res: Int,
@@ -49,4 +79,4 @@
fun Drawable.asIcon(
contentDescription: ContentDescription? = null,
@DrawableRes res: Int? = null,
-): Icon.Loaded = Icon.Loaded(this, contentDescription, res)
+): Loaded = Loaded(this, contentDescription, res)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 756edb3..5a4b0b0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -229,7 +229,7 @@
MutableStateFlow(false)
}
- val blurRadiusPx: Float = blurConfig.maxBlurRadiusPx / 2.0f
+ val blurRadiusPx: Float = blurConfig.maxBlurRadiusPx
init {
// Initialize our media host for the UMO. This only needs to happen once and must be done
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index c61530c..74cf7e4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -20,7 +20,6 @@
import android.content.Context
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.CoreStartable
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.biometrics.ui.binder.DeviceEntryUnlockTrackerViewBinder
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
@@ -46,6 +45,7 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shared.Flags.ambientAod
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.VibratorHelper
@@ -105,7 +105,7 @@
bindJankViewModel()
initializeViews()
- if (lightRevealMigration()) {
+ if (ambientAod()) {
LightRevealScrimViewBinder.bind(
lightRevealScrim,
lightRevealScrimViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt
index 45f8f10..3cf0506 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.util.MathUtils
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.communal.ui.compose.TransitionDuration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.dagger.GlanceableHubBlurComponent
@@ -28,6 +27,7 @@
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shared.Flags.ambientAod
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -56,14 +56,14 @@
return transitionAnimation.sharedFlow(
duration = 250.milliseconds,
startTime =
- if (lightRevealMigration()) {
+ if (ambientAod()) {
100.milliseconds // Wait for the light reveal to "hit" the LS elements.
} else {
0.milliseconds
},
onStart = {
currentAlpha =
- if (lightRevealMigration()) {
+ if (ambientAod()) {
viewState.alpha()
} else {
0f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
index d981eeb..ba6bda8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.util.MathUtils
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -25,6 +24,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.shared.Flags.ambientAod
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -52,13 +52,13 @@
return transitionAnimation.sharedFlow(
duration = 250.milliseconds,
startTime =
- if (lightRevealMigration()) {
+ if (ambientAod()) {
100.milliseconds // Wait for the light reveal to "hit" the LS elements.
} else {
0.milliseconds
},
onStart = {
- if (lightRevealMigration()) {
+ if (ambientAod()) {
currentAlpha = viewState.alpha()
} else {
currentAlpha = 0f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
index b15cacf..2eb5bf9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.util.MathUtils
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -25,6 +24,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.shared.Flags.ambientAod
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -56,7 +56,7 @@
duration = 250.milliseconds,
startTime = 0.milliseconds,
onStart = {
- if (lightRevealMigration()) {
+ if (ambientAod()) {
currentAlpha = viewState.alpha()
} else {
currentAlpha = 0f
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
index c6e4db7..324a3ef 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
@@ -150,6 +150,13 @@
titleTextView.isEnabled = true
} else {
errorTextView.visibility = View.VISIBLE
+ if (com.android.systemui.Flags.mediaProjectionGreyErrorText()) {
+ errorTextView.isEnabled = false
+ errorTextView.setTextColor(context.getColorStateList(R.color.menu_item_text))
+ errorTextView.setText(
+ R.string.media_projection_entry_app_permission_dialog_single_app_not_supported
+ )
+ }
titleTextView.isEnabled = false
}
return view
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
index d40ecc9..1176095 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.panels.ui.compose
+import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -23,11 +24,13 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
+import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -63,6 +66,7 @@
val title = tileDetailedViewModel.title
val subTitle = tileDetailedViewModel.subTitle
+ val colors = MaterialTheme.colorScheme
Column(
modifier =
@@ -70,20 +74,33 @@
.fillMaxWidth()
// The height of the details view is TBD.
.fillMaxHeight()
+ .background(color = colors.onPrimary)
) {
CompositionLocalProvider(
value = LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant
) {
Row(
- modifier = Modifier.fillMaxWidth(),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(
+ start = TileDetailsDefaults.TitleRowStart,
+ top = TileDetailsDefaults.TitleRowTop,
+ end = TileDetailsDefaults.TitleRowEnd,
+ bottom = TileDetailsDefaults.TitleRowBottom
+ ),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
IconButton(
onClick = { detailsViewModel.closeDetailedView() },
+ colors = IconButtonDefaults.iconButtonColors(
+ contentColor = colors.onSurface
+ ),
modifier =
- Modifier.align(Alignment.CenterVertically)
+ Modifier
+ .align(Alignment.CenterVertically)
.height(TileDetailsDefaults.IconHeight)
+ .width(TileDetailsDefaults.IconWidth)
.padding(start = TileDetailsDefaults.IconPadding),
) {
Icon(
@@ -96,13 +113,19 @@
text = title,
modifier = Modifier.align(Alignment.CenterVertically),
textAlign = TextAlign.Center,
- style = MaterialTheme.typography.titleLarge,
+ style = MaterialTheme.typography.titleMedium,
+ color = colors.onSurface,
)
IconButton(
onClick = { tileDetailedViewModel.clickOnSettingsButton() },
+ colors = IconButtonDefaults.iconButtonColors(
+ contentColor = colors.onSurface
+ ),
modifier =
- Modifier.align(Alignment.CenterVertically)
+ Modifier
+ .align(Alignment.CenterVertically)
.height(TileDetailsDefaults.IconHeight)
+ .width(TileDetailsDefaults.IconWidth)
.padding(end = TileDetailsDefaults.IconPadding),
) {
Icon(
@@ -116,7 +139,8 @@
text = subTitle,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
- style = MaterialTheme.typography.titleSmall,
+ style = MaterialTheme.typography.bodySmall,
+ color = colors.onSurfaceVariant,
)
}
MapTileDetailsContent(tileDetailedViewModel)
@@ -135,6 +159,11 @@
}
private object TileDetailsDefaults {
- val IconHeight = 48.dp
+ val IconHeight = 24.dp
+ val IconWidth = 24.dp
val IconPadding = 4.dp
+ val TitleRowStart = 14.dp
+ val TitleRowTop = 22.dp
+ val TitleRowEnd = 20.dp
+ val TitleRowBottom = 8.dp
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
index b3b6cfd..699778f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
@@ -39,6 +39,7 @@
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@@ -173,6 +174,7 @@
}
}
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun LargeTileLabels(
label: String,
@@ -188,7 +190,7 @@
Column(verticalArrangement = Arrangement.Center, modifier = modifier.fillMaxHeight()) {
TileLabel(
text = label,
- style = MaterialTheme.typography.labelLarge,
+ style = MaterialTheme.typography.titleSmallEmphasized,
color = { animatedLabelColor },
isVisible = isVisible,
)
@@ -196,7 +198,7 @@
TileLabel(
secondaryLabel ?: "",
color = { animatedSecondaryLabelColor },
- style = MaterialTheme.typography.bodyMedium,
+ style = MaterialTheme.typography.labelMedium,
isVisible = isVisible,
modifier =
Modifier.thenIf(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index ccbd8fd..46f05d0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -65,6 +65,7 @@
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor
@@ -109,11 +110,11 @@
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.text.style.Hyphens
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastMap
import com.android.compose.gesture.effect.rememberOffsetOverscrollEffectFactory
import com.android.compose.modifiers.height
@@ -165,7 +166,7 @@
object TileType
-@OptIn(ExperimentalMaterial3Api::class)
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
val primaryContainerColor = MaterialTheme.colorScheme.primaryContainer
@@ -177,7 +178,8 @@
),
title = {
Text(
- text = stringResource(id = R.string.qs_edit),
+ text = stringResource(id = R.string.qs_edit_tiles),
+ style = MaterialTheme.typography.titleLargeEmphasized,
modifier = Modifier.padding(start = 24.dp),
)
},
@@ -204,7 +206,10 @@
contentColor = MaterialTheme.colorScheme.onPrimary,
),
) {
- Text(stringResource(id = com.android.internal.R.string.reset))
+ Text(
+ text = stringResource(id = com.android.internal.R.string.reset),
+ style = MaterialTheme.typography.labelLarge,
+ )
}
}
},
@@ -212,6 +217,7 @@
)
}
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun DefaultEditTileGrid(
listState: EditTileListState,
@@ -283,7 +289,9 @@
}
}
} else {
- Text(text = stringResource(id = R.string.drag_to_rearrange_tiles))
+ EditGridCenteredText(
+ text = stringResource(id = R.string.drag_to_rearrange_tiles)
+ )
}
}
}
@@ -401,6 +409,11 @@
}
@Composable
+private fun EditGridCenteredText(text: String, modifier: Modifier = Modifier) {
+ Text(text = text, style = MaterialTheme.typography.titleSmall, modifier = modifier)
+}
+
+@Composable
private fun RemoveTileTarget(onClick: () -> Unit) {
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -486,6 +499,7 @@
}
}
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun AvailableTileGrid(
tiles: List<AvailableTileGridCell>,
@@ -524,7 +538,7 @@
) {
Text(
text = category.label.load() ?: "",
- fontSize = 20.sp,
+ style = MaterialTheme.typography.titleMediumEmphasized,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.fillMaxWidth().padding(start = 8.dp, bottom = 16.dp),
)
@@ -737,6 +751,7 @@
}
}
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun AvailableTileGridCell(
cell: AvailableTileGridCell,
@@ -803,6 +818,7 @@
color = colors.label,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.labelMedium.copy(hyphens = Hyphens.Auto),
modifier = Modifier.align(Alignment.TopCenter),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt
index 3287443..60ca6e6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt
@@ -23,11 +23,15 @@
import com.android.systemui.plugins.qs.TileDetailsViewModel
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import javax.inject.Inject
@SysUISingleton
@Stable
-class DetailsViewModel @Inject constructor(val currentTilesInteractor: CurrentTilesInteractor) {
+class DetailsViewModel @Inject constructor(
+ val currentTilesInteractor: CurrentTilesInteractor,
+ val shadeModeInteractor: ShadeModeInteractor
+) {
/**
* The current active [TileDetailsViewModel]. If it's `null`, it means the qs overlay is not
@@ -52,6 +56,10 @@
* @see activeTileDetails
*/
fun onTileClicked(spec: TileSpec?): Boolean {
+ if (!shadeModeInteractor.isDualShade){
+ return false
+ }
+
if (spec == null) {
_activeTileDetails.value = null
return false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
index 74b3f30..b94e7b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
@@ -25,6 +25,7 @@
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -36,8 +37,10 @@
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.Expandable
+import com.android.compose.modifiers.thenIf
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.common.ui.compose.load
@@ -79,6 +82,17 @@
}
is OngoingActivityChipModel.ClickBehavior.None -> null
}
+ val isClickable = onClick != null
+
+ val chipSidePadding = dimensionResource(id = R.dimen.ongoing_activity_chip_side_padding)
+ val minWidth =
+ if (isClickable) {
+ dimensionResource(id = R.dimen.min_clickable_item_size)
+ } else if (model.icon != null) {
+ dimensionResource(id = R.dimen.ongoing_activity_chip_icon_size) + chipSidePadding
+ } else {
+ dimensionResource(id = R.dimen.ongoing_activity_chip_min_text_width) + chipSidePadding
+ }
Expandable(
color = Color(model.colors.background(LocalContext.current).defaultColor),
@@ -92,6 +106,15 @@
this.contentDescription = contentDescription
}
}
+ .thenIf(isClickable) { Modifier.widthIn(min = minWidth) }
+ .layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
+ layout(placeable.width, placeable.height) {
+ if (constraints.maxWidth >= minWidth.roundToPx()) {
+ placeable.place(0, 0)
+ }
+ }
+ }
.graphicsLayer(
alpha =
if (model.transitionManager?.hideChipForTransition == true) {
@@ -103,9 +126,12 @@
borderStroke = borderStroke,
onClick = onClick,
useModifierBasedImplementation = StatusBarChipsReturnAnimations.isEnabled,
+ // Some chips like the 3-2-1 countdown chip should be very small, smaller than a
+ // reasonable minimum size.
+ defaultMinSize = false,
transitionControllerFactory = model.transitionManager?.controllerFactory,
) {
- ChipBody(model, iconViewStore, isClickable = onClick != null)
+ ChipBody(model, iconViewStore, isClickable = isClickable, minWidth = minWidth)
}
}
@@ -114,36 +140,22 @@
model: OngoingActivityChipModel.Active,
iconViewStore: NotificationIconContainerViewBinder.IconViewStore?,
isClickable: Boolean,
+ minWidth: Dp,
modifier: Modifier = Modifier,
) {
val hasEmbeddedIcon =
model.icon is OngoingActivityChipModel.ChipIcon.StatusBarView ||
model.icon is OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon
- val chipSidePadding = dimensionResource(id = R.dimen.ongoing_activity_chip_side_padding)
- val minWidth =
- if (isClickable) {
- dimensionResource(id = R.dimen.min_clickable_item_size)
- } else if (model.icon != null) {
- dimensionResource(id = R.dimen.ongoing_activity_chip_icon_size) + chipSidePadding
- } else {
- dimensionResource(id = R.dimen.ongoing_activity_chip_min_text_width) + chipSidePadding
- }
-
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier =
modifier
.fillMaxHeight()
- .layout { measurable, constraints ->
- val placeable = measurable.measure(constraints)
- layout(placeable.width, placeable.height) {
- if (constraints.maxWidth >= minWidth.roundToPx()) {
- placeable.place(0, 0)
- }
- }
- }
+ // Set the minWidth here as well as on the Expandable so that the content within
+ // this row is still centered correctly horizontally
+ .thenIf(isClickable) { Modifier.widthIn(min = minWidth) }
.padding(
horizontal =
if (hasEmbeddedIcon) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index 3bb1ff1..fed9417 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -81,7 +81,6 @@
mPowerInteractor.wakeUpIfDozing("NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE);
final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- final NotificationEntry entry = row.getEntry();
mLogger.logOnClick(row.getLoggingKey());
// Check if the notification is displaying the menu, if so slide notification back
@@ -109,16 +108,16 @@
DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
if (NotificationBundleUi.isEnabled()) {
- if (!row.getEntryAdapter().isBubbleCapable() && mBubblesOptional.isPresent()) {
+ if (!row.getEntryAdapter().isBubble() && mBubblesOptional.isPresent()) {
mBubblesOptional.get().collapseStack();
}
+ row.getEntryAdapter().onEntryClicked(row);
} else {
if (!row.getEntryLegacy().isBubble() && mBubblesOptional.isPresent()) {
mBubblesOptional.get().collapseStack();
}
+ mNotificationActivityStarter.onNotificationClicked(row.getEntryLegacy(), row);
}
-
- mNotificationActivityStarter.onNotificationClicked(entry, row);
}
private boolean isMenuVisible(ExpandableNotificationRow row) {
@@ -129,9 +128,12 @@
* Attaches the click listener to the row if appropriate.
*/
public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
+ boolean isBubble = NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().isBubble()
+ : row.getEntryLegacy().isBubble();
Notification notification = sbn.getNotification();
if (notification.contentIntent != null || notification.fullScreenIntent != null
- || row.getEntry().isBubble()) {
+ || isBubble) {
if (NotificationBundleUi.isEnabled()) {
row.setBubbleClickListener(
v -> row.getEntryAdapter().onNotificationBubbleIconClicked());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
index d06f24f..ba40010 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
@@ -17,3 +17,4 @@
yurilin@google.com
per-file MediaNotificationProcessor.java = ethibodeau@google.com
+per-file MagicActionBackgroundDrawable.kt = dupin@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
index f653573..8fc6cbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
@@ -69,10 +69,13 @@
return mUnmodifiableChildren;
}
+ void clearChildren() {
+ mChildren.clear();
+ }
+
/**
* @return Null because bundles do not have an associated NotificationEntry.
*/
-
@Nullable
@Override
public NotificationEntry getRepresentativeEntry() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
index e743d87..be17ae56c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
@@ -98,7 +98,7 @@
return false
}
- override fun isBubbleCapable(): Boolean {
+ override fun isBubble(): Boolean {
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index f39bd03..3757ebf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -125,7 +125,7 @@
boolean canDragAndDrop();
- boolean isBubbleCapable();
+ boolean isBubble();
@Nullable String getStyle();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
index 12cfa91..a23c5a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
@@ -121,7 +121,7 @@
return false
}
- override fun isBubbleCapable(): Boolean {
+ override fun isBubble(): Boolean {
return entry.isBubble
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 5cea821..fe2bd34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -567,7 +567,7 @@
for (BundleEntry be : mIdToBundleEntry.values()) {
be.beginNewAttachState();
- // TODO(b/399736937) Clear bundle children
+ be.clearChildren();
// BundleEntry has not representative summary so we do not need to clear it here.
}
mNotifList.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
index 2f0701f..3747aba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import android.util.ArrayMap
+import com.android.systemui.statusbar.notification.collection.BundleEntry
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -37,9 +38,17 @@
private fun onBeforeFinalizeFilter(entries: List<PipelineEntry>) {
// save untruncated child counts to our internal map
untruncatedChildCounts.clear()
- entries.asSequence().filterIsInstance<GroupEntry>().forEach { groupEntry ->
- untruncatedChildCounts[groupEntry] = groupEntry.children.size
- }
+ entries.asSequence()
+ .flatMap { entry ->
+ when (entry) {
+ is GroupEntry -> listOf(entry)
+ is BundleEntry -> entry.children.filterIsInstance<GroupEntry>()
+ else -> emptyList()
+ }
+ }
+ .forEach { groupEntry ->
+ untruncatedChildCounts[groupEntry] = groupEntry.children.size
+ }
}
private fun onAfterRenderGroup(group: GroupEntry, controller: NotifGroupController) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 1be415d..20169ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -36,6 +36,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.statusbar.notification.collection.BundleEntry;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -307,8 +308,15 @@
private void inflateAllRequiredViews(List<PipelineEntry> entries) {
for (int i = 0, size = entries.size(); i < size; i++) {
PipelineEntry entry = entries.get(i);
- if (NotificationBundleUi.isEnabled() && entry instanceof BundleEntry) {
- // TODO(b/399738511) Inflate bundle views.
+ if (NotificationBundleUi.isEnabled() && entry instanceof BundleEntry bundleEntry) {
+ for (ListEntry listEntry : bundleEntry.getChildren()) {
+ if (listEntry instanceof GroupEntry groupEntry) {
+ inflateRequiredGroupViews(groupEntry);
+ } else {
+ NotificationEntry notifEntry = (NotificationEntry) listEntry;
+ inflateRequiredNotifViews(notifEntry);
+ }
+ }
} else if (entry instanceof GroupEntry) {
GroupEntry groupEntry = (GroupEntry) entry;
inflateRequiredGroupViews(groupEntry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
index d35c3b61..7e19ff1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
@@ -21,6 +21,7 @@
import android.app.Notification.BigTextStyle
import android.app.Notification.CallStyle
import android.app.Notification.EXTRA_BIG_TEXT
+import android.app.Notification.EXTRA_CALL_PERSON
import android.app.Notification.EXTRA_CHRONOMETER_COUNT_DOWN
import android.app.Notification.EXTRA_PROGRESS
import android.app.Notification.EXTRA_PROGRESS_INDETERMINATE
@@ -33,6 +34,7 @@
import android.app.Notification.EXTRA_VERIFICATION_TEXT
import android.app.Notification.InboxStyle
import android.app.Notification.ProgressStyle
+import android.app.Person
import android.content.Context
import android.graphics.drawable.Icon
import com.android.systemui.Flags
@@ -108,12 +110,12 @@
contentBuilder.shortCriticalText = notification.shortCriticalText()
contentBuilder.lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs
contentBuilder.profileBadgeResId = null // TODO
- contentBuilder.title = notification.resolveTitle(recoveredBuilder.style)
- contentBuilder.text = notification.resolveText(recoveredBuilder.style)
+ contentBuilder.title = notification.title(recoveredBuilder.style)
+ contentBuilder.text = notification.text(recoveredBuilder.style)
contentBuilder.skeletonLargeIcon = notification.skeletonLargeIcon(imageModelProvider)
contentBuilder.oldProgress = notification.oldProgress()
- val colorsFromNotif = recoveredBuilder.getColors(/* header= */ false)
+ val colorsFromNotif = recoveredBuilder.getColors(/* isHeader= */ false)
contentBuilder.colors =
PromotedNotificationContentModel.Colors(
backgroundColor = colorsFromNotif.backgroundColor,
@@ -132,20 +134,16 @@
private fun Notification.bigTitle(): CharSequence? = extras?.getCharSequence(EXTRA_TITLE_BIG)
- private fun Notification.Style.bigTitleOverridesTitle(): Boolean {
- return when (this) {
+ private fun Notification.callPerson(): Person? =
+ extras?.getParcelable(EXTRA_CALL_PERSON, Person::class.java)
+
+ private fun Notification.title(style: Notification.Style?): CharSequence? {
+ return when (style) {
is BigTextStyle,
is BigPictureStyle,
- is InboxStyle -> true
- else -> false
- }
- }
-
- private fun Notification.resolveTitle(style: Notification.Style?): CharSequence? {
- return if (style?.bigTitleOverridesTitle() == true) {
- bigTitle()
- } else {
- null
+ is InboxStyle -> bigTitle()
+ is CallStyle -> callPerson()?.name
+ else -> null
} ?: title()
}
@@ -153,13 +151,10 @@
private fun Notification.bigText(): CharSequence? = extras?.getCharSequence(EXTRA_BIG_TEXT)
- private fun Notification.Style.bigTextOverridesText(): Boolean = this is BigTextStyle
-
- private fun Notification.resolveText(style: Notification.Style?): CharSequence? {
- return if (style?.bigTextOverridesText() == true) {
- bigText()
- } else {
- null
+ private fun Notification.text(style: Notification.Style?): CharSequence? {
+ return when (style) {
+ is BigTextStyle -> bigText()
+ else -> null
} ?: text()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
index 4689347..5f9678a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
@@ -63,7 +63,7 @@
INFO,
{
str1 = entry.logKey
- str2 = content.toString()
+ str2 = content.toRedactedString()
},
{ "extraction succeeded: $str2 for $str1" },
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
index 0c2859f..57b0720 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
@@ -25,6 +25,8 @@
import com.android.internal.widget.NotificationProgressModel
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
+import com.android.systemui.statusbar.notification.row.ImageResult
+import com.android.systemui.statusbar.notification.row.LazyImage
import com.android.systemui.statusbar.notification.row.shared.ImageModel
/**
@@ -152,6 +154,54 @@
Ineligible,
}
+ fun toRedactedString(): String {
+ return ("PromotedNotificationContentModel(" +
+ "identity=$identity, " +
+ "wasPromotedAutomatically=$wasPromotedAutomatically, " +
+ "smallIcon=${smallIcon?.toRedactedString()}, " +
+ "appName=$appName, " +
+ "subText=${subText?.toRedactedString()}, " +
+ "shortCriticalText=$shortCriticalText, " +
+ "time=$time, " +
+ "lastAudiblyAlertedMs=$lastAudiblyAlertedMs, " +
+ "profileBadgeResId=$profileBadgeResId, " +
+ "title=${title?.toRedactedString()}, " +
+ "text=${text?.toRedactedString()}, " +
+ "skeletonLargeIcon=${skeletonLargeIcon?.toRedactedString()}, " +
+ "oldProgress=$oldProgress, " +
+ "colors=$colors, " +
+ "style=$style, " +
+ "personIcon=${personIcon?.toRedactedString()}, " +
+ "personName=${personName?.toRedactedString()}, " +
+ "verificationIcon=$verificationIcon, " +
+ "verificationText=$verificationText, " +
+ "newProgress=$newProgress)")
+ }
+
+ private fun CharSequence.toRedactedString(): String = "[$length]"
+
+ private fun ImageModel.toRedactedString(): String {
+ return when (this) {
+ is LazyImage -> this.toRedactedString()
+ else -> this.toString()
+ }
+ }
+
+ private fun LazyImage.toRedactedString(): String {
+ return ("LazyImage(" +
+ "icon=[${icon.javaClass.simpleName}], " +
+ "sizeClass=$sizeClass, " +
+ "transform=$transform, " +
+ "result=${result?.toRedactedString()})")
+ }
+
+ private fun ImageResult.toRedactedString(): String {
+ return when (this) {
+ is ImageResult.Empty -> this.toString()
+ is ImageResult.Image -> "Image(drawable=[${drawable.javaClass.simpleName}])"
+ }
+ }
+
companion object {
@JvmStatic
fun featureFlagEnabled(): Boolean =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 256d549..2a3b266 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -4011,7 +4011,8 @@
}
} else if (isChildInGroup()) {
final int childColor = getShowingLayout().getBackgroundColorForExpansionState();
- if (Flags.notificationRowTransparency() && childColor == Color.TRANSPARENT) {
+ if ((Flags.notificationRowTransparency() || notificationsRedesignTemplates())
+ && childColor == Color.TRANSPARENT) {
// If child is not customizing its background color, switch from the parent to
// the child background when the expansion finishes.
mShowNoBackground = !mNotificationParent.mShowNoBackground;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
index fe3a856..a9ca635 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
@@ -62,6 +62,7 @@
private val buttonShape = Path()
// Color and style
+ private val outlineStaticColor = context.getColor(R.color.magic_action_button_stroke_color)
private val bgPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
val bgColor =
context.getColor(
@@ -70,15 +71,17 @@
color = bgColor
style = Paint.Style.FILL
}
- private val outlinePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- val outlineColor =
- context.getColor(
- com.android.internal.R.color.materialColorOutlineVariant
- )
- color = outlineColor
+ private val outlineGradientPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+ color = outlineStaticColor
style = Paint.Style.STROKE
strokeWidth = outlineStrokeWidth
}
+ private val outlineSolidPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+ color = outlineStaticColor
+ style = Paint.Style.STROKE
+ strokeWidth = outlineStrokeWidth
+ }
+
private val outlineStartColor =
context.getColor(
com.android.internal.R.color.materialColorTertiaryContainer
@@ -91,21 +94,35 @@
context.getColor(
com.android.internal.R.color.materialColorPrimary
)
+
// Animation
private var gradientAnimator: ValueAnimator
private var rotationAngle = 20f // Start rotation at 20 degrees
+ private var fadeAnimator: ValueAnimator? = null
+ private var gradientAlpha = 255 // Fading out gradient
+ private var solidAlpha = 0 // Fading in solid color
init {
gradientAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
- duration = 5000 // 5 seconds
+ duration = 1500
interpolator = Interpolators.LINEAR
- repeatCount = 1
+ repeatCount = 0
addUpdateListener { animator ->
val animatedValue = animator.animatedValue as Float
rotationAngle = 20f + animatedValue * 360f // Rotate in a spiral
invalidateSelf()
}
- // TODO: Reset the outline color when animation ends.
+ start()
+ }
+ fadeAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = 500
+ startDelay = 1000
+ addUpdateListener { animator ->
+ val progress = animator.animatedValue as Float
+ gradientAlpha = ((1 - progress) * 255).toInt() // Fade out gradient
+ solidAlpha = (progress * 255).toInt() // Fade in color
+ invalidateSelf()
+ }
start()
}
}
@@ -120,14 +137,9 @@
// Draw background
canvas.clipPath(buttonShape)
canvas.drawPath(buttonShape, bgPaint)
- // Apply gradient to outline
- canvas.drawPath(buttonShape, outlinePaint)
- updateGradient(boundsF)
- canvas.restore()
- }
- private fun updateGradient(boundsF: RectF) {
- val gradient = LinearGradient(
+ // Set up outline gradient
+ val gradientShader = LinearGradient(
boundsF.left, boundsF.top,
boundsF.right, boundsF.bottom,
intArrayOf(outlineStartColor, outlineMiddleColor, outlineEndColor),
@@ -137,9 +149,17 @@
// Create a rotation matrix for the spiral effect
val matrix = Matrix()
matrix.setRotate(rotationAngle, boundsF.centerX(), boundsF.centerY())
- gradient.setLocalMatrix(matrix)
+ gradientShader.setLocalMatrix(matrix)
- outlinePaint.shader = gradient
+ // Apply gradient to outline
+ outlineGradientPaint.shader = gradientShader
+ outlineGradientPaint.alpha = gradientAlpha
+ canvas.drawPath(buttonShape, outlineGradientPaint)
+ // Apply solid color to outline
+ outlineSolidPaint.alpha = solidAlpha
+ canvas.drawPath(buttonShape, outlineSolidPaint)
+
+ canvas.restore()
}
override fun onBoundsChange(bounds: Rect) {
@@ -149,13 +169,15 @@
override fun setAlpha(alpha: Int) {
bgPaint.alpha = alpha
- outlinePaint.alpha = alpha
+ outlineGradientPaint.alpha = alpha
+ outlineSolidPaint.alpha = alpha
invalidateSelf()
}
override fun setColorFilter(colorFilter: ColorFilter?) {
bgPaint.colorFilter = colorFilter
- outlinePaint.colorFilter = colorFilter
+ outlineGradientPaint.colorFilter = colorFilter
+ outlineSolidPaint.colorFilter = colorFilter
invalidateSelf()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index cb1e898..488aa44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1605,7 +1605,7 @@
if (shouldShowBubbleButton(entry)) {
boolean isBubble = NotificationBundleUi.isEnabled()
- ? mContainingNotification.getEntryAdapter().isBubbleCapable()
+ ? mContainingNotification.getEntryAdapter().isBubble()
: entry.isBubble();
// explicitly resolve drawable resource using SystemUI's theme
Drawable d = mContext.getDrawable(isBubble
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index a4ee4ad..9d9f01b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -31,11 +31,11 @@
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import static com.android.systemui.Flags.keyboardShortcutHelperRewrite;
-import static com.android.systemui.Flags.lightRevealMigration;
import static com.android.systemui.Flags.relockWithPowerButtonImmediately;
import static com.android.systemui.Flags.statusBarSignalPolicyRefactor;
import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT;
+import static com.android.systemui.shared.Flags.ambientAod;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import android.annotation.Nullable;
@@ -980,7 +980,7 @@
@Override
public void onKeyguardGoingAwayChanged() {
- if (lightRevealMigration()) {
+ if (ambientAod()) {
// This code path is not used if the KeyguardTransitionRepository is managing
// the lightreveal scrim.
return;
@@ -2446,7 +2446,7 @@
return;
}
- if (lightRevealMigration()) {
+ if (ambientAod()) {
return;
}
@@ -3103,7 +3103,7 @@
@Override
public void onDozeAmountChanged(float linear, float eased) {
- if (!lightRevealMigration()
+ if (!ambientAod()
&& !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
// If wakeAndUnlocking, this is handled in AuthRippleInteractor
if (!mBiometricUnlockController.isWakeAndUnlock()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 0d43789..8890db3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -18,7 +18,6 @@
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
import com.android.systemui.DejankUtils
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.KeyguardViewMediator
@@ -26,6 +25,7 @@
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
+import com.android.systemui.shared.Flags.ambientAod
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -100,7 +100,7 @@
duration = LIGHT_REVEAL_ANIMATION_DURATION
interpolator = Interpolators.LINEAR
addUpdateListener {
- if (lightRevealMigration()) return@addUpdateListener
+ if (ambientAod()) return@addUpdateListener
if (lightRevealScrim.revealEffect !is CircleReveal) {
lightRevealScrim.revealAmount = it.animatedValue as Float
}
@@ -116,7 +116,7 @@
addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationCancel(animation: Animator) {
- if (lightRevealMigration()) return
+ if (ambientAod()) return
if (lightRevealScrim.revealEffect !is CircleReveal) {
lightRevealScrim.revealAmount = 1f
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 9a81992..7f778bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -256,6 +256,9 @@
if (mClockFormat != null) {
mClockFormat.setTimeZone(mCalendar.getTimeZone());
}
+ if (mContentDescriptionFormat != null) {
+ mContentDescriptionFormat.setTimeZone(mCalendar.getTimeZone());
+ }
});
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
final Locale newLocale = getResources().getConfiguration().locale;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index 43d1ef4..32f784f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -16,7 +16,6 @@
package com.android.systemui.volume.dialog.sliders.ui
-import android.graphics.drawable.Drawable
import android.view.View
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
@@ -29,7 +28,6 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SliderDefaults
import androidx.compose.runtime.Composable
@@ -43,7 +41,8 @@
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.theme.PlatformTheme
-import com.android.compose.ui.graphics.painter.DrawablePainter
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.res.R
@@ -155,7 +154,7 @@
},
)
},
- accessibilityParams = AccessibilityParams(label = sliderStateModel.label),
+ accessibilityParams = AccessibilityParams(contentDescription = sliderStateModel.label),
modifier =
modifier.pointerInput(Unit) {
coroutineScope {
@@ -172,7 +171,7 @@
@Composable
private fun BoxScope.VolumeIcon(
- drawable: Drawable,
+ icon: Icon.Loaded,
isVisible: Boolean,
modifier: Modifier = Modifier,
) {
@@ -182,6 +181,6 @@
exit = fadeOut(animationSpec = tween(durationMillis = 50)),
modifier = modifier.align(Alignment.Center).size(40.dp).padding(10.dp),
) {
- Icon(painter = DrawablePainter(drawable), contentDescription = null)
+ Icon(icon)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
index ef147c7..3712276 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
@@ -18,32 +18,36 @@
import android.annotation.SuppressLint
import android.content.Context
-import android.graphics.drawable.Drawable
import android.media.AudioManager
import androidx.annotation.DrawableRes
import com.android.settingslib.R as SettingsR
import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.RingerMode
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.withContext
@SuppressLint("UseCompatLoadingForDrawables")
class VolumeDialogSliderIconProvider
@Inject
constructor(
private val context: Context,
+ @UiBackground private val uiBackgroundContext: CoroutineContext,
private val zenModeInteractor: ZenModeInteractor,
private val audioVolumeInteractor: AudioVolumeInteractor,
) {
- fun getAudioSharingIcon(isMuted: Boolean): Flow<Drawable> {
+ fun getAudioSharingIcon(isMuted: Boolean): Flow<Icon.Loaded> {
return flow {
val iconRes =
if (isMuted) {
@@ -51,11 +55,12 @@
} else {
R.drawable.ic_volume_media_bt
}
- emit(context.getDrawable(iconRes)!!)
+ val drawable = withContext(uiBackgroundContext) { context.getDrawable(iconRes)!! }
+ emit(Icon.Loaded(drawable = drawable, contentDescription = null, res = iconRes))
}
}
- fun getCastIcon(isMuted: Boolean): Flow<Drawable> {
+ fun getCastIcon(isMuted: Boolean): Flow<Icon.Loaded> {
return flow {
val iconRes =
if (isMuted) {
@@ -63,7 +68,8 @@
} else {
SettingsR.drawable.ic_volume_remote
}
- emit(context.getDrawable(iconRes)!!)
+ val drawable = withContext(uiBackgroundContext) { context.getDrawable(iconRes)!! }
+ emit(Icon.Loaded(drawable = drawable, contentDescription = null, res = iconRes))
}
}
@@ -74,15 +80,18 @@
levelMax: Int,
isMuted: Boolean,
isRoutedToBluetooth: Boolean,
- ): Flow<Drawable> {
+ ): Flow<Icon.Loaded> {
return combine(
zenModeInteractor.activeModesBlockingStream(stream),
ringerModeForStream(stream),
) { activeModesBlockingStream, ringerMode ->
if (activeModesBlockingStream?.mainMode?.icon != null) {
- return@combine activeModesBlockingStream.mainMode.icon.drawable
+ Icon.Loaded(
+ drawable = activeModesBlockingStream.mainMode.icon.drawable,
+ contentDescription = null,
+ )
} else {
- context.getDrawable(
+ val iconRes =
getIconRes(
stream,
level,
@@ -92,7 +101,8 @@
isRoutedToBluetooth,
ringerMode,
)
- )!!
+ val drawable = withContext(uiBackgroundContext) { context.getDrawable(iconRes)!! }
+ Icon.Loaded(drawable = drawable, contentDescription = null, res = iconRes)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
index 88a061f..ed59598 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
import android.content.Context
-import android.graphics.drawable.Drawable
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.shared.model.streamLabel
@@ -25,14 +25,14 @@
val value: Float,
val isDisabled: Boolean,
val valueRange: ClosedFloatingPointRange<Float>,
- val icon: Drawable,
+ val icon: Icon.Loaded,
val label: String,
)
fun VolumeDialogStreamModel.toStateModel(
context: Context,
isDisabled: Boolean,
- icon: Drawable,
+ icon: Icon.Loaded,
): VolumeDialogSliderStateModel {
return VolumeDialogSliderStateModel(
value = level.toFloat(),
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index f6aa189..faf0abd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -108,11 +108,9 @@
isMuted = isMuted,
isRoutedToBluetooth = routedToBluetooth,
)
-
is VolumeDialogSliderType.RemoteMediaStream -> {
volumeDialogSliderIconProvider.getCastIcon(isMuted)
}
-
is VolumeDialogSliderType.AudioSharingStream -> {
volumeDialogSliderIconProvider.getAudioSharingIcon(isMuted)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt
index 3d98eba..f645267 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt
@@ -16,9 +16,11 @@
package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
+import android.content.Context
import com.android.internal.logging.UiEventLogger
import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.res.R
@@ -28,6 +30,7 @@
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
@@ -39,11 +42,14 @@
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
class AudioSharingStreamSliderViewModel
@AssistedInject
constructor(
+ private val context: Context,
@Assisted private val coroutineScope: CoroutineScope,
+ @UiBackground private val uiBackgroundContext: CoroutineContext,
private val audioSharingInteractor: AudioSharingInteractor,
private val uiEventLogger: UiEventLogger,
private val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
@@ -51,6 +57,12 @@
) : SliderViewModel {
private val volumeChanges = MutableStateFlow<Int?>(null)
+ private val audioSharingIcon =
+ Icon.Loaded(
+ drawable = context.getDrawable(R.drawable.ic_volume_media_bt)!!,
+ contentDescription = null,
+ res = R.drawable.ic_volume_media_bt,
+ )
override val slider: StateFlow<SliderState> =
combine(
audioSharingInteractor.volume.distinctUntilChanged().onEach {
@@ -62,16 +74,17 @@
if (volume == null) {
SliderState.Empty
} else {
-
- State(
- value = volume.toFloat(),
- valueRange =
- audioSharingInteractor.volumeMin.toFloat()..audioSharingInteractor
- .volumeMax
- .toFloat(),
- icon = Icon.Resource(R.drawable.ic_volume_media_bt, null),
- label = deviceName,
- )
+ withContext(uiBackgroundContext) {
+ State(
+ value = volume.toFloat(),
+ valueRange =
+ audioSharingInteractor.volumeMin.toFloat()..audioSharingInteractor
+ .volumeMax
+ .toFloat(),
+ icon = audioSharingIcon,
+ label = deviceName,
+ )
+ }
}
}
.stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
@@ -107,7 +120,7 @@
private data class State(
override val value: Float,
override val valueRange: ClosedFloatingPointRange<Float>,
- override val icon: Icon,
+ override val icon: Icon.Loaded?,
override val label: String,
) : SliderState {
override val hapticFilter: SliderHapticFeedbackFilter
@@ -116,7 +129,7 @@
override val isEnabled: Boolean
get() = true
- override val a11yStep: Float
+ override val step: Float
get() = 1f
override val disabledMessage: String?
@@ -125,6 +138,9 @@
override val isMutable: Boolean
get() = false
+ override val a11yContentDescription: String
+ get() = label
+
override val a11yClickDescription: String?
get() = null
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
index 9d32285..9fe0ad4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.media.AudioManager
import android.util.Log
+import androidx.annotation.DrawableRes
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.bluetooth.CachedBluetoothDevice
@@ -28,6 +29,7 @@
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.modes.shared.ModesUiIcons
@@ -40,18 +42,21 @@
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
/** Models a particular slider state. */
class AudioStreamSliderViewModel
@@ -59,10 +64,11 @@
constructor(
@Assisted private val audioStreamWrapper: FactoryAudioStreamWrapper,
@Assisted private val coroutineScope: CoroutineScope,
+ @UiBackground private val uiBackgroundContext: CoroutineContext,
private val context: Context,
private val audioVolumeInteractor: AudioVolumeInteractor,
private val zenModeInteractor: ZenModeInteractor,
- private val audioSharingInteractor: AudioSharingInteractor,
+ audioSharingInteractor: AudioSharingInteractor,
private val uiEventLogger: UiEventLogger,
private val volumePanelLogger: VolumePanelLogger,
private val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
@@ -148,57 +154,69 @@
null
}
- private fun AudioStreamModel.toState(
+ private suspend fun AudioStreamModel.toState(
isEnabled: Boolean,
ringerMode: RingerMode,
disabledMessage: String?,
inAudioSharing: Boolean,
primaryDevice: CachedBluetoothDevice?,
- ): State {
- val label = getLabel(inAudioSharing, primaryDevice)
- val icon = getIcon(ringerMode, inAudioSharing)
- return State(
- value = volume.toFloat(),
- valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(),
- hapticFilter = createHapticFilter(ringerMode),
- icon = icon,
- label = label,
- disabledMessage = disabledMessage,
- isEnabled = isEnabled,
- a11yStep = volumeRange.step.toFloat(),
- a11yClickDescription =
- if (isAffectedByMute) {
- context.getString(
- if (isMuted) {
- R.string.volume_panel_hint_unmute
- } else {
- R.string.volume_panel_hint_mute
- },
- label,
- )
- } else {
- null
- },
- a11yStateDescription =
- if (isMuted) {
- context.getString(
- if (isAffectedByRingerMode) {
- if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) {
- R.string.volume_panel_hint_vibrate
+ ): State =
+ withContext(uiBackgroundContext) {
+ val label = getLabel(inAudioSharing, primaryDevice)
+ State(
+ value = volume.toFloat(),
+ valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(),
+ hapticFilter = createHapticFilter(ringerMode),
+ icon = getIcon(ringerMode, inAudioSharing),
+ label = label,
+ disabledMessage = disabledMessage,
+ isEnabled = isEnabled,
+ step = volumeRange.step.toFloat(),
+ a11yContentDescription =
+ if (isEnabled) {
+ label
+ } else {
+ disabledMessage?.let {
+ context.getString(
+ R.string.volume_slider_disabled_message_template,
+ label,
+ disabledMessage,
+ )
+ } ?: label
+ },
+ a11yClickDescription =
+ if (isAffectedByMute) {
+ context.getString(
+ if (isMuted) {
+ R.string.volume_panel_hint_unmute
+ } else {
+ R.string.volume_panel_hint_mute
+ },
+ label,
+ )
+ } else {
+ null
+ },
+ a11yStateDescription =
+ if (isMuted) {
+ context.getString(
+ if (isAffectedByRingerMode) {
+ if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) {
+ R.string.volume_panel_hint_vibrate
+ } else {
+ R.string.volume_panel_hint_muted
+ }
} else {
R.string.volume_panel_hint_muted
}
- } else {
- R.string.volume_panel_hint_muted
- }
- )
- } else {
- null
- },
- audioStreamModel = this,
- isMutable = isAffectedByMute,
- )
- }
+ )
+ } else {
+ null
+ },
+ audioStreamModel = this@toState,
+ isMutable = isAffectedByMute,
+ )
+ }
private fun AudioStreamModel.createHapticFilter(
ringerMode: RingerMode
@@ -220,12 +238,14 @@
flowOf(context.getString(R.string.stream_notification_unavailable))
} else {
if (zenModeInteractor.canBeBlockedByZenMode(audioStream)) {
- zenModeInteractor.activeModesBlockingStream(audioStream).map { blockingZenModes
- ->
- blockingZenModes.mainMode?.name?.let {
- context.getString(R.string.stream_unavailable_by_modes, it)
- } ?: context.getString(R.string.stream_unavailable_by_unknown)
- }
+ zenModeInteractor
+ .activeModesBlockingStream(audioStream)
+ .map { blockingZenModes ->
+ blockingZenModes.mainMode?.name?.let {
+ context.getString(R.string.stream_unavailable_by_modes, it)
+ } ?: context.getString(R.string.stream_unavailable_by_unknown)
+ }
+ .distinctUntilChanged()
} else {
flowOf(context.getString(R.string.stream_unavailable_by_unknown))
}
@@ -256,8 +276,11 @@
?: error("No label for the stream: $audioStream")
}
- private fun AudioStreamModel.getIcon(ringerMode: RingerMode, inAudioSharing: Boolean): Icon {
- val iconRes =
+ private fun AudioStreamModel.getIcon(
+ ringerMode: RingerMode,
+ inAudioSharing: Boolean,
+ ): Icon.Loaded {
+ val iconResource: Int =
if (isMuted) {
if (isAffectedByRingerMode) {
if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) {
@@ -272,14 +295,21 @@
inAudioSharing
) {
R.drawable.ic_volume_media_bt_mute
- } else R.drawable.ic_volume_off
+ } else {
+ R.drawable.ic_volume_off
+ }
}
} else {
getIconByStream(audioStream, inAudioSharing)
}
- return Icon.Resource(iconRes, null)
+ return Icon.Loaded(
+ drawable = context.getDrawable(iconResource)!!,
+ contentDescription = null,
+ res = iconResource,
+ )
}
+ @DrawableRes
private fun getIconByStream(audioStream: AudioStream, inAudioSharing: Boolean): Int =
when (audioStream.value) {
AudioManager.STREAM_MUSIC ->
@@ -302,14 +332,15 @@
private data class State(
override val value: Float,
override val valueRange: ClosedFloatingPointRange<Float>,
+ override val step: Float,
override val hapticFilter: SliderHapticFeedbackFilter,
- override val icon: Icon,
+ override val icon: Icon.Loaded?,
override val label: String,
override val disabledMessage: String?,
override val isEnabled: Boolean,
- override val a11yStep: Float,
override val a11yClickDescription: String?,
override val a11yStateDescription: String?,
+ override val a11yContentDescription: String,
override val isMutable: Boolean,
val audioStreamModel: AudioStreamModel,
) : SliderState
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
index a6c8091..01810f9 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
@@ -21,6 +21,7 @@
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.res.R
@@ -30,30 +31,40 @@
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
class CastVolumeSliderViewModel
@AssistedInject
constructor(
@Assisted private val session: MediaDeviceSession,
@Assisted private val coroutineScope: CoroutineScope,
+ @UiBackground private val uiBackgroundContext: CoroutineContext,
private val context: Context,
private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
private val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
private val volumePanelLogger: VolumePanelLogger,
) : SliderViewModel {
+ private val castLabel = context.getString(R.string.media_device_cast)
+ private val castIcon =
+ Icon.Loaded(
+ drawable = context.getDrawable(R.drawable.ic_cast)!!,
+ contentDescription = null,
+ res = R.drawable.ic_cast,
+ )
override val slider: StateFlow<SliderState> =
mediaDeviceSessionInteractor
.playbackInfo(session)
.mapNotNull {
volumePanelLogger.onVolumeUpdateReceived(session.sessionToken, it.currentVolume)
- it.getCurrentState()
+ withContext(uiBackgroundContext) { it.getCurrentState() }
}
.stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
@@ -83,20 +94,20 @@
return State(
value = currentVolume.toFloat(),
valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(),
- icon = Icon.Resource(R.drawable.ic_cast, null),
- label = context.getString(R.string.media_device_cast),
+ icon = castIcon,
+ label = castLabel,
isEnabled = true,
- a11yStep = 1f,
+ step = 1f,
)
}
private data class State(
override val value: Float,
override val valueRange: ClosedFloatingPointRange<Float>,
- override val icon: Icon,
+ override val icon: Icon.Loaded?,
override val label: String,
override val isEnabled: Boolean,
- override val a11yStep: Float,
+ override val step: Float,
) : SliderState {
override val hapticFilter: SliderHapticFeedbackFilter
get() = SliderHapticFeedbackFilter()
@@ -107,6 +118,9 @@
override val isMutable: Boolean
get() = false
+ override val a11yContentDescription: String
+ get() = label
+
override val a11yClickDescription: String?
get() = null
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt
index 4bc237b..b1d18340 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt
@@ -27,18 +27,17 @@
sealed interface SliderState {
val value: Float
val valueRange: ClosedFloatingPointRange<Float>
+ val step: Float
val hapticFilter: SliderHapticFeedbackFilter
- val icon: Icon?
+ // Force preloaded icon
+ val icon: Icon.Loaded?
val isEnabled: Boolean
val label: String
- /**
- * A11y slider controls works by adjusting one step up or down. The default slider step isn't
- * enough to trigger rounding to the correct value.
- */
- val a11yStep: Float
+
val a11yClickDescription: String?
val a11yStateDescription: String?
+ val a11yContentDescription: String
val disabledMessage: String?
val isMutable: Boolean
@@ -46,12 +45,13 @@
override val value: Float = 0f
override val valueRange: ClosedFloatingPointRange<Float> = 0f..1f
override val hapticFilter = SliderHapticFeedbackFilter()
- override val icon: Icon? = null
+ override val icon: Icon.Loaded? = null
override val label: String = ""
override val disabledMessage: String? = null
- override val a11yStep: Float = 0f
+ override val step: Float = 0f
override val a11yClickDescription: String? = null
override val a11yStateDescription: String? = null
+ override val a11yContentDescription: String = label
override val isEnabled: Boolean = true
override val isMutable: Boolean = false
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt
index f6582a0..502b311 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt
@@ -40,7 +40,6 @@
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.semantics.clearAndSetSemantics
@@ -52,7 +51,6 @@
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.res.R
import com.android.systemui.volume.haptics.ui.VolumeHapticsConfigsProvider
import kotlin.math.round
import kotlinx.coroutines.Job
@@ -108,7 +106,8 @@
}
}
val semantics =
- accessibilityParams.createSemantics(
+ createSemantics(
+ accessibilityParams,
animatable.targetValue,
valueRange,
valueChange,
@@ -167,24 +166,18 @@
return Math.round(coercedValue / stepDistance) * stepDistance
}
-@Composable
-private fun AccessibilityParams.createSemantics(
+private fun createSemantics(
+ params: AccessibilityParams,
value: Float,
valueRange: ClosedFloatingPointRange<Float>,
onValueChanged: (Float) -> Unit,
isEnabled: Boolean,
stepDistance: Float,
): SemanticsPropertyReceiver.() -> Unit {
- val semanticsContentDescription =
- disabledMessage
- ?.takeIf { !isEnabled }
- ?.let { message ->
- stringResource(R.string.volume_slider_disabled_message_template, label, message)
- } ?: label
return {
- contentDescription = semanticsContentDescription
+ contentDescription = params.contentDescription
if (isEnabled) {
- currentStateDescription?.let { stateDescription = it }
+ params.stateDescription?.let { stateDescription = it }
progressBarRangeInfo = ProgressBarRangeInfo(value, valueRange)
} else {
disabled()
@@ -253,9 +246,8 @@
}
data class AccessibilityParams(
- val label: String,
- val currentStateDescription: String? = null,
- val disabledMessage: String? = null,
+ val contentDescription: String,
+ val stateDescription: String? = null,
)
sealed interface Haptics {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java
index 146488b..108f3ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java
@@ -69,4 +69,25 @@
assertThat(end_y).isEqualTo(end_y);
}
+
+ @Test
+ public void avoidVerticalDisplayCutout_doesNotExceedTopBounds() {
+ final int y = DRAGGABLE_BOUNDS.top - 100;
+
+ final float end_y = MenuViewAppearance.avoidVerticalDisplayCutout(
+ y, MENU_HEIGHT, DRAGGABLE_BOUNDS, new Rect(0, 5, 0, 6));
+
+ assertThat(end_y).isGreaterThan(DRAGGABLE_BOUNDS.top);
+ }
+
+
+ @Test
+ public void avoidVerticalDisplayCutout_doesNotExceedBottomBounds() {
+ final int y = DRAGGABLE_BOUNDS.bottom + 100;
+
+ final float end_y = MenuViewAppearance.avoidVerticalDisplayCutout(
+ y, MENU_HEIGHT, DRAGGABLE_BOUNDS, new Rect(0, 5, 0, 6));
+
+ assertThat(end_y).isLessThan(DRAGGABLE_BOUNDS.bottom);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 8fb2a24..6a9a485 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -734,6 +734,7 @@
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
NotificationEntry entry = mock(NotificationEntry.class);
when(row.getEntry()).thenReturn(entry);
+ when(row.getEntryLegacy()).thenReturn(entry);
when(entry.isAmbient()).thenReturn(false);
EntryAdapter entryAdapter = mock(EntryAdapter.class);
when(entryAdapter.isAmbient()).thenReturn(false);
@@ -753,6 +754,7 @@
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
NotificationEntry entry = mock(NotificationEntry.class);
when(row.getEntry()).thenReturn(entry);
+ when(row.getEntryLegacy()).thenReturn(entry);
when(entry.isAmbient()).thenReturn(true);
EntryAdapter entryAdapter = mock(EntryAdapter.class);
when(entryAdapter.isAmbient()).thenReturn(true);
@@ -772,6 +774,7 @@
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
NotificationEntry entry = mock(NotificationEntry.class);
when(row.getEntry()).thenReturn(entry);
+ when(row.getEntryLegacy()).thenReturn(entry);
when(entry.isAmbient()).thenReturn(false);
EntryAdapter entryAdapter = mock(EntryAdapter.class);
when(entryAdapter.isAmbient()).thenReturn(false);
@@ -1384,6 +1387,7 @@
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.isSeenInShade()).thenReturn(true);
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ when(row.getEntryLegacy()).thenReturn(entry);
when(row.getEntry()).thenReturn(entry);
// WHEN we generate an add event
@@ -1440,6 +1444,7 @@
NotificationEntry entry = mock(NotificationEntry.class);
when(row.canViewBeCleared()).thenReturn(true);
when(row.getEntry()).thenReturn(entry);
+ when(row.getEntryLegacy()).thenReturn(entry);
when(entry.isClearable()).thenReturn(true);
EntryAdapter entryAdapter = mock(EntryAdapter.class);
when(entryAdapter.isClearable()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index a3616d2..a7f3fdc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -28,8 +28,8 @@
import static android.provider.Settings.Global.HEADS_UP_ON;
import static com.android.systemui.Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE;
-import static com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION;
import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT;
+import static com.android.systemui.shared.Flags.FLAG_AMBIENT_AOD;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.phone.CentralSurfaces.MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU;
@@ -242,7 +242,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
-@EnableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
+@EnableFlags(FLAG_AMBIENT_AOD)
public class CentralSurfacesImplTest extends SysuiTestCase {
private static final DeviceState FOLD_STATE_FOLDED = new DeviceState(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelKosmos.kt
index dc22905..56fd270 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelKosmos.kt
@@ -18,5 +18,11 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
+import com.android.systemui.shade.domain.interactor.shadeModeInteractor
-val Kosmos.detailsViewModel by Kosmos.Fixture { DetailsViewModel(currentTilesInteractor) }
+val Kosmos.detailsViewModel by Kosmos.Fixture {
+ DetailsViewModel(
+ currentTilesInteractor,
+ shadeModeInteractor
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProviderKosmos.kt
index 09f9f1c..44d7a22 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProviderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProviderKosmos.kt
@@ -18,6 +18,7 @@
import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.volume.domain.interactor.audioVolumeInteractor
@@ -25,6 +26,7 @@
Kosmos.Fixture {
VolumeDialogSliderIconProvider(
context = applicationContext,
+ uiBackgroundContext = backgroundCoroutineContext,
audioVolumeInteractor = audioVolumeInteractor,
zenModeInteractor = zenModeInteractor,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelKosmos.kt
index 8c8d024..6e43d79 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelKosmos.kt
@@ -16,9 +16,11 @@
package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
+import android.content.applicationContext
import com.android.internal.logging.uiEventLogger
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.volume.domain.interactor.audioSharingInteractor
import com.android.systemui.volume.shared.volumePanelLogger
import kotlinx.coroutines.CoroutineScope
@@ -28,7 +30,9 @@
object : AudioSharingStreamSliderViewModel.Factory {
override fun create(coroutineScope: CoroutineScope): AudioSharingStreamSliderViewModel {
return AudioSharingStreamSliderViewModel(
+ applicationContext,
coroutineScope,
+ backgroundCoroutineContext,
audioSharingInteractor,
uiEventLogger,
sliderHapticsViewModelFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
index 88c716e..47016e5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
@@ -20,6 +20,7 @@
import com.android.internal.logging.uiEventLogger
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.volume.domain.interactor.audioSharingInteractor
import com.android.systemui.volume.domain.interactor.audioVolumeInteractor
@@ -37,6 +38,7 @@
return AudioStreamSliderViewModel(
audioStream,
coroutineScope,
+ backgroundCoroutineContext,
applicationContext,
audioVolumeInteractor,
zenModeInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt
index 6875619..ed51e05 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt
@@ -19,6 +19,7 @@
import android.content.applicationContext
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.volume.mediaDeviceSessionInteractor
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
import com.android.systemui.volume.shared.volumePanelLogger
@@ -34,6 +35,7 @@
return CastVolumeSliderViewModel(
session,
coroutineScope,
+ backgroundCoroutineContext,
applicationContext,
mediaDeviceSessionInteractor,
sliderHapticsViewModelFactory,
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
index b956e44..90a9271 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
@@ -281,7 +281,6 @@
},
)
}
- downstreamSet.clear()
}
}
reset()
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
index c11eb12..81f3702 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
@@ -145,7 +145,14 @@
val conn = branchNode.upstream
severed.add(conn)
conn.removeDownstream(downstream = branchNode.schedulable)
- depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ if (conn.depthTracker.snapshotIsDirect) {
+ depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ } else {
+ depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+ depthTracker.updateIndirectRoots(
+ removals = conn.depthTracker.snapshotIndirectRoots
+ )
+ }
}
}
@@ -156,7 +163,14 @@
val conn = branchNode.upstream
severed.add(conn)
conn.removeDownstream(downstream = branchNode.schedulable)
- depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ if (conn.depthTracker.snapshotIsDirect) {
+ depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ } else {
+ depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+ depthTracker.updateIndirectRoots(
+ removals = conn.depthTracker.snapshotIndirectRoots
+ )
+ }
}
// add new
@@ -343,13 +357,8 @@
val (patchesConn, needsEval) =
getPatches(evalScope).activate(evalScope, downstream = muxNode.schedulable)
?: run {
- // Turns out we can't connect to patches, so update our depth and
- // propagate
- if (muxNode.depthTracker.setIsIndirectRoot(false)) {
- // TODO: schedules might not be necessary now that we're not
- // parallel?
- muxNode.depthTracker.schedule(evalScope.scheduler, muxNode)
- }
+ // Turns out we can't connect to patches, so update our depth
+ muxNode.depthTracker.setIsIndirectRoot(false)
return
}
muxNode.patches = patchesConn
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
index cb2c6e5..faef6a2 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
@@ -109,7 +109,14 @@
val conn: NodeConnection<V> = branchNode.upstream
severed.add(conn)
conn.removeDownstream(downstream = branchNode.schedulable)
- depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ if (conn.depthTracker.snapshotIsDirect) {
+ depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ } else {
+ depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+ depthTracker.updateIndirectRoots(
+ removals = conn.depthTracker.snapshotIndirectRoots
+ )
+ }
}
}
@@ -123,7 +130,14 @@
val conn: NodeConnection<V> = oldBranch.upstream
severed.add(conn)
conn.removeDownstream(downstream = oldBranch.schedulable)
- depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ if (conn.depthTracker.snapshotIsDirect) {
+ depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ } else {
+ depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+ depthTracker.updateIndirectRoots(
+ removals = conn.depthTracker.snapshotIndirectRoots
+ )
+ }
}
// add new
diff --git a/ravenwood/tools/hoststubgen/Android.bp b/ravenwood/tools/hoststubgen/Android.bp
index 004834e..e605318 100644
--- a/ravenwood/tools/hoststubgen/Android.bp
+++ b/ravenwood/tools/hoststubgen/Android.bp
@@ -99,6 +99,7 @@
"ow2-asm-commons",
"ow2-asm-tree",
"ow2-asm-util",
+ "apache-commons-compress",
],
}
diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/Utils.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/Utils.kt
index b2af782..a62f66d 100644
--- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/Utils.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/Utils.kt
@@ -16,6 +16,15 @@
package com.android.hoststubgen
import java.io.PrintWriter
+import java.util.zip.CRC32
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
+import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
+import org.apache.commons.compress.archivers.zip.ZipFile
+
+/**
+ * Whether to skip compression when adding processed entries back to a zip file.
+ */
+private const val SKIP_COMPRESSION = false
/**
* Name of this executable. Set it in the main method.
@@ -118,3 +127,29 @@
System.exit(if (success) 0 else 1 )
}
+
+/**
+ * Copy a single ZIP entry to the output.
+ */
+fun copyZipEntry(
+ inZip: ZipFile,
+ entry: ZipArchiveEntry,
+ out: ZipArchiveOutputStream,
+) {
+ inZip.getRawInputStream(entry).use { out.addRawArchiveEntry(entry, it) }
+}
+
+/**
+ * Add a single ZIP entry with data.
+ */
+fun ZipArchiveOutputStream.addBytesEntry(name: String, data: ByteArray) {
+ val newEntry = ZipArchiveEntry(name)
+ if (SKIP_COMPRESSION) {
+ newEntry.method = 0
+ newEntry.size = data.size.toLong()
+ newEntry.crc = CRC32().apply { update(data) }.value
+ }
+ putArchiveEntry(newEntry)
+ write(data)
+ closeArchiveEntry()
+}
diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/asm/ClassNodes.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/asm/ClassNodes.kt
index e2647eb..40d343a 100644
--- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/asm/ClassNodes.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/asm/ClassNodes.kt
@@ -18,21 +18,20 @@
import com.android.hoststubgen.ClassParseException
import com.android.hoststubgen.InvalidJarFileException
import com.android.hoststubgen.log
-import org.objectweb.asm.ClassReader
-import org.objectweb.asm.tree.AnnotationNode
-import org.objectweb.asm.tree.ClassNode
-import org.objectweb.asm.tree.FieldNode
-import org.objectweb.asm.tree.MethodNode
-import org.objectweb.asm.tree.TypeAnnotationNode
-import java.io.BufferedInputStream
import java.io.PrintWriter
import java.util.Arrays
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
import java.util.function.Consumer
-import java.util.zip.ZipEntry
-import java.util.zip.ZipFile
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
+import org.apache.commons.compress.archivers.zip.ZipFile
+import org.objectweb.asm.ClassReader
+import org.objectweb.asm.tree.AnnotationNode
+import org.objectweb.asm.tree.ClassNode
+import org.objectweb.asm.tree.FieldNode
+import org.objectweb.asm.tree.MethodNode
+import org.objectweb.asm.tree.TypeAnnotationNode
/**
* Stores all classes loaded from a jar file, in a form of [ClassNode]
@@ -189,7 +188,8 @@
* Load all the classes, without code.
*/
fun loadClassStructures(
- inJar: String,
+ inJar: ZipFile,
+ jarName: String,
timeCollector: Consumer<Double>? = null,
): ClassNodes {
val allClasses = ClassNodes()
@@ -201,20 +201,19 @@
val exception = AtomicReference<Throwable>()
// Called on a BG thread. Read a single jar entry and add it to [allClasses].
- fun parseClass(inZip: ZipFile, entry: ZipEntry) {
+ fun parseClass(inZip: ZipFile, entry: ZipArchiveEntry) {
try {
- inZip.getInputStream(entry).use { ins ->
- val cr = ClassReader(BufferedInputStream(ins))
- val cn = ClassNode()
- cr.accept(
- cn, ClassReader.SKIP_CODE
- or ClassReader.SKIP_DEBUG
- or ClassReader.SKIP_FRAMES
- )
- synchronized(allClasses) {
- if (!allClasses.addClass(cn)) {
- log.w("Duplicate class found: ${cn.name}")
- }
+ val classBytes = inZip.getInputStream(entry).use { it.readAllBytes() }
+ val cr = ClassReader(classBytes)
+ val cn = ClassNode()
+ cr.accept(
+ cn, ClassReader.SKIP_CODE
+ or ClassReader.SKIP_DEBUG
+ or ClassReader.SKIP_FRAMES
+ )
+ synchronized(allClasses) {
+ if (!allClasses.addClass(cn)) {
+ log.w("Duplicate class found: ${cn.name}")
}
}
} catch (e: Throwable) {
@@ -224,35 +223,30 @@
}
// Actually open the jar and read it on worker threads.
- val time = log.iTime("Reading class structure from $inJar") {
+ val time = log.iTime("Reading class structure from $jarName") {
log.withIndent {
- ZipFile(inJar).use { inZip ->
- val inEntries = inZip.entries()
-
- while (inEntries.hasMoreElements()) {
- val entry = inEntries.nextElement()
-
- if (entry.name.endsWith(".class")) {
- executor.submit {
- parseClass(inZip, entry)
- }
- } else if (entry.name.endsWith(".dex")) {
- // Seems like it's an ART jar file. We can't process it.
- // It's a fatal error.
- throw InvalidJarFileException(
- "$inJar is not a desktop jar file."
- + " It contains a *.dex file."
- )
- } else {
- // Unknown file type. Skip.
+ inJar.entries.asSequence().forEach { entry ->
+ if (entry.name.endsWith(".class")) {
+ executor.submit {
+ parseClass(inJar, entry)
}
+ } else if (entry.name.endsWith(".dex")) {
+ // Seems like it's an ART jar file. We can't process it.
+ // It's a fatal error.
+ throw InvalidJarFileException(
+ "$jarName is not a desktop jar file."
+ + " It contains a *.dex file."
+ )
+ } else {
+ // Unknown file type. Skip.
}
- // Wait for all the work to complete. (must do it before closing the zip)
- log.i("Waiting for all loaders to finish...")
- executor.shutdown()
- executor.awaitTermination(5, TimeUnit.MINUTES)
- log.i("All loaders to finished.")
}
+
+ // Wait for all the work to complete. (must do it before closing the zip)
+ log.i("Waiting for all loaders to finish...")
+ executor.shutdown()
+ executor.awaitTermination(5, TimeUnit.MINUTES)
+ log.i("All loaders to finished.")
}
// If any exception is detected, throw it.
@@ -261,13 +255,13 @@
}
if (allClasses.size == 0) {
- log.w("$inJar contains no *.class files.")
+ log.w("$jarName contains no *.class files.")
} else {
- log.i("Loaded ${allClasses.size} classes from $inJar.")
+ log.i("Loaded ${allClasses.size} classes from $jarName.")
}
}
timeCollector?.accept(time)
return allClasses
}
}
-}
\ No newline at end of file
+}
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 3335405..2edcb2a 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -19,13 +19,11 @@
import com.android.hoststubgen.dumper.ApiDumper
import com.android.hoststubgen.filters.FilterPolicy
import com.android.hoststubgen.filters.printAsTextPolicy
-import java.io.BufferedInputStream
-import java.io.BufferedOutputStream
import java.io.FileOutputStream
import java.io.PrintWriter
-import java.util.zip.ZipEntry
-import java.util.zip.ZipFile
-import java.util.zip.ZipOutputStream
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
+import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
+import org.apache.commons.compress.archivers.zip.ZipFile
/**
* Actual main class.
@@ -34,9 +32,10 @@
fun run() {
val errors = HostStubGenErrors()
val stats = HostStubGenStats()
+ val inJar = ZipFile(options.inJar.get)
// Load all classes.
- val allClasses = ClassNodes.loadClassStructures(options.inJar.get)
+ val allClasses = ClassNodes.loadClassStructures(inJar, options.inJar.get)
// Dump the classes, if specified.
options.inputJarDumpFile.ifSet {
@@ -59,7 +58,7 @@
val processor = HostStubGenClassProcessor(options, allClasses, errors, stats)
// Transform the jar.
- convert(
+ inJar.convert(
options.inJar.get,
options.outJar.get,
processor,
@@ -88,7 +87,7 @@
/**
* Convert a JAR file into "stub" and "impl" JAR files.
*/
- private fun convert(
+ private fun ZipFile.convert(
inJar: String,
outJar: String?,
processor: HostStubGenClassProcessor,
@@ -100,45 +99,39 @@
log.i("ASM CheckClassAdapter is %s", if (enableChecker) "enabled" else "disabled")
log.iTime("Transforming jar") {
- var itemIndex = 0
var numItemsProcessed = 0
var numItems = -1 // == Unknown
log.withIndent {
- // Open the input jar file and process each entry.
- ZipFile(inJar).use { inZip ->
+ val entries = entries.toList()
- numItems = inZip.size()
- val shardStart = numItems * shard / numShards
- val shardNextStart = numItems * (shard + 1) / numShards
+ numItems = entries.size
+ val shardStart = numItems * shard / numShards
+ val shardNextStart = numItems * (shard + 1) / numShards
- maybeWithZipOutputStream(outJar) { outStream ->
- val inEntries = inZip.entries()
- while (inEntries.hasMoreElements()) {
- val entry = inEntries.nextElement()
- val inShard = (shardStart <= itemIndex)
- && (itemIndex < shardNextStart)
- itemIndex++
- if (!inShard) {
- continue
- }
- convertSingleEntry(inZip, entry, outStream, processor)
- numItemsProcessed++
+ maybeWithZipOutputStream(outJar) { outStream ->
+ entries.forEachIndexed { itemIndex, entry ->
+ val inShard = (shardStart <= itemIndex)
+ && (itemIndex < shardNextStart)
+ if (!inShard) {
+ return@forEachIndexed
}
- log.i("Converted all entries.")
+ convertSingleEntry(this, entry, outStream, processor)
+ numItemsProcessed++
}
- outJar?.let { log.i("Created: $it") }
+ log.i("Converted all entries.")
}
+ outJar?.let { log.i("Created: $it") }
}
log.i("%d / %d item(s) processed.", numItemsProcessed, numItems)
}
}
- private fun <T> maybeWithZipOutputStream(filename: String?, block: (ZipOutputStream?) -> T): T {
+ private fun <T> maybeWithZipOutputStream(filename: String?, block: (ZipArchiveOutputStream?) -> T): T {
if (filename == null) {
return block(null)
}
- return ZipOutputStream(BufferedOutputStream(FileOutputStream(filename))).use(block)
+ return ZipArchiveOutputStream(FileOutputStream(filename).buffered()).use(block)
}
/**
@@ -146,8 +139,8 @@
*/
private fun convertSingleEntry(
inZip: ZipFile,
- entry: ZipEntry,
- outStream: ZipOutputStream?,
+ entry: ZipArchiveEntry,
+ outStream: ZipArchiveOutputStream?,
processor: HostStubGenClassProcessor
) {
log.d("Entry: %s", entry.name)
@@ -181,32 +174,12 @@
}
/**
- * Copy a single ZIP entry to the output.
- */
- private fun copyZipEntry(
- inZip: ZipFile,
- entry: ZipEntry,
- out: ZipOutputStream,
- ) {
- // TODO: It seems like copying entries this way is _very_ slow,
- // even with out.setLevel(0). Look for other ways to do it.
-
- inZip.getInputStream(entry).use { ins ->
- // Copy unknown entries as is to the impl out. (but not to the stub out.)
- val outEntry = ZipEntry(entry.name)
- out.putNextEntry(outEntry)
- ins.transferTo(out)
- out.closeEntry()
- }
- }
-
- /**
* Convert a single class.
*/
private fun processSingleClass(
inZip: ZipFile,
- entry: ZipEntry,
- outStream: ZipOutputStream?,
+ entry: ZipArchiveEntry,
+ outStream: ZipArchiveOutputStream?,
processor: HostStubGenClassProcessor
) {
val classInternalName = entry.name.replaceFirst("\\.class$".toRegex(), "")
@@ -227,12 +200,10 @@
if (outStream != null) {
log.v("Creating class: %s Policy: %s", classInternalName, classPolicy)
log.withIndent {
- BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
- val newEntry = ZipEntry(newName)
- outStream.putNextEntry(newEntry)
- val classBytecode = bis.readAllBytes()
- outStream.write(processor.processClassBytecode(classBytecode))
- outStream.closeEntry()
+ inZip.getInputStream(entry).use { zis ->
+ var classBytecode = zis.readAllBytes()
+ classBytecode = processor.processClassBytecode(classBytecode)
+ outStream.addBytesEntry(newName, classBytecode)
}
}
}
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
index 04e3bda2..8e36323 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
@@ -17,17 +17,17 @@
import com.android.hoststubgen.GeneralUserErrorException
import com.android.hoststubgen.HostStubGenClassProcessor
+import com.android.hoststubgen.addBytesEntry
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.zipEntryNameToClassName
+import com.android.hoststubgen.copyZipEntry
import com.android.hoststubgen.executableName
import com.android.hoststubgen.log
import com.android.platform.test.ravenwood.ravenizer.adapter.RunnerRewritingAdapter
-import java.io.BufferedInputStream
-import java.io.BufferedOutputStream
import java.io.FileOutputStream
-import java.util.zip.ZipEntry
-import java.util.zip.ZipFile
-import java.util.zip.ZipOutputStream
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
+import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
+import org.apache.commons.compress.archivers.zip.ZipFile
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter
@@ -93,13 +93,14 @@
val stats = RavenizerStats()
stats.totalTime = log.nTime {
- val allClasses = ClassNodes.loadClassStructures(options.inJar.get) {
+ val inJar = ZipFile(options.inJar.get)
+ val allClasses = ClassNodes.loadClassStructures(inJar, options.inJar.get) {
stats.loadStructureTime = it
}
val processor = HostStubGenClassProcessor(options, allClasses)
- process(
- options.inJar.get,
+ inJar.process(
+ options.outJar.get,
options.outJar.get,
options.enableValidation.get,
options.fatalValidation.get,
@@ -111,7 +112,7 @@
log.i(stats.toString())
}
- private fun process(
+ private fun ZipFile.process(
inJar: String,
outJar: String,
enableValidation: Boolean,
@@ -138,40 +139,34 @@
}
stats.totalProcessTime = log.vTime("$executableName processing $inJar") {
- ZipFile(inJar).use { inZip ->
- val inEntries = inZip.entries()
+ ZipArchiveOutputStream(FileOutputStream(outJar).buffered()).use { outZip ->
+ entries.asSequence().forEach { entry ->
+ stats.totalEntries++
+ if (entry.name.endsWith(".dex")) {
+ // Seems like it's an ART jar file. We can't process it.
+ // It's a fatal error.
+ throw GeneralUserErrorException(
+ "$inJar is not a desktop jar file. It contains a *.dex file."
+ )
+ }
- stats.totalEntries = inZip.size()
+ if (stripMockito && entry.name.isMockitoFile()) {
+ // Skip this entry
+ return@forEach
+ }
- ZipOutputStream(BufferedOutputStream(FileOutputStream(outJar))).use { outZip ->
- while (inEntries.hasMoreElements()) {
- val entry = inEntries.nextElement()
+ val className = zipEntryNameToClassName(entry.name)
- if (entry.name.endsWith(".dex")) {
- // Seems like it's an ART jar file. We can't process it.
- // It's a fatal error.
- throw GeneralUserErrorException(
- "$inJar is not a desktop jar file. It contains a *.dex file."
- )
- }
+ if (className != null) {
+ stats.totalClasses += 1
+ }
- if (stripMockito && entry.name.isMockitoFile()) {
- // Skip this entry
- continue
- }
-
- val className = zipEntryNameToClassName(entry.name)
-
- if (className != null) {
- stats.totalClasses += 1
- }
-
- if (className != null &&
- shouldProcessClass(processor.allClasses, className)) {
- processSingleClass(inZip, entry, outZip, processor, stats)
- } else {
- // Too slow, let's use merge_zips to bring back the original classes.
- copyZipEntry(inZip, entry, outZip, stats)
+ if (className != null &&
+ shouldProcessClass(processor.allClasses, className)) {
+ processSingleClass(this, entry, outZip, processor, stats)
+ } else {
+ stats.totalCopyTime += log.nTime {
+ copyZipEntry(this, entry, outZip)
}
}
}
@@ -179,53 +174,25 @@
}
}
- /**
- * Copy a single ZIP entry to the output.
- */
- private fun copyZipEntry(
- inZip: ZipFile,
- entry: ZipEntry,
- out: ZipOutputStream,
- stats: RavenizerStats,
- ) {
- stats.totalCopyTime += log.nTime {
- inZip.getInputStream(entry).use { ins ->
- // Copy unknown entries as is to the impl out. (but not to the stub out.)
- val outEntry = ZipEntry(entry.name)
- outEntry.method = 0
- outEntry.size = entry.size
- outEntry.crc = entry.crc
- out.putNextEntry(outEntry)
-
- ins.transferTo(out)
-
- out.closeEntry()
- }
- }
- }
-
private fun processSingleClass(
inZip: ZipFile,
- entry: ZipEntry,
- outZip: ZipOutputStream,
+ entry: ZipArchiveEntry,
+ outZip: ZipArchiveOutputStream,
processor: HostStubGenClassProcessor,
stats: RavenizerStats,
) {
stats.processedClasses += 1
- val newEntry = ZipEntry(entry.name)
- outZip.putNextEntry(newEntry)
-
- BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
- var classBytes = bis.readBytes()
+ inZip.getInputStream(entry).use { zis ->
+ var classBytes = zis.readAllBytes()
stats.totalRavenizeTime += log.vTime("Ravenize ${entry.name}") {
classBytes = ravenizeSingleClass(entry, classBytes, processor.allClasses)
}
stats.totalHostStubGenTime += log.vTime("HostStubGen ${entry.name}") {
classBytes = processor.processClassBytecode(classBytes)
}
- outZip.write(classBytes)
+ // TODO: if the class does not change, use copyZipEntry
+ outZip.addBytesEntry(entry.name, classBytes)
}
- outZip.closeEntry()
}
/**
@@ -237,7 +204,7 @@
}
private fun ravenizeSingleClass(
- entry: ZipEntry,
+ entry: ZipArchiveEntry,
input: ByteArray,
allClasses: ClassNodes,
): ByteArray {
diff --git a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
index 94cef41..edcf574 100644
--- a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility;
import android.Manifest;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.app.Notification;
@@ -149,11 +150,7 @@
}
if (state == TelephonyManager.CALL_STATE_IDLE) {
- if (mIsCommDeviceChangedRegistered) {
- mIsCommDeviceChangedRegistered = false;
- mAudioManager.removeOnCommunicationDeviceChangedListener(
- mCommDeviceChangedListener);
- }
+ removeOnCommunicationDeviceChangedListenerIfNeeded(mCommDeviceChangedListener);
dismissNotificationIfNeeded();
if (mHearingDevice != null) {
@@ -172,10 +169,8 @@
if (mHearingDevice != null) {
showNotificationIfNeeded();
} else {
- mAudioManager.addOnCommunicationDeviceChangedListener(
- mCommDeviceChangedExecutor,
+ addOnCommunicationDeviceChangedListenerIfNeeded(mCommDeviceChangedExecutor,
mCommDeviceChangedListener);
- mIsCommDeviceChangedRegistered = true;
}
} else {
mHearingDevice = getSupportedInputHearingDeviceInfo(
@@ -187,6 +182,27 @@
}
}
+ private void addOnCommunicationDeviceChangedListenerIfNeeded(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AudioManager.OnCommunicationDeviceChangedListener listener) {
+ if (mIsCommDeviceChangedRegistered) {
+ return;
+ }
+
+ mIsCommDeviceChangedRegistered = true;
+ mAudioManager.addOnCommunicationDeviceChangedListener(executor, listener);
+ }
+
+ private void removeOnCommunicationDeviceChangedListenerIfNeeded(
+ @NonNull AudioManager.OnCommunicationDeviceChangedListener listener) {
+ if (!mIsCommDeviceChangedRegistered) {
+ return;
+ }
+
+ mAudioManager.removeOnCommunicationDeviceChangedListener(listener);
+ mIsCommDeviceChangedRegistered = false;
+ }
+
private void showNotificationIfNeeded() {
if (mIsNotificationShown) {
return;
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 658ea4c..193d827 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -168,7 +168,6 @@
private AdbConnectionInfo mAdbConnectionInfo = new AdbConnectionInfo();
// Polls for a tls port property when adb wifi is enabled
private AdbConnectionPortPoller mConnectionPortPoller;
- private final PortListenerImpl mPortListener = new PortListenerImpl();
private final Ticker mTicker;
public AdbDebuggingManager(Context context) {
@@ -323,10 +322,6 @@
}
}
- interface AdbConnectionPortListener {
- void onPortReceived(int port);
- }
-
/**
* This class will poll for a period of time for adbd to write the port
* it connected to.
@@ -336,16 +331,11 @@
* port through different means. A better fix would be to always start AdbDebuggingManager, but
* it needs to adjust accordingly on whether ro.adb.secure is set.
*/
- static class AdbConnectionPortPoller extends Thread {
+ private class AdbConnectionPortPoller extends Thread {
private final String mAdbPortProp = "service.adb.tls.port";
- private AdbConnectionPortListener mListener;
private final int mDurationSecs = 10;
private AtomicBoolean mCanceled = new AtomicBoolean(false);
- AdbConnectionPortPoller(AdbConnectionPortListener listener) {
- mListener = listener;
- }
-
@Override
public void run() {
Slog.d(TAG, "Starting adb port property poller");
@@ -362,13 +352,22 @@
// to start the server. Otherwise we should have a valid port.
int port = SystemProperties.getInt(mAdbPortProp, Integer.MAX_VALUE);
if (port == -1 || (port > 0 && port <= 65535)) {
- mListener.onPortReceived(port);
+ onPortReceived(port);
return;
}
SystemClock.sleep(1000);
}
Slog.w(TAG, "Failed to receive adb connection port");
- mListener.onPortReceived(-1);
+ onPortReceived(-1);
+ }
+
+ private void onPortReceived(int port) {
+ Slog.d(TAG, "Received tls port=" + port);
+ Message msg = mHandler.obtainMessage(port > 0
+ ? AdbDebuggingHandler.MSG_SERVER_CONNECTED
+ : AdbDebuggingHandler.MSG_SERVER_DISCONNECTED);
+ msg.obj = port;
+ mHandler.sendMessage(msg);
}
public void cancelAndWait() {
@@ -382,17 +381,6 @@
}
}
- class PortListenerImpl implements AdbConnectionPortListener {
- public void onPortReceived(int port) {
- Slog.d(TAG, "Received tls port=" + port);
- Message msg = mHandler.obtainMessage(port > 0
- ? AdbDebuggingHandler.MSG_SERVER_CONNECTED
- : AdbDebuggingHandler.MSG_SERVER_DISCONNECTED);
- msg.obj = port;
- mHandler.sendMessage(msg);
- }
- }
-
@VisibleForTesting
static class AdbDebuggingThread extends Thread {
private boolean mStopped;
@@ -800,7 +788,6 @@
// === Messages we can send to adbd ===========
static final String MSG_DISCONNECT_DEVICE = "DD";
- static final String MSG_DISABLE_ADBDWIFI = "DA";
@Nullable @VisibleForTesting AdbKeyStore mAdbKeyStore;
@@ -1088,8 +1075,7 @@
mContext.registerReceiver(mBroadcastReceiver, intentFilter);
SystemProperties.set(AdbService.WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
- mConnectionPortPoller =
- new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+ mConnectionPortPoller = new AdbDebuggingManager.AdbConnectionPortPoller();
mConnectionPortPoller.start();
startAdbDebuggingThread();
@@ -1106,9 +1092,6 @@
setAdbConnectionInfo(null);
mContext.unregisterReceiver(mBroadcastReceiver);
- if (mThread != null) {
- mThread.sendResponse(MSG_DISABLE_ADBDWIFI);
- }
onAdbdWifiServerDisconnected(-1);
stopAdbDebuggingThread();
break;
@@ -1138,8 +1121,7 @@
mContext.registerReceiver(mBroadcastReceiver, intentFilter);
SystemProperties.set(AdbService.WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
- mConnectionPortPoller =
- new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+ mConnectionPortPoller = new AdbDebuggingManager.AdbConnectionPortPoller();
mConnectionPortPoller.start();
startAdbDebuggingThread();
@@ -1257,7 +1239,7 @@
if (mAdbWifiEnabled) {
// In scenarios where adbd is restarted, the tls port may change.
mConnectionPortPoller =
- new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+ new AdbDebuggingManager.AdbConnectionPortPoller();
mConnectionPortPoller.start();
}
break;
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 40f7c87..d12a0a2 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -21,10 +21,8 @@
import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
-import android.debug.AdbManager;
import android.debug.AdbManagerInternal;
import android.debug.AdbTransportType;
import android.debug.FingerprintAndPairDevice;
@@ -40,10 +38,8 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.provider.Settings;
import android.service.adb.AdbServiceDumpProto;
-import android.sysprop.AdbProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -63,7 +59,6 @@
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* The Android Debug Bridge (ADB) service. This controls the availability of ADB and authorization
@@ -85,12 +80,6 @@
*/
static final String CTL_STOP = "ctl.stop";
- // The tcp port adb is currently using
- AtomicInteger mConnectionPort = new AtomicInteger(-1);
-
- private final AdbConnectionPortListener mPortListener = new AdbConnectionPortListener();
- private AdbDebuggingManager.AdbConnectionPortPoller mConnectionPortPoller;
-
private final RemoteCallbackList<IAdbCallback> mCallbacks = new RemoteCallbackList<>();
/**
* Manages the service lifecycle for {@code AdbService} in {@code SystemServer}.
@@ -404,39 +393,6 @@
Slog.d(TAG, "Unregistering callback " + callback);
mCallbacks.unregister(callback);
}
- /**
- * This listener is only used when ro.adb.secure=0. Otherwise, AdbDebuggingManager will
- * do this.
- */
- class AdbConnectionPortListener implements AdbDebuggingManager.AdbConnectionPortListener {
- public void onPortReceived(int port) {
- if (port > 0 && port <= 65535) {
- mConnectionPort.set(port);
- } else {
- mConnectionPort.set(-1);
- // Turn off wifi debugging, since the server did not start.
- try {
- Settings.Global.putInt(mContentResolver,
- Settings.Global.ADB_WIFI_ENABLED, 0);
- } catch (SecurityException e) {
- // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't
- // be changed.
- Slog.d(TAG, "ADB_ENABLED is restricted.");
- }
- }
- broadcastPortInfo(mConnectionPort.get());
- }
- }
-
- private void broadcastPortInfo(int port) {
- Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
- intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, (port >= 0)
- ? AdbManager.WIRELESS_STATUS_CONNECTED
- : AdbManager.WIRELESS_STATUS_DISCONNECTED);
- intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
- AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
- Slog.i(TAG, "sent port broadcast port=" + port);
- }
private void startAdbd() {
SystemProperties.set(CTL_START, ADBD);
@@ -470,20 +426,11 @@
} else if (transportType == AdbTransportType.WIFI && enable != mIsAdbWifiEnabled) {
mIsAdbWifiEnabled = enable;
if (mIsAdbWifiEnabled) {
- if (!AdbProperties.secure().orElse(false)) {
- // Start adbd. If this is secure adb, then we defer enabling adb over WiFi.
- SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
- mConnectionPortPoller =
- new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
- mConnectionPortPoller.start();
- }
+ // Start adb over WiFi.
+ SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
} else {
// Stop adb over WiFi.
SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "0");
- if (mConnectionPortPoller != null) {
- mConnectionPortPoller.cancelAndWait();
- mConnectionPortPoller = null;
- }
}
} else {
// No change
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index bf7f194..6b3661a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -929,7 +929,8 @@
private final Object mAbsoluteVolumeDeviceInfoMapLock = new Object();
// Devices where the framework sends a full scale audio signal, and controls the volume of
// the external audio system separately.
- // For possible volume behaviors, see {@link AudioManager.AbsoluteDeviceVolumeBehavior}.
+ // For possible volume behaviors, see
+ // {@link AudioDeviceVolumeManager.AbsoluteDeviceVolumeBehavior}.
@GuardedBy("mAbsoluteVolumeDeviceInfoMapLock")
Map<Integer, AbsoluteVolumeDeviceInfo> mAbsoluteVolumeDeviceInfoMap = new ArrayMap<>();
@@ -942,7 +943,7 @@
private final List<VolumeInfo> mVolumeInfos;
private final IAudioDeviceVolumeDispatcher mCallback;
private final boolean mHandlesVolumeAdjustment;
- private @AudioManager.AbsoluteDeviceVolumeBehavior int mDeviceVolumeBehavior;
+ private @AudioDeviceVolumeManager.AbsoluteDeviceVolumeBehavior int mDeviceVolumeBehavior;
private AbsoluteVolumeDeviceInfo(
AudioService parent,
@@ -950,7 +951,7 @@
List<VolumeInfo> volumeInfos,
IAudioDeviceVolumeDispatcher callback,
boolean handlesVolumeAdjustment,
- @AudioManager.AbsoluteDeviceVolumeBehavior int behavior) {
+ @AudioDeviceVolumeManager.AbsoluteDeviceVolumeBehavior int behavior) {
this.mParent = parent;
this.mDevice = device;
this.mVolumeInfos = volumeInfos;
@@ -8173,7 +8174,7 @@
IAudioDeviceVolumeDispatcher cb, String packageName,
AudioDeviceAttributes device, List<VolumeInfo> volumes,
boolean handlesVolumeAdjustment,
- @AudioManager.AbsoluteDeviceVolumeBehavior int deviceVolumeBehavior) {
+ @AudioDeviceVolumeManager.AbsoluteDeviceVolumeBehavior int deviceVolumeBehavior) {
// verify permissions
if (mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_ROUTING)
!= PackageManager.PERMISSION_GRANTED
@@ -8240,12 +8241,13 @@
@android.annotation.EnforcePermission(anyOf = {
MODIFY_AUDIO_ROUTING, MODIFY_AUDIO_SETTINGS_PRIVILEGED })
public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @Nullable String pkgName) {
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int deviceVolumeBehavior,
+ @Nullable String pkgName) {
// verify permissions
super.setDeviceVolumeBehavior_enforcePermission();
// verify arguments
Objects.requireNonNull(device);
- AudioManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
+ AudioDeviceVolumeManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
device = retrieveBluetoothAddress(device);
@@ -8268,7 +8270,8 @@
}
private void setDeviceVolumeBehaviorInternal(@NonNull AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @NonNull String caller) {
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int deviceVolumeBehavior,
+ @NonNull String caller) {
int audioSystemDeviceOut = device.getInternalType();
boolean volumeBehaviorChanged = false;
// update device masks based on volume behavior
@@ -8323,7 +8326,7 @@
@android.annotation.EnforcePermission(anyOf = {
MODIFY_AUDIO_ROUTING, QUERY_AUDIO_STATE, MODIFY_AUDIO_SETTINGS_PRIVILEGED
})
- public @AudioManager.DeviceVolumeBehavior
+ public @AudioDeviceVolumeManager.DeviceVolumeBehavior
int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
// verify permissions
super.getDeviceVolumeBehavior_enforcePermission();
@@ -8335,7 +8338,7 @@
return getDeviceVolumeBehaviorInt(device);
}
- private @AudioManager.DeviceVolumeBehavior
+ private @AudioDeviceVolumeManager.DeviceVolumeBehavior
int getDeviceVolumeBehaviorInt(@NonNull AudioDeviceAttributes device) {
// Get the internal type set by the AudioDeviceAttributes constructor which is always more
// exact (avoids double conversions) than a conversion from SDK type via
@@ -15354,7 +15357,8 @@
/**
* Returns whether the input device uses absolute volume behavior, including its variants.
- * For included volume behaviors, see {@link AudioManager.AbsoluteDeviceVolumeBehavior}.
+ * For included volume behaviors, see
+ * {@link AudioDeviceVolumeManager.AbsoluteDeviceVolumeBehavior}.
* <p>This is distinct from Bluetooth A2DP absolute volume behavior
* ({@link #isA2dpAbsoluteVolumeDevice}).
*/
@@ -15381,7 +15385,7 @@
}
private void persistDeviceVolumeBehavior(int deviceType,
- @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior) {
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int deviceVolumeBehavior) {
if (DEBUG_VOL) {
Log.d(TAG, "Persisting Volume Behavior for DeviceType: " + deviceType);
}
@@ -15396,7 +15400,7 @@
}
}
- @AudioManager.DeviceVolumeBehaviorState
+ @AudioDeviceVolumeManager.DeviceVolumeBehaviorState
private int retrieveStoredDeviceVolumeBehavior(int deviceType) {
return mSettings.getSystemIntForUser(mContentResolver,
getSettingsNameForDeviceVolumeBehavior(deviceType),
diff --git a/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java b/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java
index ab86433..62c3dbd 100644
--- a/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java
+++ b/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceVolumeManager;
+import android.media.AudioManager;
import android.media.VolumeInfo;
import java.util.concurrent.Executor;
@@ -53,7 +54,7 @@
/**
* Wrapper for {@link AudioDeviceVolumeManager#setDeviceAbsoluteVolumeBehavior(
- * AudioDeviceAttributes, VolumeInfo, Executor, OnAudioDeviceVolumeChangedListener, boolean)}
+ * AudioDeviceAttributes, VolumeInfo, boolean, Executor, OnAudioDeviceVolumeChangedListener)}
*/
void setDeviceAbsoluteVolumeBehavior(
@NonNull AudioDeviceAttributes device,
@@ -64,7 +65,7 @@
/**
* Wrapper for {@link AudioDeviceVolumeManager#setDeviceAbsoluteVolumeAdjustOnlyBehavior(
- * AudioDeviceAttributes, VolumeInfo, Executor, OnAudioDeviceVolumeChangedListener, boolean)}
+ * AudioDeviceAttributes, VolumeInfo, boolean, Executor, OnAudioDeviceVolumeChangedListener)}
*/
void setDeviceAbsoluteVolumeAdjustOnlyBehavior(
@NonNull AudioDeviceAttributes device,
@@ -72,4 +73,16 @@
boolean handlesVolumeAdjustment,
@NonNull @CallbackExecutor Executor executor,
@NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener);
+
+ /**
+ * Wraps {@link AudioDeviceVolumeManager#getDeviceVolumeBehavior(AudioDeviceAttributes)}
+ */
+ @AudioManager.DeviceVolumeBehavior
+ int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device);
+
+ /**
+ * Wraps {@link AudioDeviceVolumeManager#setDeviceVolumeBehavior(AudioDeviceAttributes, int)}
+ */
+ void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int deviceVolumeBehavior);
}
diff --git a/services/core/java/com/android/server/hdmi/AudioManagerWrapper.java b/services/core/java/com/android/server/hdmi/AudioManagerWrapper.java
index fd4dd51..6d01e2d 100644
--- a/services/core/java/com/android/server/hdmi/AudioManagerWrapper.java
+++ b/services/core/java/com/android/server/hdmi/AudioManagerWrapper.java
@@ -85,18 +85,6 @@
void setWiredDeviceConnectionState(int device, int state, String address, String name);
/**
- * Wraps {@link AudioManager#getDeviceVolumeBehavior(AudioDeviceAttributes)}
- */
- @AudioManager.DeviceVolumeBehavior
- int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device);
-
- /**
- * Wraps {@link AudioManager#setDeviceVolumeBehavior(AudioDeviceAttributes, int)}
- */
- void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior);
-
- /**
* Wraps {@link AudioManager#getDevicesForAttributes(AudioAttributes)}
*/
@NonNull
diff --git a/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java b/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java
index 10cbb00..02d8579 100644
--- a/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java
+++ b/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java
@@ -16,11 +16,14 @@
package com.android.server.hdmi;
+import static android.media.audio.Flags.unifyAbsoluteVolumeManagement;
+
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.content.Context;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceVolumeManager;
+import android.media.AudioManager;
import android.media.VolumeInfo;
import java.util.concurrent.Executor;
@@ -38,9 +41,11 @@
private static final String TAG = "AudioDeviceVolumeManagerWrapper";
private final AudioDeviceVolumeManager mAudioDeviceVolumeManager;
+ private final AudioManager mAudioManager;
public DefaultAudioDeviceVolumeManagerWrapper(Context context) {
mAudioDeviceVolumeManager = new AudioDeviceVolumeManager(context);
+ mAudioManager = context.getSystemService(AudioManager.class);
}
@Override
@@ -78,4 +83,24 @@
mAudioDeviceVolumeManager.setDeviceAbsoluteVolumeAdjustOnlyBehavior(device, volume,
handlesVolumeAdjustment, executor, vclistener);
}
+
+ @Override
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior
+ public int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
+ if (!unifyAbsoluteVolumeManagement()) {
+ int deviceBehavior = mAudioManager.getDeviceVolumeBehavior(device);
+ return deviceBehavior;
+ }
+ return mAudioDeviceVolumeManager.getDeviceVolumeBehavior(device);
+ }
+
+ @Override
+ public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int deviceVolumeBehavior) {
+ if (!unifyAbsoluteVolumeManagement()) {
+ int deviceBehavior = deviceVolumeBehavior;
+ mAudioManager.setDeviceVolumeBehavior(device, deviceBehavior);
+ }
+ mAudioDeviceVolumeManager.setDeviceVolumeBehavior(device, deviceVolumeBehavior);
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/DefaultAudioManagerWrapper.java b/services/core/java/com/android/server/hdmi/DefaultAudioManagerWrapper.java
index 061e145..6627154 100644
--- a/services/core/java/com/android/server/hdmi/DefaultAudioManagerWrapper.java
+++ b/services/core/java/com/android/server/hdmi/DefaultAudioManagerWrapper.java
@@ -94,18 +94,6 @@
}
@Override
- @AudioManager.DeviceVolumeBehavior
- public int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
- return mAudioManager.getDeviceVolumeBehavior(device);
- }
-
- @Override
- public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior) {
- mAudioManager.setDeviceVolumeBehavior(device, deviceVolumeBehavior);
- }
-
- @Override
@NonNull
public List<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java b/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java
index 9118c46..574e484 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java
@@ -167,7 +167,11 @@
private boolean handleReportPowerStatus(int powerStatus) {
switch (powerStatus) {
case HdmiControlManager.POWER_STATUS_ON:
- selectDevice();
+ if (tv().getActiveSource().physicalAddress == mTarget.getPhysicalAddress()) {
+ finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+ } else {
+ selectDevice();
+ }
return true;
case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY:
if (mPowerStatusCounter < 4) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index fdd0ef2..41b0b4d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -4595,7 +4595,7 @@
* Wrapper for {@link AudioManager#getDeviceVolumeBehavior} that takes advantage of cached
* results for the volume behaviors of HDMI audio devices.
*/
- @AudioManager.DeviceVolumeBehavior
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior
private int getDeviceVolumeBehavior(AudioDeviceAttributes device) {
if (AVB_AUDIO_OUTPUT_DEVICES.contains(device)) {
synchronized (mLock) {
@@ -4604,7 +4604,7 @@
}
}
}
- return getAudioManager().getDeviceVolumeBehavior(device);
+ return getAudioDeviceVolumeManager().getDeviceVolumeBehavior(device);
}
/**
@@ -4695,7 +4695,7 @@
// Condition 3: All AVB-capable audio outputs already use full/absolute volume behavior
// We only need to check the first AVB-capable audio output because only TV panels
// have more than one of them, and they always have the same volume behavior.
- @AudioManager.DeviceVolumeBehavior int currentVolumeBehavior =
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int currentVolumeBehavior =
getDeviceVolumeBehavior(getAvbCapableAudioOutputDevices().get(0));
boolean alreadyUsingFullOrAbsoluteVolume =
FULL_AND_ABSOLUTE_VOLUME_BEHAVIORS.contains(currentVolumeBehavior);
@@ -4719,7 +4719,8 @@
// Condition 5: The System Audio device supports <Set Audio Volume Level>
switch (systemAudioDeviceInfo.getDeviceFeatures().getSetAudioVolumeLevelSupport()) {
case DeviceFeatures.FEATURE_SUPPORTED:
- if (currentVolumeBehavior != AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
+ if (currentVolumeBehavior
+ != AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
// Start an action that will call enableAbsoluteVolumeBehavior
// once the System Audio device sends <Report Audio Status>
localCecDevice.startNewAvbAudioStatusAction(
@@ -4731,13 +4732,15 @@
// This allows the device to display numeric volume UI for the System Audio device.
if (tv() != null && mNumericSoundbarVolumeUiOnTvFeatureFlagEnabled) {
if (currentVolumeBehavior
- != AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY) {
+ != AudioDeviceVolumeManager
+ .DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY) {
// If we're currently using absolute volume behavior, switch to full volume
// behavior until we successfully adopt adjust-only absolute volume behavior
- if (currentVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
+ if (currentVolumeBehavior
+ == AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) {
- getAudioManager().setDeviceVolumeBehavior(device,
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ getAudioDeviceVolumeManager().setDeviceVolumeBehavior(device,
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
}
// Start an action that will call enableAbsoluteVolumeBehavior
@@ -4750,7 +4753,8 @@
}
return;
case DeviceFeatures.FEATURE_SUPPORT_UNKNOWN:
- if (currentVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
+ if (currentVolumeBehavior
+ == AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
switchToFullVolumeBehavior();
}
localCecDevice.querySetAudioVolumeLevelSupport(
@@ -4773,8 +4777,8 @@
for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) {
if (ABSOLUTE_VOLUME_BEHAVIORS.contains(getDeviceVolumeBehavior(device))) {
- getAudioManager().setDeviceVolumeBehavior(device,
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ getAudioDeviceVolumeManager().setDeviceVolumeBehavior(device,
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
}
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index 6db62c8..ccb9e3e 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -301,7 +301,8 @@
throw new IllegalArgumentException(
"Unknown session ID in closeSession: id=" + sessionId);
}
- halCloseEndpointSession(sessionId, ContextHubServiceUtil.toHalReason(reason));
+ mEndpointManager.halCloseEndpointSession(
+ sessionId, ContextHubServiceUtil.toHalReason(reason));
}
@Override
@@ -312,7 +313,7 @@
// Iterate in reverse since cleanupSessionResources will remove the entry
for (int i = mSessionMap.size() - 1; i >= 0; i--) {
int id = mSessionMap.keyAt(i);
- halCloseEndpointSessionNoThrow(id, Reason.ENDPOINT_GONE);
+ mEndpointManager.halCloseEndpointSessionNoThrow(id, Reason.ENDPOINT_GONE);
cleanupSessionResources(id);
}
}
@@ -444,7 +445,8 @@
int id = mSessionMap.keyAt(i);
HubEndpointInfo target = mSessionMap.get(id).getRemoteEndpointInfo();
if (!hasEndpointPermissions(target)) {
- halCloseEndpointSessionNoThrow(id, Reason.PERMISSION_DENIED);
+ mEndpointManager.halCloseEndpointSessionNoThrow(
+ id, Reason.PERMISSION_DENIED);
onCloseEndpointSession(id, Reason.PERMISSION_DENIED);
// Resource cleanup is done in onCloseEndpointSession
}
@@ -503,17 +505,7 @@
mContextHubEndpointCallback.asBinder().linkToDeath(this, 0 /* flags */);
}
- /* package */ void onEndpointSessionOpenRequest(
- int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
- Optional<Byte> error =
- onEndpointSessionOpenRequestInternal(sessionId, initiator, serviceDescriptor);
- if (error.isPresent()) {
- halCloseEndpointSessionNoThrow(sessionId, error.get());
- onCloseEndpointSession(sessionId, error.get());
- // Resource cleanup is done in onCloseEndpointSession
- }
- }
-
+ /** Handle close endpoint callback to the client side */
/* package */ void onCloseEndpointSession(int sessionId, byte reason) {
if (!cleanupSessionResources(sessionId)) {
Log.w(TAG, "Unknown session ID in onCloseEndpointSession: id=" + sessionId);
@@ -585,7 +577,7 @@
}
}
- private Optional<Byte> onEndpointSessionOpenRequestInternal(
+ /* package */ Optional<Byte> onEndpointSessionOpenRequest(
int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
if (!hasEndpointPermissions(initiator)) {
Log.e(
@@ -594,15 +586,41 @@
+ initiator
+ " doesn't have permission for "
+ mEndpointInfo);
- return Optional.of(Reason.PERMISSION_DENIED);
+ byte reason = Reason.PERMISSION_DENIED;
+ onCloseEndpointSession(sessionId, reason);
+ return Optional.of(reason);
}
+ // Check & handle error cases for duplicated session id.
+ final boolean existingSession;
+ final boolean existingSessionActive;
synchronized (mOpenSessionLock) {
if (hasSessionId(sessionId)) {
- Log.e(TAG, "Existing session in onEndpointSessionOpenRequest: id=" + sessionId);
- return Optional.of(Reason.UNSPECIFIED);
+ existingSession = true;
+ existingSessionActive = mSessionMap.get(sessionId).isActive();
+ Log.w(
+ TAG,
+ "onEndpointSessionOpenRequest: "
+ + "Existing session ID: "
+ + sessionId
+ + ", isActive: "
+ + existingSessionActive);
+ } else {
+ existingSession = false;
+ existingSessionActive = false;
+ mSessionMap.put(sessionId, new Session(initiator, true));
}
- mSessionMap.put(sessionId, new Session(initiator, true));
+ }
+
+ if (existingSession) {
+ if (existingSessionActive) {
+ // Existing session is already active, call onSessionOpenComplete.
+ openSessionRequestComplete(sessionId);
+ return Optional.empty();
+ }
+ // Reject the session open request for now. Consider invalidating previous pending
+ // session open request based on timeout.
+ return Optional.of(Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
}
boolean success =
@@ -610,7 +628,11 @@
(consumer) ->
consumer.onSessionOpenRequest(
sessionId, initiator, serviceDescriptor));
- return success ? Optional.empty() : Optional.of(Reason.UNSPECIFIED);
+ byte reason = Reason.UNSPECIFIED;
+ if (!success) {
+ onCloseEndpointSession(sessionId, reason);
+ }
+ return success ? Optional.empty() : Optional.of(reason);
}
private byte onMessageReceivedInternal(int sessionId, HubMessage message) {
@@ -657,29 +679,6 @@
}
/**
- * Calls the HAL closeEndpointSession API.
- *
- * @param sessionId The session ID to close
- * @param halReason The HAL reason
- */
- private void halCloseEndpointSession(int sessionId, byte halReason) throws RemoteException {
- try {
- mHubInterface.closeEndpointSession(sessionId, halReason);
- } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
- throw e;
- }
- }
-
- /** Same as halCloseEndpointSession but does not throw the exception */
- private void halCloseEndpointSessionNoThrow(int sessionId, byte halReason) {
- try {
- halCloseEndpointSession(sessionId, halReason);
- } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
- Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
- }
- }
-
- /**
* Cleans up resources related to a session with the provided ID.
*
* @param sessionId The session ID to clean up resources for
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
index 8ab581e..e156159 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -29,6 +29,7 @@
import android.hardware.contexthub.IContextHubEndpointCallback;
import android.hardware.contexthub.IEndpointCommunication;
import android.hardware.contexthub.MessageDeliveryStatus;
+import android.hardware.contexthub.Reason;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
@@ -42,6 +43,7 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -316,6 +318,11 @@
}
}
+ /** Returns if a sessionId can be allocated for the service hub. */
+ private boolean isSessionIdAllocatedForService(int sessionId) {
+ return sessionId > mMaxSessionId || sessionId < mMinSessionId;
+ }
+
/**
* Unregisters an endpoint given its ID.
*
@@ -337,8 +344,7 @@
}
}
- @Override
- public void onEndpointSessionOpenRequest(
+ private Optional<Byte> onEndpointSessionOpenRequestInternal(
int sessionId,
HubEndpointInfo.HubEndpointIdentifier destination,
HubEndpointInfo.HubEndpointIdentifier initiator,
@@ -348,7 +354,7 @@
TAG,
"onEndpointSessionOpenRequest: invalid destination hub ID: "
+ destination.getHub());
- return;
+ return Optional.of(Reason.ENDPOINT_INVALID);
}
ContextHubEndpointBroker broker = mEndpointMap.get(destination.getEndpoint());
if (broker == null) {
@@ -356,7 +362,7 @@
TAG,
"onEndpointSessionOpenRequest: unknown destination endpoint ID: "
+ destination.getEndpoint());
- return;
+ return Optional.of(Reason.ENDPOINT_INVALID);
}
HubEndpointInfo initiatorInfo = mHubInfoRegistry.getEndpointInfo(initiator);
if (initiatorInfo == null) {
@@ -364,9 +370,29 @@
TAG,
"onEndpointSessionOpenRequest: unknown initiator endpoint ID: "
+ initiator.getEndpoint());
- return;
+ return Optional.of(Reason.ENDPOINT_INVALID);
}
- broker.onEndpointSessionOpenRequest(sessionId, initiatorInfo, serviceDescriptor);
+ if (!isSessionIdAllocatedForService(sessionId)) {
+ Log.e(
+ TAG,
+ "onEndpointSessionOpenRequest: invalid session ID, rejected:"
+ + " sessionId="
+ + sessionId);
+ return Optional.of(Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
+ }
+ return broker.onEndpointSessionOpenRequest(sessionId, initiatorInfo, serviceDescriptor);
+ }
+
+ @Override
+ public void onEndpointSessionOpenRequest(
+ int sessionId,
+ HubEndpointInfo.HubEndpointIdentifier destination,
+ HubEndpointInfo.HubEndpointIdentifier initiator,
+ String serviceDescriptor) {
+ Optional<Byte> errorOptional =
+ onEndpointSessionOpenRequestInternal(
+ sessionId, destination, initiator, serviceDescriptor);
+ errorOptional.ifPresent((error) -> halCloseEndpointSessionNoThrow(sessionId, error));
}
@Override
@@ -418,6 +444,30 @@
}
}
+ /**
+ * Calls the HAL closeEndpointSession API.
+ *
+ * @param sessionId The session ID to close
+ * @param halReason The HAL reason
+ */
+ /* package */ void halCloseEndpointSession(int sessionId, byte halReason)
+ throws RemoteException {
+ try {
+ mHubInterface.closeEndpointSession(sessionId, halReason);
+ } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+ throw e;
+ }
+ }
+
+ /** Same as halCloseEndpointSession but does not throw the exception */
+ /* package */ void halCloseEndpointSessionNoThrow(int sessionId, byte halReason) {
+ try {
+ halCloseEndpointSession(sessionId, halReason);
+ } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+ Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
+ }
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 233b577..33a7e74 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -7724,6 +7724,7 @@
pw.print(" Has profile owner: ");
pw.println(mIsUserManaged.get(userId));
+
pw.println(" Restrictions:");
synchronized (mRestrictionsLock) {
UserRestrictionsUtils.dumpRestrictions(
@@ -7756,6 +7757,9 @@
}
}
+ pw.print(" Can have profile: ");
+ pw.println(userInfo.canHaveProfile());
+
if (userData.userProperties != null) {
userData.userProperties.println(pw, " ");
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
index f413fe3..58f34d0 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
@@ -36,7 +36,4 @@
/** Notifies when the screen starts turning on and is not yet visible to the user. */
public abstract void onScreenTurningOn(int displayId);
-
- /** Notifies when the keyguard is going away. Sent right after the bouncer is gone. */
- public abstract void onKeyguardGoingAway();
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index e7da33d..274175a 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -22,6 +22,7 @@
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.Flags.fixWallpaperChanged;
import static android.app.Flags.liveWallpaperContentHandling;
+import static android.app.Flags.notifyKeyguardEvents;
import static android.app.Flags.removeNextWallpaperComponent;
import static android.app.WallpaperManager.COMMAND_REAPPLY;
import static android.app.WallpaperManager.FLAG_LOCK;
@@ -1709,8 +1710,32 @@
mWallpaperDataParser = new WallpaperDataParser(mContext, mWallpaperDisplayHelper,
mWallpaperCropper);
LocalServices.addService(WallpaperManagerInternal.class, new LocalService());
+
+ LocalServices.getService(ActivityTaskManagerInternal.class)
+ .registerScreenObserver(mKeyguardObserver);
+
}
+ private final ActivityTaskManagerInternal.ScreenObserver mKeyguardObserver =
+ new ActivityTaskManagerInternal.ScreenObserver() {
+ @Override
+ public void onKeyguardStateChanged(boolean isShowing) {
+ if (!notifyKeyguardEvents()) {
+ return;
+ }
+ if (isShowing) {
+ notifyKeyguardAppearing();
+ } else {
+ notifyKeyguardGoingAway();
+ }
+ }
+
+ @Override
+ public void onKeyguardGoingAway() {
+ notifyKeyguardGoingAway();
+ }
+ };
+
private final class LocalService extends WallpaperManagerInternal {
@Override
public void onDisplayAddSystemDecorations(int displayId) {
@@ -1733,11 +1758,6 @@
public void onScreenTurningOn(int displayId) {
notifyScreenTurningOn(displayId);
}
-
- @Override
- public void onKeyguardGoingAway() {
- notifyKeyguardGoingAway();
- }
}
void initialize() {
@@ -2571,6 +2591,18 @@
return mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
}
+ private boolean hasPermission(WallpaperData data, String permission) {
+ try {
+ return PackageManager.PERMISSION_GRANTED == mIPackageManager.checkPermission(
+ permission,
+ data.getComponent().getPackageName(),
+ data.userId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to check wallpaper service permission", e);
+ return false;
+ }
+ }
+
private boolean hasAppOpPermission(String permission, int callingUid, String callingPackage,
String attributionTag, String message) {
final String op = AppOpsManager.permissionToOp(permission);
@@ -2873,16 +2905,37 @@
* Propagate a keyguard going away event to the wallpaper engine.
*/
private void notifyKeyguardGoingAway() {
+ dispatchKeyguardCommand(WallpaperManager.COMMAND_KEYGUARD_GOING_AWAY);
+ }
+
+ /**
+ * Propagate a keyguard appearing event to the wallpaper engine.
+ */
+ private void notifyKeyguardAppearing() {
+ dispatchKeyguardCommand(WallpaperManager.COMMAND_KEYGUARD_APPEARING);
+ }
+
+ /**
+ * Propagate a keyguard-related event to the wallpaper engine.
+ *
+ * When the flag below is enabled, the event will only be dispatched in case the recipient
+ * has {@link android.Manifest.pertmission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE} permission.
+ */
+ private void dispatchKeyguardCommand(String command) {
synchronized (mLock) {
for (WallpaperData data : getActiveWallpapers()) {
+ if (notifyKeyguardEvents() && !hasPermission(
+ data, android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)) {
+ continue;
+ }
+
data.connection.forEachDisplayConnector(displayConnector -> {
if (displayConnector.mEngine != null) {
try {
displayConnector.mEngine.dispatchWallpaperCommand(
- WallpaperManager.COMMAND_KEYGUARD_GOING_AWAY,
- -1, -1, -1, new Bundle());
+ command, -1, -1, -1, new Bundle());
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify that the keyguard is going away", e);
+ Slog.w(TAG, "Failed to dispatch wallpaper command: " + command, e);
}
}
});
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 5cc186c..a19f438 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -272,6 +272,12 @@
mActivityOptions = interceptResult.getActivityOptions();
mCallingPid = mRealCallingPid;
mCallingUid = mRealCallingUid;
+ // When an activity launch is intercepted, Intent#prepareToLeaveProcess is not called
+ // since the interception happens in the system_server. So if any activity is calling
+ // a trampoline activity, the keys do not get collected. Since all the interceptors
+ // are present in the system_server, add the creator token before launching the
+ // intercepted intent.
+ mService.mAmInternal.addCreatorToken(mIntent, mCallingPackage);
if (interceptResult.isActivityResolved()) {
return true;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index c243cdc..21b730e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -124,8 +124,9 @@
public static final String ASSIST_KEY_RECEIVER_EXTRAS = "receiverExtras";
public interface ScreenObserver {
- void onAwakeStateChanged(boolean isAwake);
- void onKeyguardStateChanged(boolean isShowing);
+ default void onAwakeStateChanged(boolean isAwake) {}
+ default void onKeyguardStateChanged(boolean isShowing) {}
+ default void onKeyguardGoingAway() {}
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 9c96566..46d24b0 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -281,7 +281,6 @@
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.utils.WindowStyleCache;
import com.android.wm.shell.Flags;
@@ -373,7 +372,6 @@
private ComponentName mSysUiServiceComponent;
private PermissionPolicyInternal mPermissionPolicyInternal;
private StatusBarManagerInternal mStatusBarManagerInternal;
- private WallpaperManagerInternal mWallpaperManagerInternal;
private UserManagerInternal mUserManagerInternal;
@VisibleForTesting
final ActivityTaskManagerInternal mInternal;
@@ -3719,10 +3717,12 @@
if (isPowerModePreApplied && !foundResumed) {
endPowerMode(POWER_MODE_REASON_START_ACTIVITY);
}
- }
- WallpaperManagerInternal wallpaperManagerInternal = getWallpaperManagerInternal();
- if (wallpaperManagerInternal != null) {
- wallpaperManagerInternal.onKeyguardGoingAway();
+
+ mH.post(() -> {
+ for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+ mScreenObservers.get(i).onKeyguardGoingAway();
+ }
+ });
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -5573,13 +5573,6 @@
return mStatusBarManagerInternal;
}
- WallpaperManagerInternal getWallpaperManagerInternal() {
- if (mWallpaperManagerInternal == null) {
- mWallpaperManagerInternal = LocalServices.getService(WallpaperManagerInternal.class);
- }
- return mWallpaperManagerInternal;
- }
-
UserManagerInternal getUserManagerInternal() {
if (mUserManagerInternal == null) {
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
diff --git a/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java b/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java
index 9596093..cd6a014 100644
--- a/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java
@@ -156,6 +156,8 @@
void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
if (mNeedsSafeRegionBounds) {
pw.println(prefix + " mNeedsSafeRegionBounds=true");
+ pw.println(
+ prefix + " latestSafeRegionBoundsOnActivity=" + getLatestSafeRegionBounds());
}
if (isLetterboxedForSafeRegionOnlyAllowed()) {
pw.println(prefix + " isLetterboxForSafeRegionOnlyAllowed=true");
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 2798e84..ab87459 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -218,6 +218,11 @@
*/
protected void adjustAppearance(@NonNull WindowState dimmingContainer,
float alpha, int blurRadius) {
+ if (!mHost.isVisibleRequested()) {
+ // If the host is already going away, there is no point in keeping dimming
+ return;
+ }
+
if (mDimState != null || (alpha != 0 || blurRadius != 0)) {
final DimState d = obtainDimState(dimmingContainer);
d.prepareLookChange(alpha, blurRadius);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 2c10af4..65001f4 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -3401,7 +3401,6 @@
* Applies the new configuration for the changed displays. Returns the activities that should
* check whether to deliver the new configuration to clients.
*/
- @Nullable
void applyDisplayChangeIfNeeded(@NonNull ArraySet<WindowContainer<?>> activitiesMayChange) {
for (int i = mParticipants.size() - 1; i >= 0; --i) {
final WindowContainer<?> wc = mParticipants.valueAt(i);
diff --git a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
index f6b107b..d60807c 100644
--- a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
@@ -62,6 +62,8 @@
Collectors.toSet())).size(); // Dedupe type strings
mRequestSessionMetric.collectGetFlowInitialMetricInfo(request);
mPrepareGetCredentialCallback = prepareGetCredentialCallback;
+
+ Slog.i(TAG, "PrepareGetRequestSession constructed.");
}
@Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 543e32f..9ff6eb6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -34,6 +34,7 @@
import android.app.admin.PolicyKey;
import android.app.admin.PolicyValue;
import android.app.admin.UserRestrictionPolicyKey;
+import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentFilter;
@@ -282,7 +283,9 @@
static PolicyDefinition<Set<String>> PERMITTED_INPUT_METHODS = new PolicyDefinition<>(
new NoArgsPolicyKey(DevicePolicyIdentifiers.PERMITTED_INPUT_METHODS_POLICY),
- new MostRecent<>(),
+ (Flags.usePolicyIntersectionForPermittedInputMethods()
+ ? new StringSetIntersection()
+ : new MostRecent<>()),
POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_INHERITABLE,
PolicyEnforcerCallbacks::noOp,
new PackageSetPolicySerializer());
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java
new file mode 100644
index 0000000..bc075b02
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2025 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.devicepolicy;
+
+import android.annotation.NonNull;
+import android.app.admin.PolicyValue;
+import android.app.admin.PackageSetPolicyValue;
+
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Objects;
+import java.util.Set;
+
+final class StringSetIntersection extends ResolutionMechanism<Set<String>> {
+
+ @Override
+ PolicyValue<Set<String>> resolve(
+ @NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<Set<String>>> adminPolicies) {
+ Objects.requireNonNull(adminPolicies);
+ Set<String> intersectionOfPolicies = null;
+ for (PolicyValue<Set<String>> policy : adminPolicies.values()) {
+ if (intersectionOfPolicies == null) {
+ intersectionOfPolicies = new HashSet<>(policy.getValue());
+ } else {
+ intersectionOfPolicies.retainAll(policy.getValue());
+ }
+ }
+ if (intersectionOfPolicies == null) {
+ return null;
+ }
+ // Note that the resulting set below may be empty, but that's fine:
+ // particular policy should decide what is the meaning of an empty set.
+ return new PackageSetPolicyValue(intersectionOfPolicies);
+ }
+
+ @Override
+ android.app.admin.StringSetIntersection getParcelableResolutionMechanism() {
+ return new android.app.admin.StringSetIntersection();
+ }
+
+ @Override
+ public String toString() {
+ return "StringSetIntersection {}";
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 8c09f26..fdf78ad 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -56,12 +56,12 @@
import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.UiModeManager;
+import android.app.compat.CompatChanges;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobWorkItem;
import android.app.usage.UsageStatsManagerInternal;
-import android.compat.testing.PlatformCompatChangeRule;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -81,6 +81,7 @@
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
@@ -98,6 +99,7 @@
import com.android.server.LocalServices;
import com.android.server.PowerAllowlistInternal;
import com.android.server.SystemServiceManager;
+import com.android.server.compat.PlatformCompat;
import com.android.server.job.controllers.ConnectivityController;
import com.android.server.job.controllers.JobStatus;
import com.android.server.job.controllers.QuotaController;
@@ -106,14 +108,10 @@
import com.android.server.pm.UserManagerInternal;
import com.android.server.usage.AppStandbyInternal;
-import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
-import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TestRule;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
@@ -147,9 +145,6 @@
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
- @Rule
- public TestRule compatChangeRule = new PlatformCompatChangeRule();
-
private ChargingPolicyChangeListener mChargingPolicyChangeListener;
private int mSourceUid;
@@ -166,8 +161,10 @@
mMockingSession = mockitoSession()
.initMocks(this)
.strictness(Strictness.LENIENT)
+ .mockStatic(CompatChanges.class)
.mockStatic(LocalServices.class)
.mockStatic(PermissionChecker.class)
+ .mockStatic(ServiceManager.class)
.startMocking();
// Called in JobSchedulerService constructor.
@@ -230,6 +227,9 @@
ArgumentCaptor<ChargingPolicyChangeListener> chargingPolicyChangeListenerCaptor =
ArgumentCaptor.forClass(ChargingPolicyChangeListener.class);
+ doReturn(mock(PlatformCompat.class))
+ .when(() -> ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+
mService = new TestJobSchedulerService(mContext);
mService.waitOnAsyncLoadingForTesting();
@@ -1074,12 +1074,15 @@
*/
@Test
@EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
- @DisableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
public void testGetRescheduleJobForFailure_abandonedJob() {
final long nowElapsed = sElapsedRealtimeClock.millis();
final long initialBackoffMs = MINUTE_IN_MILLIS;
mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO = 3;
+ // Mock the OVERRIDE_HANDLE_ABANDONED_JOBS compat change overrides.
+ when(CompatChanges.isChangeEnabled(
+ eq(JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS), anyInt())).thenReturn(false);
+
JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure",
createJobInfo()
.setBackoffCriteria(initialBackoffMs, JobInfo.BACKOFF_POLICY_LINEAR));
@@ -1148,8 +1151,10 @@
*/
@Test
@EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
- @DisableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
public void testGetRescheduleJobForFailure_EnableFlagDisableCompatCheckAggressiveBackoff() {
+ // Mock the OVERRIDE_HANDLE_ABANDONED_JOBS compat change overrides.
+ when(CompatChanges.isChangeEnabled(
+ eq(JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS), anyInt())).thenReturn(false);
assertFalse(mService.shouldUseAggressiveBackoff(
mService.mConstants.ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF - 1,
mSourceUid));
@@ -1167,8 +1172,10 @@
*/
@Test
@EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
- @EnableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
public void testGetRescheduleJobForFailure_EnableFlagEnableCompatCheckAggressiveBackoff() {
+ // Mock the OVERRIDE_HANDLE_ABANDONED_JOBS compat change overrides.
+ when(CompatChanges.isChangeEnabled(
+ eq(JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS), anyInt())).thenReturn(true);
assertFalse(mService.shouldUseAggressiveBackoff(
mService.mConstants.ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF - 1,
mSourceUid));
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 2d84887..924fe95 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -76,6 +76,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
@@ -93,6 +94,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.PowerAllowlistInternal;
+import com.android.server.compat.PlatformCompat;
import com.android.server.job.Flags;
import com.android.server.job.JobSchedulerInternal;
import com.android.server.job.JobSchedulerService;
@@ -104,8 +106,6 @@
import com.android.server.job.controllers.QuotaController.TimingSession;
import com.android.server.usage.AppStandbyInternal;
-import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -168,6 +168,8 @@
private PowerAllowlistInternal mPowerAllowlistInternal;
@Mock
private UsageStatsManagerInternal mUsageStatsManager;
+ @Mock
+ private PlatformCompat mPlatformCompat;
@Rule
public final CheckFlagsRule mCheckFlagsRule =
@@ -182,6 +184,7 @@
.strictness(Strictness.LENIENT)
.spyStatic(DeviceConfig.class)
.mockStatic(LocalServices.class)
+ .mockStatic(ServiceManager.class)
.startMocking();
// Called in StateController constructor.
@@ -198,6 +201,7 @@
}
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager);
+
doReturn(mActivityMangerInternal)
.when(() -> LocalServices.getService(ActivityManagerInternal.class));
doReturn(mock(AppStandbyInternal.class))
@@ -253,6 +257,8 @@
ArgumentCaptor.forClass(PowerAllowlistInternal.TempAllowlistChangeListener.class);
ArgumentCaptor<UsageStatsManagerInternal.UsageEventListener> ueListenerCaptor =
ArgumentCaptor.forClass(UsageStatsManagerInternal.UsageEventListener.class);
+ doReturn(mPlatformCompat)
+ .when(() -> ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
mQuotaController = new QuotaController(mJobSchedulerService,
mock(BackgroundJobsController.class), mock(ConnectivityController.class));
@@ -5591,13 +5597,17 @@
}
@Test
- @EnableCompatChanges({QuotaController.OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS,
- QuotaController.OVERRIDE_QUOTA_ENFORCEMENT_TO_FGS_JOBS})
@RequiresFlagsEnabled({Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS,
Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS})
public void testTracking_OutOfQuota_ForegroundAndBackground_CompactChangeOverrides() {
setDischarging();
+ // Mock the OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS compat change overrides.
+ doReturn(true).when(mPlatformCompat).isChangeEnabledByUid(
+ eq(QuotaController.OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS), anyInt());
+ doReturn(true).when(mPlatformCompat).isChangeEnabledByUid(
+ eq(QuotaController.OVERRIDE_QUOTA_ENFORCEMENT_TO_FGS_JOBS), anyInt());
+
JobStatus jobBg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 1);
JobStatus jobTop = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 2);
trackJobs(jobBg, jobTop);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index fc864dd..3ed4a52 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -343,12 +343,13 @@
assertThat(mReadFiles).containsExactly("123.bh", "1000.bh");
} else if (item.eventCode == HistoryItem.EVENT_ALARM) {
eventsRead++;
- assertThat(mReadFiles).containsExactly("123.bh", "1000.bh", "2000.bh");
+ // This event is in the current buffer, so 2000.bh shouldn't be read from disk
+ assertThat(mReadFiles).containsExactly("123.bh", "1000.bh");
}
}
assertThat(eventsRead).isEqualTo(3);
- assertThat(mReadFiles).containsExactly("123.bh", "1000.bh", "2000.bh", "3000.bh");
+ assertThat(mReadFiles).containsExactly("123.bh", "1000.bh");
}
@Test
@@ -366,34 +367,41 @@
return invocation.callRealMethod();
}).when(mHistory).readFragmentToParcel(any(), any());
- BatteryStatsHistoryIterator iterator = mHistory.iterate(1000, 3000);
+ int eventsRead = 0;
+ BatteryStatsHistoryIterator iterator = mHistory.iterate(1001, 3000);
while (iterator.hasNext()) {
HistoryItem item = iterator.next();
if (item.eventCode == HistoryItem.EVENT_JOB_START) {
fail("Event outside the range");
} else if (item.eventCode == HistoryItem.EVENT_JOB_FINISH) {
+ eventsRead++;
assertThat(mReadFiles).containsExactly("1000.bh");
} else if (item.eventCode == HistoryItem.EVENT_ALARM) {
fail("Event outside the range");
}
}
- assertThat(mReadFiles).containsExactly("1000.bh", "2000.bh");
+ assertThat(eventsRead).isEqualTo(1);
+ assertThat(mReadFiles).containsExactly("1000.bh");
}
private void prepareMultiFileHistory() {
- mClock.realtime = 1000;
- mClock.uptime = 1000;
+ mClock.realtime = 500;
+ mClock.uptime = 500;
mHistory.recordEvent(mClock.realtime, mClock.uptime,
BatteryStats.HistoryItem.EVENT_JOB_START, "job", 42);
+ mClock.realtime = 1000;
+ mClock.uptime = 1000;
mHistory.startNextFragment(mClock.realtime); // 1000.bh
- mClock.realtime = 2000;
- mClock.uptime = 2000;
+ mClock.realtime = 1500;
+ mClock.uptime = 1500;
mHistory.recordEvent(mClock.realtime, mClock.uptime,
BatteryStats.HistoryItem.EVENT_JOB_FINISH, "job", 42);
+ mClock.realtime = 2000;
+ mClock.uptime = 2000;
mHistory.startNextFragment(mClock.realtime); // 2000.bh
mClock.realtime = 3000;
@@ -401,8 +409,8 @@
mHistory.recordEvent(mClock.realtime, mClock.uptime,
HistoryItem.EVENT_ALARM, "alarm", 42);
- // Flush accumulated history to disk
- mHistory.startNextFragment(mClock.realtime);
+ // Back up accumulated history to disk
+ mHistory.writeHistory();
}
private void verifyActiveFile(BatteryStatsHistory history, String file) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
index 3565244..33529c3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -207,6 +208,23 @@
eq(SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH), any());
}
+ @Test
+ @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE)
+ public void onCallStateChanged_offHookMultiple_addListenerOnlyOneTime() {
+ AudioDeviceInfo a2dpDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+ AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
+ when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+ new AudioDeviceInfo[]{a2dpDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
+
+ mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+ mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+
+ verify(mAudioManager, times(1)).addOnCommunicationDeviceChangedListener(
+ any(Executor.class),
+ any(AudioManager.OnCommunicationDeviceChangedListener.class));
+ }
+
private AudioDeviceInfo createAudioDeviceInfo(String address, int type) {
AudioDevicePort audioDevicePort = mock(AudioDevicePort.class);
doReturn(type).when(audioDevicePort).type();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
index b2d48a7..2349120 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
@@ -226,13 +226,16 @@
*/
protected void adoptFullVolumeBehaviorOnAvbCapableAudioOutputDevices() {
if (getDeviceType() == HdmiDeviceInfo.DEVICE_PLAYBACK) {
- mAudioManager.setDeviceVolumeBehavior(HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI,
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ mAudioDeviceVolumeManager.setDeviceVolumeBehavior(
+ HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI,
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
} else if (getDeviceType() == HdmiDeviceInfo.DEVICE_TV) {
- mAudioManager.setDeviceVolumeBehavior(HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI_ARC,
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
- mAudioManager.setDeviceVolumeBehavior(HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI_EARC,
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ mAudioDeviceVolumeManager.setDeviceVolumeBehavior(
+ HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI_ARC,
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ mAudioDeviceVolumeManager.setDeviceVolumeBehavior(
+ HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI_EARC,
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
}
@@ -307,8 +310,9 @@
INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume(),
INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute());
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
protected void enableAdjustOnlyAbsoluteVolumeBehavior() {
@@ -320,8 +324,9 @@
INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume(),
INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute());
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
}
protected void verifyGiveAudioStatusNeverSent() {
@@ -419,14 +424,16 @@
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
// AVB should not be enabled before receiving <Report Audio Status>
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
receiveReportAudioStatus(60, false);
// Check that absolute volume behavior was the last one adopted
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
// Check that the volume and mute status received were included when setting AVB
verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeBehavior(
@@ -447,19 +454,22 @@
enableSystemAudioModeIfNeeded();
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
receiveReportAudioStatus(127, false);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
public void avbEnabled_standby_avbDisabled() {
enableAbsoluteVolumeBehavior();
mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
@@ -468,8 +478,9 @@
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_DISABLED);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
@@ -477,8 +488,9 @@
enableAbsoluteVolumeBehavior();
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
@@ -489,8 +501,9 @@
getSystemAudioDeviceLogicalAddress(), getLogicalAddress(),
Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL, Constants.ABORT_UNRECOGNIZED_OPCODE));
mTestLooper.dispatchAll();
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
@@ -501,8 +514,9 @@
enableAbsoluteVolumeBehavior();
receiveSetSystemAudioMode(false);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BasePlaybackDeviceAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BasePlaybackDeviceAvbTest.java
index 4c12e436..7c7e220 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BasePlaybackDeviceAvbTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BasePlaybackDeviceAvbTest.java
@@ -20,7 +20,7 @@
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.AudioDeviceAttributes;
-import android.media.AudioManager;
+import android.media.AudioDeviceVolumeManager;
import org.junit.Test;
@@ -60,8 +60,8 @@
*/
@Test
public void savlNotSupported_allOtherConditionsMet_giveAudioStatusNotSent() {
- mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ mAudioDeviceVolumeManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
enableSystemAudioModeIfNeeded();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
index f44517a..7a43598 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
@@ -96,13 +96,15 @@
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
// Adjust-only AVB should not be enabled before receiving <Report Audio Status>
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
receiveReportAudioStatus(20, false);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeAdjustOnlyBehavior(
eq(getAudioOutputDevice()),
@@ -124,8 +126,9 @@
receiveReportAudioStatus(40, true);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeAdjustOnlyBehavior(
eq(getAudioOutputDevice()),
@@ -149,8 +152,9 @@
receiveReportAudioStatus(40, true);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeAdjustOnlyBehavior(
eq(getAudioOutputDevice()),
@@ -283,13 +287,15 @@
verifyGiveAudioStatusSent();
// The device should use adjust-only AVB while waiting for <Report Audio Status>
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
// The device should switch to AVB upon receiving <Report Audio Status>
receiveReportAudioStatus(60, false);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
@@ -321,8 +327,9 @@
mTestLooper.dispatchAll();
// The device should not switch away from adjust-only AVB
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
// The device should query support for <Set Audio Volume Level> again
assertThat(mNativeWrapper.getResultMessages()).contains(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
index a4c71bd..2227eeb 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
@@ -251,6 +251,35 @@
}
@Test
+ public void testDeviceSelect_DeviceAssertsActiveSource_singleSetStreamPathMessage() {
+ // TV was watching playback2 device connected at port 2, and wants to select
+ // playback1.
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback,
+ /*isCec20=*/false);
+ mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2,
+ "testDeviceSelect");
+ action.start();
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
+ mNativeWrapper.clearResultMessages();
+ mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYBACK_1,
+ "testDeviceSelect");
+ mTestLooper.dispatchAll();
+
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_POWER_STATE_CHANGE);
+ action.handleTimerEvent(STATE_WAIT_FOR_POWER_STATE_CHANGE);
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
+ action.processCommand(REPORT_POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
public void testDeviceSelect_DeviceInStandbyStatus_Cec14b() {
mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2,
"testDeviceSelect");
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java
index 90f94cb..e07f4f9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceVolumeManager;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.VolumeInfo;
@@ -137,18 +138,6 @@
// Do nothing
}
-
- @Override
- @AudioManager.DeviceVolumeBehavior
- public int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
- return mDeviceVolumeBehaviors.getOrDefault(device, DEFAULT_DEVICE_VOLUME_BEHAVIOR);
- }
-
- public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior) {
- setVolumeBehaviorHelper(device, deviceVolumeBehavior);
- }
-
@Override
@NonNull
public List<AudioDeviceAttributes> getDevicesForAttributes(
@@ -186,7 +175,8 @@
boolean handlesVolumeAdjustment,
@NonNull @CallbackExecutor Executor executor,
@NonNull OnAudioDeviceVolumeChangedListener vclistener) {
- setVolumeBehaviorHelper(device, AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+ setVolumeBehaviorHelper(device,
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
@Override
@@ -197,7 +187,19 @@
@NonNull @CallbackExecutor Executor executor,
@NonNull OnAudioDeviceVolumeChangedListener vclistener) {
setVolumeBehaviorHelper(device,
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ }
+
+ @Override
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior
+ public int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
+ return mDeviceVolumeBehaviors.getOrDefault(device, DEFAULT_DEVICE_VOLUME_BEHAVIOR);
+ }
+
+ @Override
+ public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int deviceVolumeBehavior) {
+ setVolumeBehaviorHelper(device, deviceVolumeBehavior);
}
}
@@ -222,7 +224,7 @@
* Helper method for changing an audio device's volume behavior. Notifies listeners.
*/
private void setVolumeBehaviorHelper(AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int newVolumeBehavior) {
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int newVolumeBehavior) {
int currentVolumeBehavior = mDeviceVolumeBehaviors.getOrDefault(
device, DEFAULT_DEVICE_VOLUME_BEHAVIOR);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToAudioSystemAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToAudioSystemAvbTest.java
index 43ab804..ffc1c62 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToAudioSystemAvbTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToAudioSystemAvbTest.java
@@ -21,7 +21,7 @@
import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
-import android.media.AudioManager;
+import android.media.AudioDeviceVolumeManager;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -64,8 +64,9 @@
// Audio System disables System Audio Mode. AVB should be disabled.
receiveSetSystemAudioMode(false);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
// TV reports support for <Set Audio Volume Level>
mNativeWrapper.onCecMessage(ReportFeaturesMessage.build(
@@ -85,7 +86,8 @@
false));
mTestLooper.dispatchAll();
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToTvAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToTvAvbTest.java
index 9b343e3..0926180 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToTvAvbTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToTvAvbTest.java
@@ -23,7 +23,7 @@
import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
-import android.media.AudioManager;
+import android.media.AudioDeviceVolumeManager;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -65,8 +65,9 @@
// Audio System enables System Audio Mode. AVB should be disabled.
receiveSetSystemAudioMode(true);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
clearInvocations(mAudioManager, mAudioDeviceVolumeManager);
@@ -88,7 +89,8 @@
false));
mTestLooper.dispatchAll();
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
index 4d2dcf6..43b1ec3 100644
--- a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
@@ -20,6 +20,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -67,12 +68,15 @@
private static final int SESSION_ID_RANGE = ContextHubEndpointManager.SERVICE_SESSION_RANGE;
private static final int MIN_SESSION_ID = 0;
private static final int MAX_SESSION_ID = MIN_SESSION_ID + SESSION_ID_RANGE - 1;
+ private static final int SESSION_ID_FOR_OPEN_REQUEST = MAX_SESSION_ID + 1;
+ private static final int INVALID_SESSION_ID_FOR_OPEN_REQUEST = MIN_SESSION_ID + 1;
private static final String ENDPOINT_NAME = "Example test endpoint";
private static final int ENDPOINT_ID = 1;
private static final String ENDPOINT_PACKAGE_NAME = "com.android.server.location.contexthub";
private static final String TARGET_ENDPOINT_NAME = "Example target endpoint";
+ private static final String ENDPOINT_SERVICE_DESCRIPTOR = "serviceDescriptor";
private static final int TARGET_ENDPOINT_ID = 1;
private static final int SAMPLE_MESSAGE_TYPE = 1234;
@@ -225,6 +229,105 @@
}
@Test
+ public void testEndpointSessionOpenRequest() throws RemoteException {
+ assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+ IContextHubEndpoint endpoint = registerExampleEndpoint();
+
+ HubEndpointInfo targetInfo =
+ new HubEndpointInfo(
+ TARGET_ENDPOINT_NAME,
+ TARGET_ENDPOINT_ID,
+ ENDPOINT_PACKAGE_NAME,
+ Collections.emptyList());
+ mHubInfoRegistry.onEndpointStarted(new HubEndpointInfo[] {targetInfo});
+ mEndpointManager.onEndpointSessionOpenRequest(
+ SESSION_ID_FOR_OPEN_REQUEST,
+ endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+ targetInfo.getIdentifier(),
+ ENDPOINT_SERVICE_DESCRIPTOR);
+
+ verify(mMockCallback)
+ .onSessionOpenRequest(
+ SESSION_ID_FOR_OPEN_REQUEST, targetInfo, ENDPOINT_SERVICE_DESCRIPTOR);
+
+ // Accept
+ endpoint.openSessionRequestComplete(SESSION_ID_FOR_OPEN_REQUEST);
+ verify(mMockEndpointCommunications)
+ .endpointSessionOpenComplete(SESSION_ID_FOR_OPEN_REQUEST);
+
+ unregisterExampleEndpoint(endpoint);
+ }
+
+ @Test
+ public void testEndpointSessionOpenRequestWithInvalidSessionId() throws RemoteException {
+ assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+ IContextHubEndpoint endpoint = registerExampleEndpoint();
+
+ HubEndpointInfo targetInfo =
+ new HubEndpointInfo(
+ TARGET_ENDPOINT_NAME,
+ TARGET_ENDPOINT_ID,
+ ENDPOINT_PACKAGE_NAME,
+ Collections.emptyList());
+ mHubInfoRegistry.onEndpointStarted(new HubEndpointInfo[] {targetInfo});
+ mEndpointManager.onEndpointSessionOpenRequest(
+ INVALID_SESSION_ID_FOR_OPEN_REQUEST,
+ endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+ targetInfo.getIdentifier(),
+ ENDPOINT_SERVICE_DESCRIPTOR);
+ verify(mMockEndpointCommunications)
+ .closeEndpointSession(
+ INVALID_SESSION_ID_FOR_OPEN_REQUEST,
+ Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
+ verify(mMockCallback, never())
+ .onSessionOpenRequest(
+ INVALID_SESSION_ID_FOR_OPEN_REQUEST,
+ targetInfo,
+ ENDPOINT_SERVICE_DESCRIPTOR);
+
+ unregisterExampleEndpoint(endpoint);
+ }
+
+ @Test
+ public void testEndpointSessionOpenRequest_duplicatedSessionId_noopWhenSessionIsActive()
+ throws RemoteException {
+ assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+ IContextHubEndpoint endpoint = registerExampleEndpoint();
+
+ HubEndpointInfo targetInfo =
+ new HubEndpointInfo(
+ TARGET_ENDPOINT_NAME,
+ TARGET_ENDPOINT_ID,
+ ENDPOINT_PACKAGE_NAME,
+ Collections.emptyList());
+ mHubInfoRegistry.onEndpointStarted(new HubEndpointInfo[] {targetInfo});
+ mEndpointManager.onEndpointSessionOpenRequest(
+ SESSION_ID_FOR_OPEN_REQUEST,
+ endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+ targetInfo.getIdentifier(),
+ ENDPOINT_SERVICE_DESCRIPTOR);
+ endpoint.openSessionRequestComplete(SESSION_ID_FOR_OPEN_REQUEST);
+ // Now session with id SESSION_ID_FOR_OPEN_REQUEST is active
+
+ // Duplicated session open request
+ mEndpointManager.onEndpointSessionOpenRequest(
+ SESSION_ID_FOR_OPEN_REQUEST,
+ endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+ targetInfo.getIdentifier(),
+ ENDPOINT_SERVICE_DESCRIPTOR);
+
+ // Client API is only invoked once
+ verify(mMockCallback, times(1))
+ .onSessionOpenRequest(
+ SESSION_ID_FOR_OPEN_REQUEST, targetInfo, ENDPOINT_SERVICE_DESCRIPTOR);
+ // HAL still receives two open complete notifications
+ verify(mMockEndpointCommunications, times(2))
+ .endpointSessionOpenComplete(SESSION_ID_FOR_OPEN_REQUEST);
+
+ unregisterExampleEndpoint(endpoint);
+ }
+
+ @Test
public void testMessageTransaction() throws RemoteException {
IContextHubEndpoint endpoint = registerExampleEndpoint();
testMessageTransactionInternal(endpoint, /* deliverMessageStatus= */ true);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index d1b2e8e..1fb8411 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -274,7 +274,7 @@
/** Test UserInfo.canHaveProfile for main user */
@Test
public void testCanHaveProfile() throws Exception {
- UserInfo userInfo = createUser(100, FLAG_MAIN, null);
+ UserInfo userInfo = createUser(100, FLAG_FULL | FLAG_MAIN, null);
assertTrue("Main users can have profile", userInfo.canHaveProfile());
}
diff --git a/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java b/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
index d9bb7db..5419d94 100644
--- a/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
+++ b/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
@@ -110,7 +110,7 @@
blockingCallSpeak("foo bar", delegate);
ArgumentCaptor<SynthesisRequest> req = ArgumentCaptor.forClass(SynthesisRequest.class);
Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req.capture(),
- Mockito.<SynthesisCallback>anyObject());
+ Mockito.<SynthesisCallback>any());
assertEquals("eng", req.getValue().getLanguage());
assertEquals("USA", req.getValue().getCountry());
@@ -133,7 +133,7 @@
blockingCallSpeak("le fou barre", delegate);
ArgumentCaptor<SynthesisRequest> req2 = ArgumentCaptor.forClass(SynthesisRequest.class);
Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req2.capture(),
- Mockito.<SynthesisCallback>anyObject());
+ Mockito.<SynthesisCallback>any());
// The params are basically unchanged.
assertEquals("eng", req2.getValue().getLanguage());
@@ -177,7 +177,7 @@
blockingCallSpeak("foo bar", delegate);
ArgumentCaptor<SynthesisRequest> req = ArgumentCaptor.forClass(SynthesisRequest.class);
Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req.capture(),
- Mockito.<SynthesisCallback>anyObject());
+ Mockito.<SynthesisCallback>any());
assertEquals(defaultLocale.getISO3Language(), req.getValue().getLanguage());
assertEquals(defaultLocale.getISO3Country(), req.getValue().getCountry());
@@ -189,8 +189,8 @@
private void blockingCallSpeak(String speech, IDelegate mock) throws
InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
- doCountDown(latch).when(mock).onSynthesizeText(Mockito.<SynthesisRequest>anyObject(),
- Mockito.<SynthesisCallback>anyObject());
+ doCountDown(latch).when(mock).onSynthesizeText(Mockito.<SynthesisRequest>any(),
+ Mockito.<SynthesisCallback>any());
mTts.speak(speech, TextToSpeech.QUEUE_ADD, null);
awaitCountDown(latch, 5, TimeUnit.SECONDS);