Merge "dpc: call updatePowerState instead of post to handler" into main
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 384d786..ff73a49 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -80,6 +80,7 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
import android.os.storage.StorageManagerInternal;
@@ -179,6 +180,8 @@
public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public static final boolean DEBUG_STANDBY = DEBUG || false;
+ public static final String TRACE_TRACK_NAME = "JobScheduler";
+
/** The maximum number of jobs that we allow an app to schedule */
private static final int MAX_JOBS_PER_APP = 150;
/** The number of the most recently completed jobs to keep track of for debugging purposes. */
@@ -4344,7 +4347,11 @@
final boolean wasConsideredCharging = isConsideredCharging();
mChargingPolicy = newPolicy;
-
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME,
+ "CHARGING POLICY CHANGED#" + mChargingPolicy);
+ }
if (isConsideredCharging() != wasConsideredCharging) {
for (int c = mControllers.size() - 1; c >= 0; --c) {
mControllers.get(c).onBatteryStateChangedLocked();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 39d50f5..d65a66c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -535,29 +535,17 @@
sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
final String sourcePackage = job.getSourcePackageName();
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
- final String componentPackage = job.getServiceComponent().getPackageName();
- String traceTag = "*job*<" + job.getSourceUid() + ">" + sourcePackage;
- if (!sourcePackage.equals(componentPackage)) {
- traceTag += ":" + componentPackage;
- }
- traceTag += "/" + job.getServiceComponent().getShortClassName();
- if (!componentPackage.equals(job.serviceProcessName)) {
- traceTag += "$" + job.serviceProcessName;
- }
- if (job.getNamespace() != null) {
- traceTag += "@" + job.getNamespace();
- }
- traceTag += "#" + job.getJobId();
-
// Use the context's ID to distinguish traces since there'll only be one job
// running per context.
- Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
- traceTag, getId());
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME, job.computeSystemTraceTag(),
+ getId());
}
if (job.getAppTraceTag() != null) {
// Use the job's ID to distinguish traces since the ID will be unique per app.
- Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, "JobScheduler",
- job.getAppTraceTag(), job.getJobId());
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP,
+ JobSchedulerService.TRACE_TRACK_NAME, job.getAppTraceTag(),
+ job.getJobId());
}
try {
mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
@@ -1605,12 +1593,12 @@
completedJob.getFilteredTraceTag(),
completedJob.getFilteredDebugTags());
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
- getId());
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME, getId());
}
if (completedJob.getAppTraceTag() != null) {
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, "JobScheduler",
- completedJob.getJobId());
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP,
+ JobSchedulerService.TRACE_TRACK_NAME, completedJob.getJobId());
}
try {
mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 7fca867..e3af1d8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -572,6 +572,9 @@
/** The reason a job most recently went from ready to not ready. */
private int mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
+ /** The system trace tag for this job. */
+ private String mSystemTraceTag;
+
/**
* Core constructor for JobStatus instances. All other ctors funnel down to this one.
*
@@ -1058,6 +1061,38 @@
return job.getTraceTag();
}
+ /** Returns a trace tag using debug information provided by job scheduler service. */
+ @NonNull
+ public String computeSystemTraceTag() {
+ // Guarded by JobSchedulerService.mLock, no need for synchronization.
+ if (mSystemTraceTag != null) {
+ return mSystemTraceTag;
+ }
+
+ mSystemTraceTag = computeSystemTraceTagInner();
+ return mSystemTraceTag;
+ }
+
+ @NonNull
+ private String computeSystemTraceTagInner() {
+ final String componentPackage = getServiceComponent().getPackageName();
+ StringBuilder traceTag = new StringBuilder(128);
+ traceTag.append("*job*<").append(sourceUid).append(">").append(sourcePackageName);
+ if (!sourcePackageName.equals(componentPackage)) {
+ traceTag.append(":").append(componentPackage);
+ }
+ traceTag.append("/").append(getServiceComponent().getShortClassName());
+ if (!componentPackage.equals(serviceProcessName)) {
+ traceTag.append("$").append(serviceProcessName);
+ }
+ if (mNamespace != null && !mNamespace.trim().isEmpty()) {
+ traceTag.append("@").append(mNamespace);
+ }
+ traceTag.append("#").append(getJobId());
+
+ return traceTag.toString();
+ }
+
/** Returns whether this job was scheduled by one app on behalf of another. */
public boolean isProxyJob() {
return mIsProxyJob;
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 c240b3f..a1c72fb 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
@@ -50,6 +50,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.ArraySet;
@@ -2181,6 +2182,12 @@
}
scheduleCutoff();
}
+ } else {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME,
+ "QC/- " + mPkg);
+ }
}
}
@@ -2720,6 +2727,11 @@
if (timeRemainingMs <= 50) {
// Less than 50 milliseconds left. Start process of shutting down jobs.
if (DEBUG) Slog.d(TAG, pkg + " has reached its quota.");
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME,
+ pkg + "#" + MSG_REACHED_TIME_QUOTA);
+ }
mStateChangedListener.onControllerStateChanged(
maybeUpdateConstraintForPkgLocked(
sElapsedRealtimeClock.millis(),
@@ -2748,6 +2760,11 @@
pkg.userId, pkg.packageName);
if (timeRemainingMs <= 0) {
if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota.");
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME,
+ pkg + "#" + MSG_REACHED_EJ_TIME_QUOTA);
+ }
mStateChangedListener.onControllerStateChanged(
maybeUpdateConstraintForPkgLocked(
sElapsedRealtimeClock.millis(),
@@ -2772,6 +2789,12 @@
Slog.d(TAG, pkg + " has reached its count quota.");
}
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME,
+ pkg + "#" + MSG_REACHED_COUNT_QUOTA);
+ }
+
mStateChangedListener.onControllerStateChanged(
maybeUpdateConstraintForPkgLocked(
sElapsedRealtimeClock.millis(),
@@ -2928,6 +2951,11 @@
}
mTempAllowlistGraceCache.delete(uid);
mTopAppGraceCache.delete(uid);
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME,
+ "<" + uid + ">#" + MSG_END_GRACE_PERIOD);
+ }
final ArraySet<String> packages = mService.getPackagesForUidLocked(uid);
if (packages != null) {
final int userId = UserHandle.getUserId(uid);
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
index 488292d..f726361 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
@@ -292,13 +292,17 @@
int childCount = node.getChildCount();
for (int x = 0; x < childCount; x++) {
AccessibilityNodeInfo childNode = node.getChild(x);
-
+ if (childNode == null) {
+ continue;
+ }
if (!safeCharSeqToString(childNode.getContentDescription()).isEmpty()
- || !safeCharSeqToString(childNode.getText()).isEmpty())
+ || !safeCharSeqToString(childNode.getText()).isEmpty()) {
return true;
+ }
- if (childNafCheck(childNode))
+ if (childNafCheck(childNode)) {
return true;
+ }
}
return false;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9c80659..8b19664 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5704,6 +5704,7 @@
p.headerless(resId == getBaseLayoutResource()
|| resId == getHeadsUpBaseLayoutResource()
|| resId == getCompactHeadsUpBaseLayoutResource()
+ || resId == getMessagingCompactHeadsUpLayoutResource()
|| resId == getMessagingLayoutResource()
|| resId == R.layout.notification_template_material_media);
RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
@@ -7294,6 +7295,10 @@
return R.layout.notification_template_material_compact_heads_up_base;
}
+ private int getMessagingCompactHeadsUpLayoutResource() {
+ return R.layout.notification_template_material_messaging_compact_heads_up;
+ }
+
private int getBigBaseLayoutResource() {
return R.layout.notification_template_material_big_base;
}
@@ -9166,10 +9171,78 @@
@Nullable
@Override
public RemoteViews makeCompactHeadsUpContentView() {
- // TODO(b/336229954): Apply minimal HUN treatment to Messaging Notifications.
- return makeHeadsUpContentView(false);
+ final boolean isConversationLayout = mConversationType != CONVERSATION_TYPE_LEGACY;
+ Icon conversationIcon = null;
+ Notification.Action remoteInputAction = null;
+ if (isConversationLayout) {
+
+ conversationIcon = mShortcutIcon;
+
+ // conversation icon is m
+ // Extract the conversation icon for one to one conversations from
+ // the latest incoming message since
+ // fixTitleAndTextExtras also uses it as data source for title and text
+ if (conversationIcon == null && !mIsGroupConversation) {
+ final Message message = findLatestIncomingMessage();
+ if (message != null) {
+ final Person sender = message.mSender;
+ if (sender != null) {
+ conversationIcon = sender.getIcon();
+ }
+ }
+ }
+
+ if (Flags.compactHeadsUpNotificationReply()) {
+ // Get the first non-contextual inline reply action.
+ final List<Notification.Action> nonContextualActions =
+ mBuilder.getNonContextualActions();
+ for (int i = 0; i < nonContextualActions.size(); i++) {
+ final Notification.Action action = nonContextualActions.get(i);
+ if (mBuilder.hasValidRemoteInput(action)) {
+ remoteInputAction = action;
+ break;
+ }
+ }
+ }
+ }
+
+ // This method fills title and text
+ fixTitleAndTextExtras(mBuilder.mN.extras);
+ final StandardTemplateParams p = mBuilder.mParams.reset()
+ .viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP)
+ .highlightExpander(isConversationLayout)
+ .fillTextsFrom(mBuilder)
+ .hideTime(true)
+ .summaryText("");
+ p.headerTextSecondary(p.mText);
+ TemplateBindResult bindResult = new TemplateBindResult();
+
+ RemoteViews contentView = mBuilder.applyStandardTemplate(
+ mBuilder.getMessagingCompactHeadsUpLayoutResource(), p, bindResult);
+ if (conversationIcon != null) {
+ contentView.setViewVisibility(R.id.icon, View.GONE);
+ contentView.setViewVisibility(R.id.conversation_icon, View.VISIBLE);
+ contentView.setBoolean(R.id.conversation_icon, "setApplyCircularCrop", true);
+ contentView.setImageViewIcon(R.id.conversation_icon, conversationIcon);
+ }
+
+ if (remoteInputAction != null) {
+ contentView.setViewVisibility(R.id.reply_action_container, View.VISIBLE);
+
+ final RemoteViews inlineReplyButton =
+ mBuilder.generateActionButton(remoteInputAction, false, p);
+ // Clear the drawable
+ inlineReplyButton.setInt(R.id.action0, "setBackgroundResource", 0);
+ inlineReplyButton.setTextViewText(R.id.action0,
+ mBuilder.mContext.getString(R.string.notification_compact_heads_up_reply));
+ contentView.addView(R.id.reply_action_container, inlineReplyButton);
+ } else {
+ contentView.setViewVisibility(R.id.reply_action_container, View.GONE);
+ }
+ return contentView;
}
+
/**
* @hide
*/
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 50c7b7f..6ceae17 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -160,3 +160,10 @@
description: "[Minimal HUN] Enables the compact heads up notification feature"
bug: "270709257"
}
+
+flag {
+ name: "compact_heads_up_notification_reply"
+ namespace: "systemui"
+ description: "[Minimal HUN] Enables the compact heads up notification reply capability for Conversation Notifications"
+ bug: "336229954"
+}
\ No newline at end of file
diff --git a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
index 5e1c1e0..a37f51b 100644
--- a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
+++ b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
@@ -529,7 +529,6 @@
* {@link Bundle}s annotated with this type will be validated that they are in-effect read-only
* when passed via Binder IPC. Following restrictions apply :
* <ul>
- * <li> No Nested Bundles are allowed.</li>
* <li> {@link PersistableBundle}s are allowed.</li>
* <li> Any primitive types or their collections can be added as usual.</li>
* <li>IBinder objects should *not* be added.</li>
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index 3e23762..b29b52d 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -127,4 +127,15 @@
metadata {
purpose: PURPOSE_BUGFIX
}
+}
+
+flag {
+ name: "impulse_velocity_strategy_for_touch_navigation"
+ is_exported: true
+ namespace: "virtual_devices"
+ description: "Use impulse velocity strategy during conversion of touch navigation flings into Dpad events"
+ bug: "338426241"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4b579e7..1f6730b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2628,6 +2628,15 @@
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
+ // STOPSHIP: hack for the pre-release SDK
+ if (platformSdkCodenames.length == 0
+ && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+ targetCode)) {
+ Slog.w(TAG, "Package requires development platform " + targetCode
+ + ", returning current version " + Build.VERSION.SDK_INT);
+ return Build.VERSION.SDK_INT;
+ }
+
// Otherwise, we're looking at an incompatible pre-release SDK.
if (platformSdkCodenames.length > 0) {
outError[0] = "Requires development platform " + targetCode
@@ -2699,6 +2708,15 @@
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
+ // STOPSHIP: hack for the pre-release SDK
+ if (platformSdkCodenames.length == 0
+ && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+ minCode)) {
+ Slog.w(TAG, "Package requires min development platform " + minCode
+ + ", returning current version " + Build.VERSION.SDK_INT);
+ return Build.VERSION.SDK_INT;
+ }
+
// Otherwise, we're looking at an incompatible pre-release SDK.
if (platformSdkCodenames.length > 0) {
outError[0] = "Requires development platform " + minCode
diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
index 153dd9a..c7403c0 100644
--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
@@ -316,6 +316,15 @@
return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
}
+ // STOPSHIP: hack for the pre-release SDK
+ if (platformSdkCodenames.length == 0
+ && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+ minCode)) {
+ Slog.w(TAG, "Parsed package requires min development platform " + minCode
+ + ", returning current version " + Build.VERSION.SDK_INT);
+ return input.success(Build.VERSION.SDK_INT);
+ }
+
// Otherwise, we're looking at an incompatible pre-release SDK.
if (platformSdkCodenames.length > 0) {
return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
@@ -368,19 +377,27 @@
return input.success(targetVers);
}
+ // If it's a pre-release SDK and the codename matches this platform, it
+ // definitely targets this SDK.
+ if (matchTargetCode(platformSdkCodenames, targetCode)) {
+ return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ }
+
+ // STOPSHIP: hack for the pre-release SDK
+ if (platformSdkCodenames.length == 0
+ && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+ targetCode)) {
+ Slog.w(TAG, "Parsed package requires development platform " + targetCode
+ + ", returning current version " + Build.VERSION.SDK_INT);
+ return input.success(Build.VERSION.SDK_INT);
+ }
+
try {
if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {
return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
}
} catch (IllegalArgumentException e) {
- // isAtMost() throws it when encountering an older SDK codename
- return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, e.getMessage());
- }
-
- // If it's a pre-release SDK and the codename matches this platform, it
- // definitely targets this SDK.
- if (matchTargetCode(platformSdkCodenames, targetCode)) {
- return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK");
}
// Otherwise, we're looking at an incompatible pre-release SDK.
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 2b7d8f1..708f8a1 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -39,7 +39,6 @@
import android.content.pm.PackageManager;
import android.graphics.Point;
import android.hardware.CameraExtensionSessionStats;
-import android.hardware.CameraIdRemapping;
import android.hardware.CameraStatus;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceListener;
@@ -1996,17 +1995,6 @@
}
/**
- * Remaps Camera Ids in the CameraService.
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.CAMERA_INJECT_EXTERNAL_CAMERA)
- public void remapCameraIds(@NonNull CameraIdRemapping cameraIdRemapping)
- throws CameraAccessException, SecurityException, IllegalArgumentException {
- CameraManagerGlobal.get().remapCameraIds(cameraIdRemapping);
- }
-
- /**
* Injects session params into existing clients in the CameraService.
*
* @param cameraId The camera id of client to inject session params into.
@@ -2117,13 +2105,6 @@
private final Object mLock = new Object();
- /**
- * The active CameraIdRemapping. This will be used to refresh the cameraIdRemapping state
- * in the CameraService every time we connect to it, including when the CameraService
- * Binder dies and we reconnect to it.
- */
- @Nullable private CameraIdRemapping mActiveCameraIdRemapping;
-
// Access only through getCameraService to deal with binder death
private ICameraService mCameraService;
private boolean mHasOpenCloseListenerPermission = false;
@@ -2275,41 +2256,6 @@
} catch (RemoteException e) {
// Camera service died in all probability
}
-
- if (mActiveCameraIdRemapping != null) {
- try {
- cameraService.remapCameraIds(mActiveCameraIdRemapping);
- } catch (ServiceSpecificException e) {
- // Unexpected failure, ignore and continue.
- Log.e(TAG, "Unable to remap camera Ids in the camera service");
- } catch (RemoteException e) {
- // Camera service died in all probability
- }
- }
- }
-
- /** Updates the cameraIdRemapping state in the CameraService. */
- public void remapCameraIds(@NonNull CameraIdRemapping cameraIdRemapping)
- throws CameraAccessException, SecurityException {
- synchronized (mLock) {
- ICameraService cameraService = getCameraService();
- if (cameraService == null) {
- throw new CameraAccessException(
- CameraAccessException.CAMERA_DISCONNECTED,
- "Camera service is currently unavailable.");
- }
-
- try {
- cameraService.remapCameraIds(cameraIdRemapping);
- mActiveCameraIdRemapping = cameraIdRemapping;
- } catch (ServiceSpecificException e) {
- throw ExceptionUtils.throwAsPublicException(e);
- } catch (RemoteException e) {
- throw new CameraAccessException(
- CameraAccessException.CAMERA_DISCONNECTED,
- "Camera service is currently unavailable.");
- }
- }
}
/** Injects session params into an existing client for cameraid. */
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 7754e32..de26384 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2359,7 +2359,10 @@
* FPS.</p>
* <p>If the session configuration is not supported, the AE mode reported in the
* CaptureResult will be 'ON' instead of 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'.</p>
- * <p>The application can observe the CapturerResult field
+ * <p>When this AE mode is enabled, the CaptureResult field
+ * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will be present and not null. Otherwise, the
+ * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} field will not be present in the CaptureResult.</p>
+ * <p>The application can observe the CaptureResult field
* {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} to determine when low light boost is 'ACTIVE' or
* 'INACTIVE'.</p>
* <p>The low light boost is 'ACTIVE' once the scene lighting condition is less than the
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 5765a73..1460515 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2819,6 +2819,8 @@
* <p>When low light boost is enabled by setting the AE mode to
* 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY', it can dynamically apply a low light
* boost when the light level threshold is exceeded.</p>
+ * <p>This field is present in the CaptureResult when the AE mode is set to
+ * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'. Otherwise, the field is not present.</p>
* <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can
* indicate when it is not being applied by returning 'INACTIVE'.</p>
* <p>This key will be absent from the CaptureResult if AE mode is not set to
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 594ec18..334b231 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -173,6 +173,12 @@
public static final String FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY = "low_power_standby";
/** @hide */
public static final String FIREWALL_CHAIN_NAME_BACKGROUND = "background";
+ /** @hide */
+ public static final String FIREWALL_CHAIN_NAME_METERED_ALLOW = "metered_allow";
+ /** @hide */
+ public static final String FIREWALL_CHAIN_NAME_METERED_DENY_USER = "metered_deny_user";
+ /** @hide */
+ public static final String FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN = "metered_deny_admin";
private static final boolean ALLOW_PLATFORM_APP_POLICY = true;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index d45a17f..91ad22f 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -120,6 +120,8 @@
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* StorageManager is the interface to the systems storage service. The storage
@@ -2512,6 +2514,9 @@
return userId * PER_USER_RANGE + projectId;
}
+ private static final Pattern PATTERN_USER_ID = Pattern.compile(
+ "(?i)^/storage/emulated/([0-9]+)");
+
/**
* Let StorageManager know that the quota type for a file on external storage should
* be updated. Android tracks quotas for various media types. Consequently, this should be
@@ -2541,26 +2546,35 @@
@SystemApi
public void updateExternalStorageFileQuotaType(@NonNull File path,
@QuotaType int quotaType) throws IOException {
+ if (!path.exists()) return;
+
long projectId;
final String filePath = path.getCanonicalPath();
- int volFlags = FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE;
- // If caller has MANAGE_EXTERNAL_STORAGE permission, results from User Profile(s) are also
- // returned by enabling FLAG_INCLUDE_SHARED_PROFILE.
- if (mContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
- volFlags |= FLAG_INCLUDE_SHARED_PROFILE;
- }
- final StorageVolume[] availableVolumes = getVolumeList(mContext.getUserId(), volFlags);
- final StorageVolume volume = getStorageVolume(availableVolumes, path);
- if (volume == null) {
- Log.w(TAG, "Failed to update quota type for " + filePath);
- return;
- }
- if (!volume.isEmulated()) {
- // We only support quota tracking on emulated filesystems
- return;
+
+ final int userId;
+ final Matcher matcher = PATTERN_USER_ID.matcher(filePath);
+ if (matcher.find()) {
+ userId = Integer.parseInt(matcher.group(1));
+ } else { // fallback
+ int volFlags = FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE;
+ // If caller has MANAGE_EXTERNAL_STORAGE permission, results from User Profile(s) are
+ // also returned by enabling FLAG_INCLUDE_SHARED_PROFILE.
+ if (mContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
+ volFlags |= FLAG_INCLUDE_SHARED_PROFILE;
+ }
+ final StorageVolume[] availableVolumes = getVolumeList(mContext.getUserId(), volFlags);
+ final StorageVolume volume = getStorageVolume(availableVolumes, path);
+ if (volume == null) {
+ Log.w(TAG, "Failed to update quota type for " + filePath);
+ return;
+ }
+ if (!volume.isEmulated()) {
+ // We only support quota tracking on emulated filesystems
+ return;
+ }
+ userId = volume.getOwner().getIdentifier();
}
- final int userId = volume.getOwner().getIdentifier();
if (userId < 0) {
throw new IllegalStateException("Failed to update quota type for " + filePath);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 90ec5aa..4f5b67c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11173,6 +11173,35 @@
"visual_query_accessibility_detection_enabled";
/**
+ * Timeout to be used for unbinding to the configured remote
+ * {@link android.service.ondeviceintelligence.OnDeviceIntelligenceService} if there are no
+ * requests in the queue. A value of -1 represents to never unbind.
+ *
+ * @hide
+ */
+ public static final String ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS =
+ "on_device_intelligence_unbind_timeout_ms";
+
+
+ /**
+ * Timeout that represents maximum idle time before which a callback should be populated.
+ *
+ * @hide
+ */
+ public static final String ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS =
+ "on_device_intelligence_idle_timeout_ms";
+
+ /**
+ * Timeout to be used for unbinding to the configured remote
+ * {@link android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService} if there
+ * are no requests in the queue. A value of -1 represents to never unbind.
+ *
+ * @hide
+ */
+ public static final String ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS =
+ "on_device_inference_unbind_timeout_ms";
+
+ /**
* Control whether Night display is currently activated.
* @hide
*/
diff --git a/core/java/android/provider/TEST_MAPPING b/core/java/android/provider/TEST_MAPPING
index d5ac7a7..2eb285d 100644
--- a/core/java/android/provider/TEST_MAPPING
+++ b/core/java/android/provider/TEST_MAPPING
@@ -8,6 +8,9 @@
}
]
},
+ {
+ "name": "CtsMediaProviderTestCases"
+ },
{
"name": "CalendarProviderTests"
},
diff --git a/core/java/android/tracing/perfetto/DataSource.java b/core/java/android/tracing/perfetto/DataSource.java
index b9ab82c..4de7b62 100644
--- a/core/java/android/tracing/perfetto/DataSource.java
+++ b/core/java/android/tracing/perfetto/DataSource.java
@@ -85,7 +85,7 @@
new TracingContext<>(this, instanceIndex);
fun.trace(ctx);
- ctx.flush();
+ nativeWritePackets(mNativeObj, ctx.getAndClearAllPendingTracePackets());
} while (nativePerfettoDsTraceIterateNext(mNativeObj));
} finally {
nativePerfettoDsTraceIterateBreak(mNativeObj);
@@ -130,7 +130,8 @@
* @param params Params to initialize the datasource with.
*/
public void register(DataSourceParams params) {
- nativeRegisterDataSource(this.mNativeObj, params.bufferExhaustedPolicy);
+ nativeRegisterDataSource(this.mNativeObj, params.bufferExhaustedPolicy,
+ params.willNotifyOnStop, params.noFlush);
}
/**
@@ -163,8 +164,8 @@
return this.createInstance(inputStream, instanceIndex);
}
- private static native void nativeRegisterDataSource(
- long dataSourcePtr, int bufferExhaustedPolicy);
+ private static native void nativeRegisterDataSource(long dataSourcePtr,
+ int bufferExhaustedPolicy, boolean willNotifyOnStop, boolean noFlush);
private static native long nativeCreate(DataSource thiz, String name);
private static native void nativeFlushAll(long nativeDataSourcePointer);
@@ -179,4 +180,6 @@
private static native boolean nativePerfettoDsTraceIterateNext(long dataSourcePtr);
private static native void nativePerfettoDsTraceIterateBreak(long dataSourcePtr);
private static native int nativeGetPerfettoDsInstanceIndex(long dataSourcePtr);
+
+ private static native void nativeWritePackets(long dataSourcePtr, byte[][] packetData);
}
diff --git a/core/java/android/tracing/perfetto/DataSourceParams.java b/core/java/android/tracing/perfetto/DataSourceParams.java
index 6cd04e3..e50f9d7 100644
--- a/core/java/android/tracing/perfetto/DataSourceParams.java
+++ b/core/java/android/tracing/perfetto/DataSourceParams.java
@@ -46,12 +46,67 @@
// after a while.
public static final int PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT = 1;
- public static DataSourceParams DEFAULTS =
- new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP);
+ public static DataSourceParams DEFAULTS = new DataSourceParams.Builder().build();
- public DataSourceParams(@PerfettoDsBufferExhausted int bufferExhaustedPolicy) {
+ private DataSourceParams(@PerfettoDsBufferExhausted int bufferExhaustedPolicy,
+ boolean willNotifyOnStop, boolean noFlush) {
this.bufferExhaustedPolicy = bufferExhaustedPolicy;
+ this.willNotifyOnStop = willNotifyOnStop;
+ this.noFlush = noFlush;
}
public final @PerfettoDsBufferExhausted int bufferExhaustedPolicy;
+ public final boolean willNotifyOnStop;
+ public final boolean noFlush;
+
+ /**
+ * DataSource Parameters builder
+ *
+ * @hide
+ */
+ public static final class Builder {
+ /**
+ * Specify behavior when running out of shared memory buffer space.
+ */
+ public Builder setBufferExhaustedPolicy(@PerfettoDsBufferExhausted int value) {
+ this.mBufferExhaustedPolicy = value;
+ return this;
+ }
+
+ /**
+ * If true, the data source is expected to ack the stop request through the
+ * NotifyDataSourceStopped() IPC. If false, the service won't wait for an ack.
+ * Set this parameter to false when dealing with potentially frozen producers
+ * that wouldn't be able to quickly ack the stop request.
+ *
+ * Default value: true
+ */
+ public Builder setWillNotifyOnStop(boolean value) {
+ this.mWillNotifyOnStop = value;
+ return this;
+ }
+
+ /**
+ * If true, the service won't emit flush requests for this data source. This
+ * allows the service to reduce the flush-related IPC traffic and better deal
+ * with frozen producers (see go/perfetto-frozen).
+ */
+ public Builder setNoFlush(boolean value) {
+ this.mNoFlush = value;
+ return this;
+ }
+
+ /**
+ * Build the DataSource parameters.
+ */
+ public DataSourceParams build() {
+ return new DataSourceParams(
+ this.mBufferExhaustedPolicy, this.mWillNotifyOnStop, this.mNoFlush);
+ }
+
+ private @PerfettoDsBufferExhausted int mBufferExhaustedPolicy =
+ PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP;
+ private boolean mWillNotifyOnStop = true;
+ private boolean mNoFlush = false;
+ }
}
diff --git a/core/java/android/tracing/perfetto/TracingContext.java b/core/java/android/tracing/perfetto/TracingContext.java
index 6b7df54..98cb4c8 100644
--- a/core/java/android/tracing/perfetto/TracingContext.java
+++ b/core/java/android/tracing/perfetto/TracingContext.java
@@ -59,19 +59,6 @@
}
/**
- * Forces a commit of the thread-local tracing data written so far to the
- * service. This is almost never required (tracing data is periodically
- * committed as trace pages are filled up) and has a non-negligible
- * performance hit (requires an IPC + refresh of the current thread-local
- * chunk). The only case when this should be used is when handling OnStop()
- * asynchronously, to ensure sure that the data is committed before the
- * Stop timeout expires.
- */
- public void flush() {
- nativeFlush(mDataSource.mNativeObj, getAndClearAllPendingTracePackets());
- }
-
- /**
* Can optionally be used to store custom per-sequence
* session data, which is not reset when incremental state is cleared
* (e.g. configuration options).
@@ -109,7 +96,7 @@
return incrementalState;
}
- private byte[][] getAndClearAllPendingTracePackets() {
+ protected byte[][] getAndClearAllPendingTracePackets() {
byte[][] res = new byte[mTracePackets.size()][];
for (int i = 0; i < mTracePackets.size(); i++) {
ProtoOutputStream tracePacket = mTracePackets.get(i);
@@ -120,8 +107,6 @@
return res;
}
- private static native void nativeFlush(long dataSourcePtr, byte[][] packetData);
-
private static native Object nativeGetCustomTls(long nativeDsPtr);
private static native void nativeSetCustomTls(long nativeDsPtr, Object tlsState);
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 9ff29a8..4837ee5 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -26,11 +26,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiContext;
+import android.app.Activity;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.StrictMode;
import android.os.SystemClock;
@@ -299,6 +301,11 @@
private VelocityTracker mVelocityTracker;
/**
+ * Determines strategy for velocity calculation
+ */
+ private @VelocityTracker.VelocityTrackerStrategy int mVelocityTrackerStrategy;
+
+ /**
* Consistency verifier for debugging purposes.
*/
private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
@@ -347,17 +354,17 @@
/**
* Creates a GestureDetector with the supplied listener.
- * This variant of the constructor should be used from a non-UI thread
+ * This variant of the constructor should be used from a non-UI thread
* (as it allows specifying the Handler).
- *
+ *
* @param listener the listener invoked for all the callbacks, this must
* not be null.
* @param handler the handler to use
*
* @throws NullPointerException if {@code listener} is null.
*
- * @deprecated Use {@link #GestureDetector(android.content.Context,
- * android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead.
+ * @deprecated Use {@link #GestureDetector(Context, GestureDetector.OnGestureListener, Handler)}
+ * instead.
*/
@Deprecated
public GestureDetector(@NonNull OnGestureListener listener, @Nullable Handler handler) {
@@ -367,15 +374,14 @@
/**
* Creates a GestureDetector with the supplied listener.
* You may only use this constructor from a UI thread (this is the usual situation).
- * @see android.os.Handler#Handler()
- *
+ * @see Handler#Handler()
+ *
* @param listener the listener invoked for all the callbacks, this must
* not be null.
- *
+ *
* @throws NullPointerException if {@code listener} is null.
*
- * @deprecated Use {@link #GestureDetector(android.content.Context,
- * android.view.GestureDetector.OnGestureListener)} instead.
+ * @deprecated Use {@link #GestureDetector(Context, GestureDetector.OnGestureListener)} instead.
*/
@Deprecated
public GestureDetector(@NonNull OnGestureListener listener) {
@@ -384,10 +390,10 @@
/**
* Creates a GestureDetector with the supplied listener.
- * You may only use this constructor from a {@link android.os.Looper} thread.
- * @see android.os.Handler#Handler()
+ * You may only use this constructor from a {@link Looper} thread.
+ * @see Handler#Handler()
*
- * @param context An {@link android.app.Activity} or a {@link Context} created from
+ * @param context An {@link Activity} or a {@link Context} created from
* {@link Context#createWindowContext(int, Bundle)}
* @param listener the listener invoked for all the callbacks, this must
* not be null. If the listener implements the {@link OnDoubleTapListener} or
@@ -404,10 +410,10 @@
/**
* Creates a GestureDetector with the supplied listener that runs deferred events on the
- * thread associated with the supplied {@link android.os.Handler}.
- * @see android.os.Handler#Handler()
+ * thread associated with the supplied {@link Handler}.
+ * @see Handler#Handler()
*
- * @param context An {@link android.app.Activity} or a {@link Context} created from
+ * @param context An {@link Activity} or a {@link Context} created from
* {@link Context#createWindowContext(int, Bundle)}
* @param listener the listener invoked for all the callbacks, this must
* not be null. If the listener implements the {@link OnDoubleTapListener} or
@@ -419,6 +425,31 @@
*/
public GestureDetector(@Nullable @UiContext Context context,
@NonNull OnGestureListener listener, @Nullable Handler handler) {
+ this(context, listener, handler, VelocityTracker.VELOCITY_TRACKER_STRATEGY_DEFAULT);
+ }
+
+ /**
+ * Creates a GestureDetector with the supplied listener that runs deferred events on the
+ * thread associated with the supplied {@link Handler}.
+ * @see Handler#Handler()
+ *
+ * @param context An {@link Activity} or a {@link Context} created from
+ * {@link Context#createWindowContext(int, Bundle)}
+ * @param listener the listener invoked for all the callbacks, this must
+ * not be null. If the listener implements the {@link OnDoubleTapListener} or
+ * {@link OnContextClickListener} then it will also be set as the listener for
+ * these callbacks (for example when using the {@link SimpleOnGestureListener}).
+ * @param handler the handler to use for running deferred listener events.
+ * @param velocityTrackerStrategy strategy to use for velocity calculation of scroll/fling
+ * events.
+ *
+ * @throws NullPointerException if {@code listener} is null.
+ *
+ * @hide
+ */
+ public GestureDetector(@Nullable @UiContext Context context,
+ @NonNull OnGestureListener listener, @Nullable Handler handler,
+ @VelocityTracker.VelocityTrackerStrategy int velocityTrackerStrategy) {
if (handler != null) {
mHandler = new GestureHandler(handler);
} else {
@@ -431,15 +462,16 @@
if (listener instanceof OnContextClickListener) {
setContextClickListener((OnContextClickListener) listener);
}
+ mVelocityTrackerStrategy = velocityTrackerStrategy;
init(context);
}
-
+
/**
* Creates a GestureDetector with the supplied listener that runs deferred events on the
- * thread associated with the supplied {@link android.os.Handler}.
- * @see android.os.Handler#Handler()
+ * thread associated with the supplied {@link Handler}.
+ * @see Handler#Handler()
*
- * @param context An {@link android.app.Activity} or a {@link Context} created from
+ * @param context An {@link Activity} or a {@link Context} created from
* {@link Context#createWindowContext(int, Bundle)}
* @param listener the listener invoked for all the callbacks, this must
* not be null.
@@ -547,7 +579,7 @@
mCurrentMotionEvent = MotionEvent.obtain(ev);
if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
+ mVelocityTracker = VelocityTracker.obtain(mVelocityTrackerStrategy);
}
mVelocityTracker.addMovement(ev);
diff --git a/core/java/android/view/ImeBackAnimationController.java b/core/java/android/view/ImeBackAnimationController.java
index 9503f49..8c14de6 100644
--- a/core/java/android/view/ImeBackAnimationController.java
+++ b/core/java/android/view/ImeBackAnimationController.java
@@ -210,18 +210,9 @@
mInsetsController.setPredictiveBackImeHideAnimInProgress(true);
notifyHideIme();
}
- if (mStartRootScrollY != 0) {
- // RootView is panned, ensure that it is scrolled back to the intended scroll position
- if (triggerBack) {
- // requesting ime as invisible
- mInsetsController.setRequestedVisibleTypes(0, ime());
- // changes the animation state and notifies RootView of changed insets, which
- // causes it to reset its scrollY to 0f (animated)
- mInsetsController.onAnimationStateChanged(ime(), /*running*/ true);
- } else {
- // This causes RootView to update its scroll back to the panned position
- mInsetsController.getHost().notifyInsetsChanged();
- }
+ if (mStartRootScrollY != 0 && !triggerBack) {
+ // This causes RootView to update its scroll back to the panned position
+ mInsetsController.getHost().notifyInsetsChanged();
}
}
@@ -237,6 +228,12 @@
// the IME away
mInsetsController.getHost().getInputMethodManager()
.notifyImeHidden(mInsetsController.getHost().getWindowToken(), statsToken);
+
+ // requesting IME as invisible during post-commit
+ mInsetsController.setRequestedVisibleTypes(0, ime());
+ // Changes the animation state. This also notifies RootView of changed insets, which causes
+ // it to reset its scrollY to 0f (animated) if it was panned
+ mInsetsController.onAnimationStateChanged(ime(), /*running*/ true);
}
private void reset() {
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index d31f823..27176a4 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -264,7 +264,6 @@
/**
* Obtains a velocity tracker with the specified strategy.
- * For testing and comparison purposes only.
*
* @param strategy The strategy Id, VELOCITY_TRACKER_STRATEGY_DEFAULT to use the default.
* @return The velocity tracker.
@@ -272,6 +271,9 @@
* @hide
*/
public static VelocityTracker obtain(int strategy) {
+ if (strategy == VELOCITY_TRACKER_STRATEGY_DEFAULT) {
+ return obtain();
+ }
return new VelocityTracker(strategy);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 155c053..1df851a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -427,12 +427,6 @@
private static final long NANOS_PER_SEC = 1000000000;
- // If the ViewRootImpl has been idle for more than 200ms, clear the preferred
- // frame rate category and frame rate.
- private static final int IDLE_TIME_MILLIS = 250;
-
- private static final long NANOS_PER_MILLI = 1_000_000;
-
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
@@ -665,8 +659,6 @@
private int mMinusOneFrameIntervalMillis = 0;
// VRR interval between the previous and the frame before
private int mMinusTwoFrameIntervalMillis = 0;
- // VRR has the invalidation idle message been posted?
- private boolean mInvalidationIdleMessagePosted = false;
/**
* Update the Choreographer's FrameInfo object with the timing information for the current
@@ -4278,10 +4270,6 @@
// when the values are applicable.
if (mDrawnThisFrame) {
mDrawnThisFrame = false;
- if (!mInvalidationIdleMessagePosted) {
- mInvalidationIdleMessagePosted = true;
- mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS);
- }
setCategoryFromCategoryCounts();
updateInfrequentCount();
setPreferredFrameRate(mPreferredFrameRate);
@@ -6523,8 +6511,6 @@
return "MSG_WINDOW_TOUCH_MODE_CHANGED";
case MSG_KEEP_CLEAR_RECTS_CHANGED:
return "MSG_KEEP_CLEAR_RECTS_CHANGED";
- case MSG_CHECK_INVALIDATION_IDLE:
- return "MSG_CHECK_INVALIDATION_IDLE";
case MSG_REFRESH_POINTER_ICON:
return "MSG_REFRESH_POINTER_ICON";
case MSG_TOUCH_BOOST_TIMEOUT:
@@ -6789,30 +6775,6 @@
mNumPausedForSync = 0;
scheduleTraversals();
break;
- case MSG_CHECK_INVALIDATION_IDLE: {
- long delta;
- if (mIsTouchBoosting || mIsFrameRateBoosting || mInsetsAnimationRunning) {
- delta = 0;
- } else {
- delta = System.nanoTime() / NANOS_PER_MILLI - mLastUpdateTimeMillis;
- }
- if (delta >= IDLE_TIME_MILLIS) {
- mFrameRateCategoryHighCount = 0;
- mFrameRateCategoryHighHintCount = 0;
- mFrameRateCategoryNormalCount = 0;
- mFrameRateCategoryLowCount = 0;
- mPreferredFrameRate = 0;
- mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
- setPreferredFrameRateCategory(FRAME_RATE_CATEGORY_NO_PREFERENCE);
- setPreferredFrameRate(0f);
- mInvalidationIdleMessagePosted = false;
- } else {
- mInvalidationIdleMessagePosted = true;
- mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
- IDLE_TIME_MILLIS - delta);
- }
- break;
- }
case MSG_TOUCH_BOOST_TIMEOUT:
/**
* Lower the frame rate after the boosting period (FRAME_RATE_TOUCH_BOOST_TIME).
@@ -8609,48 +8571,55 @@
private int mPendingKeyMetaState;
- private final GestureDetector mGestureDetector = new GestureDetector(mContext,
- new GestureDetector.OnGestureListener() {
- @Override
- public boolean onDown(@NonNull MotionEvent e) {
- // This can be ignored since it's not clear what KeyEvent this will
- // belong to.
- return true;
- }
-
- @Override
- public void onShowPress(@NonNull MotionEvent e) {
-
- }
-
- @Override
- public boolean onSingleTapUp(@NonNull MotionEvent e) {
- dispatchTap(e.getEventTime());
- return true;
- }
-
- @Override
- public boolean onScroll(@Nullable MotionEvent e1, @NonNull MotionEvent e2,
- float distanceX, float distanceY) {
- // Scroll doesn't translate to DPAD events so should be ignored.
- return true;
- }
-
- @Override
- public void onLongPress(@NonNull MotionEvent e) {
- // Long presses don't translate to DPAD events so should be ignored.
- }
-
- @Override
- public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2,
- float velocityX, float velocityY) {
- dispatchFling(velocityX, velocityY, e2.getEventTime());
- return true;
- }
- });
+ private final GestureDetector mGestureDetector;
SyntheticTouchNavigationHandler() {
super(true);
+ int gestureDetectorVelocityStrategy =
+ android.companion.virtual.flags.Flags
+ .impulseVelocityStrategyForTouchNavigation()
+ ? VelocityTracker.VELOCITY_TRACKER_STRATEGY_IMPULSE
+ : VelocityTracker.VELOCITY_TRACKER_STRATEGY_DEFAULT;
+ mGestureDetector = new GestureDetector(mContext,
+ new GestureDetector.OnGestureListener() {
+ @Override
+ public boolean onDown(@NonNull MotionEvent e) {
+ // This can be ignored since it's not clear what KeyEvent this will
+ // belong to.
+ return true;
+ }
+
+ @Override
+ public void onShowPress(@NonNull MotionEvent e) {
+ }
+
+ @Override
+ public boolean onSingleTapUp(@NonNull MotionEvent e) {
+ dispatchTap(e.getEventTime());
+ return true;
+ }
+
+ @Override
+ public boolean onScroll(@Nullable MotionEvent e1, @NonNull MotionEvent e2,
+ float distanceX, float distanceY) {
+ // Scroll doesn't translate to DPAD events so should be ignored.
+ return true;
+ }
+
+ @Override
+ public void onLongPress(@NonNull MotionEvent e) {
+ // Long presses don't translate to DPAD events so should be ignored.
+ }
+
+ @Override
+ public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2,
+ float velocityX, float velocityY) {
+ dispatchFling(velocityX, velocityY, e2.getEventTime());
+ return true;
+ }
+ },
+ /* handler= */ null,
+ gestureDetectorVelocityStrategy);
}
public void process(MotionEvent event) {
@@ -13034,10 +13003,6 @@
private void removeVrrMessages() {
mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
- if (mInvalidationIdleMessagePosted) {
- mInvalidationIdleMessagePosted = false;
- mHandler.removeMessages(MSG_CHECK_INVALIDATION_IDLE);
- }
}
/**
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 7885cd9..11ee286 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -54,6 +54,7 @@
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -431,6 +432,14 @@
* @hide
*/
public InputMethodInfo(InputMethodInfo source) {
+ this(source, Collections.emptyList());
+ }
+
+ /**
+ * @hide
+ */
+ public InputMethodInfo(@NonNull InputMethodInfo source,
+ @NonNull List<InputMethodSubtype> additionalSubtypes) {
mId = source.mId;
mSettingsActivityName = source.mSettingsActivityName;
mLanguageSettingsActivityName = source.mLanguageSettingsActivityName;
@@ -445,7 +454,19 @@
mIsVrOnly = source.mIsVrOnly;
mIsVirtualDeviceOnly = source.mIsVirtualDeviceOnly;
mService = source.mService;
- mSubtypes = source.mSubtypes;
+ if (additionalSubtypes.isEmpty()) {
+ mSubtypes = source.mSubtypes;
+ } else {
+ final ArrayList<InputMethodSubtype> subtypes = source.mSubtypes.toList();
+ final int additionalSubtypeCount = additionalSubtypes.size();
+ for (int i = 0; i < additionalSubtypeCount; ++i) {
+ final InputMethodSubtype additionalSubtype = additionalSubtypes.get(i);
+ if (!subtypes.contains(additionalSubtype)) {
+ subtypes.add(additionalSubtype);
+ }
+ }
+ mSubtypes = new InputMethodSubtypeArray(subtypes);
+ }
mHandledConfigChanges = source.mHandledConfigChanges;
mSupportsStylusHandwriting = source.mSupportsStylusHandwriting;
mSupportsConnectionlessStylusHandwriting = source.mSupportsConnectionlessStylusHandwriting;
diff --git a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java
index c243a22..e2d343f 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java
@@ -25,6 +25,7 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
@@ -163,6 +164,18 @@
}
/**
+ * @return A list of {@link InputMethodInfo} copied from this array.
+ */
+ @NonNull
+ public ArrayList<InputMethodSubtype> toList() {
+ final ArrayList<InputMethodSubtype> list = new ArrayList<>(mCount);
+ for (int i = 0; i < mCount; ++i) {
+ list.add(get(i));
+ }
+ return list;
+ }
+
+ /**
* Return the number of {@link InputMethodSubtype} objects.
*/
public int getCount() {
diff --git a/core/java/android/widget/flags/notification_widget_flags.aconfig b/core/java/android/widget/flags/notification_widget_flags.aconfig
index 3a39631..503e542 100644
--- a/core/java/android/widget/flags/notification_widget_flags.aconfig
+++ b/core/java/android/widget/flags/notification_widget_flags.aconfig
@@ -47,3 +47,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "messaging_child_request_layout"
+ namespace: "systemui"
+ description: "MessagingChild always needs to be measured during MessagingLinearLayout onMeasure."
+ bug: "324537506"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 31e3a34..af3d7eb 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -56,6 +56,8 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.policy.DecorView;
+import java.io.Closeable;
+import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.function.Consumer;
@@ -568,6 +570,12 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
releaseAnimationSurfaceHost();
+ if (mIconView instanceof ImageView imageView
+ && imageView.getDrawable() instanceof Closeable closeableDrawable) {
+ try {
+ closeableDrawable.close();
+ } catch (IOException ignore) { }
+ }
}
@Override
diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java
index d0f3c3e..6f7660a 100644
--- a/core/java/android/window/TaskFragmentOperation.java
+++ b/core/java/android/window/TaskFragmentOperation.java
@@ -84,8 +84,8 @@
/**
* Sets the activity navigation to be isolated, where the activity navigation on the
* TaskFragment is separated from the rest activities in the Task. Activities cannot be
- * started on an isolated TaskFragment unless the activities are launched from the same
- * TaskFragment or explicitly requested to.
+ * started on an isolated TaskFragment unless explicitly requested to. That said, new launched
+ * activities should be positioned as a sibling to the TaskFragment with higher z-ordering.
*/
public static final int OP_TYPE_SET_ISOLATED_NAVIGATION = 11;
@@ -149,6 +149,18 @@
*/
public static final int OP_TYPE_SET_DECOR_SURFACE_BOOSTED = 18;
+ /**
+ * Sets the TaskFragment to be pinned.
+ * <p>
+ * If a TaskFragment is pinned, the TaskFragment should be the top-most TaskFragment among other
+ * sibling TaskFragments. Any newly launched and embeddable activity should not be placed in the
+ * pinned TaskFragment, unless the activity is launched from the pinned TaskFragment or
+ * explicitly requested to. Non-embeddable activities are not restricted to.
+ * <p>
+ * See {@link #OP_TYPE_REORDER_TO_FRONT} on how to reorder a pinned TaskFragment to the top.
+ */
+ public static final int OP_TYPE_SET_PINNED = 19;
+
@IntDef(prefix = { "OP_TYPE_" }, value = {
OP_TYPE_UNKNOWN,
OP_TYPE_CREATE_TASK_FRAGMENT,
@@ -170,6 +182,7 @@
OP_TYPE_SET_DIM_ON_TASK,
OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH,
OP_TYPE_SET_DECOR_SURFACE_BOOSTED,
+ OP_TYPE_SET_PINNED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface OperationType {}
diff --git a/core/java/android/window/TaskSnapshot.java b/core/java/android/window/TaskSnapshot.java
index 41b6d31..a2e3d40 100644
--- a/core/java/android/window/TaskSnapshot.java
+++ b/core/java/android/window/TaskSnapshot.java
@@ -16,6 +16,7 @@
package android.window;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
@@ -32,6 +33,9 @@
import android.view.Surface;
import android.view.WindowInsetsController;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Represents a task snapshot.
* @hide
@@ -68,6 +72,21 @@
private final boolean mHasImeSurface;
// Must be one of the named color spaces, otherwise, always use SRGB color space.
private final ColorSpace mColorSpace;
+ private int mInternalReferences;
+
+ /** This snapshot object is being broadcast. */
+ public static final int REFERENCE_BROADCAST = 1;
+ /** This snapshot object is in the cache. */
+ public static final int REFERENCE_CACHE = 1 << 1;
+ /** This snapshot object is being persistent. */
+ public static final int REFERENCE_PERSIST = 1 << 2;
+ @IntDef(flag = true, prefix = { "REFERENCE_" }, value = {
+ REFERENCE_BROADCAST,
+ REFERENCE_CACHE,
+ REFERENCE_PERSIST
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ReferenceFlags {}
public TaskSnapshot(long id, long captureTime,
@NonNull ComponentName topActivityComponent, HardwareBuffer snapshot,
@@ -296,7 +315,28 @@
+ " mWindowingMode=" + mWindowingMode
+ " mAppearance=" + mAppearance
+ " mIsTranslucent=" + mIsTranslucent
- + " mHasImeSurface=" + mHasImeSurface;
+ + " mHasImeSurface=" + mHasImeSurface
+ + " mInternalReferences=" + mInternalReferences;
+ }
+
+ /**
+ * Adds a reference when the object is held somewhere.
+ * Only used in core.
+ */
+ public synchronized void addReference(@ReferenceFlags int usage) {
+ mInternalReferences |= usage;
+ }
+
+ /**
+ * Removes a reference when the object is not held from somewhere. The snapshot will be closed
+ * once the reference becomes zero.
+ * Only used in core.
+ */
+ public synchronized void removeReference(@ReferenceFlags int usage) {
+ mInternalReferences &= ~usage;
+ if (mInternalReferences == 0 && mSnapshot != null && !mSnapshot.isClosed()) {
+ mSnapshot.close();
+ }
}
public static final @NonNull Creator<TaskSnapshot> CREATOR = new Creator<TaskSnapshot>() {
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index 6864bf7..8e18f84 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -24,6 +24,7 @@
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__DISABLED;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__ENABLED;
@@ -32,6 +33,7 @@
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON_LONG_PRESS;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__QUICK_SETTINGS;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TWO_FINGER_TRIPLE_TAP;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
@@ -48,7 +50,6 @@
import android.content.Context;
import android.provider.Settings;
-import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.internal.util.FrameworkStatsLog;
@@ -248,6 +249,8 @@
}
case HARDWARE:
return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
+ case QUICK_SETTINGS:
+ return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__QUICK_SETTINGS;
}
return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
}
diff --git a/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java b/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java
index 91b80dd..24cd1c9 100644
--- a/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java
+++ b/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java
@@ -52,8 +52,14 @@
ImeTracingPerfettoImpl() {
Producer.init(InitArguments.DEFAULTS);
- mDataSource.register(
- new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT));
+ DataSourceParams params =
+ new DataSourceParams.Builder()
+ .setBufferExhaustedPolicy(
+ PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+ .setNoFlush(true)
+ .setWillNotifyOnStop(false)
+ .build();
+ mDataSource.register(params);
}
diff --git a/core/java/com/android/internal/inputmethod/InlineSuggestionsRequestCallback.java b/core/java/com/android/internal/inputmethod/InlineSuggestionsRequestCallback.java
new file mode 100644
index 0000000..92d453b
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InlineSuggestionsRequestCallback.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 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.internal.inputmethod;
+
+import android.annotation.BinderThread;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+/**
+ * An internal interface that mirrors {@link IInlineSuggestionsRequestCallback}.
+ *
+ * <p>This interface is used to forward incoming IPCs from
+ * {@link com.android.server.inputmethod.AutofillSuggestionsController} to
+ * {@link com.android.server.autofill.AutofillInlineSuggestionsRequestSession}.</p>
+ */
+public interface InlineSuggestionsRequestCallback {
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsUnsupported()}.
+ */
+ @BinderThread
+ void onInlineSuggestionsUnsupported();
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsRequest(
+ * InlineSuggestionsRequest, IInlineSuggestionsResponseCallback)}.
+ */
+ @BinderThread
+ void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
+ IInlineSuggestionsResponseCallback callback);
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodStartInput(AutofillId)}.
+ */
+ @BinderThread
+ void onInputMethodStartInput(AutofillId imeFieldId);
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodShowInputRequested(boolean)}.
+ */
+ @BinderThread
+ void onInputMethodShowInputRequested(boolean requestResult);
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodStartInputView()}.
+ */
+ @BinderThread
+ void onInputMethodStartInputView();
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodFinishInputView()}.
+ */
+ @BinderThread
+ void onInputMethodFinishInputView();
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodFinishInput()}.
+ */
+ @BinderThread
+ void onInputMethodFinishInput();
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsSessionInvalidated()}.
+ */
+ @BinderThread
+ void onInlineSuggestionsSessionInvalidated();
+}
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index ee33eb4..37b7288 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -131,8 +131,13 @@
Runnable cacheUpdater
) {
Producer.init(InitArguments.DEFAULTS);
- mDataSource.register(new DataSourceParams(
- DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT));
+ DataSourceParams params =
+ new DataSourceParams.Builder()
+ .setBufferExhaustedPolicy(
+ DataSourceParams
+ .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+ .build();
+ mDataSource.register(params);
this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider;
this.mViewerConfigReader = viewerConfigReader;
this.mLogGroups = logGroups;
@@ -186,8 +191,6 @@
}
os.end(outProtologViewerConfigToken);
-
- ctx.flush();
} catch (IOException e) {
Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump on tracing end", e);
}
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
index e07acac..3d8237e 100644
--- a/core/java/com/android/internal/widget/MessagingLinearLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -16,6 +16,8 @@
package com.android.internal.widget;
+import static android.widget.flags.Flags.messagingChildRequestLayout;
+
import android.annotation.Nullable;
import android.annotation.Px;
import android.content.Context;
@@ -92,6 +94,10 @@
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.hide = true;
+ // Child always needs to be measured to calculate hide property correctly in onMeasure.
+ if (messagingChildRequestLayout()) {
+ child.requestLayout();
+ }
if (child instanceof MessagingChild) {
MessagingChild messagingChild = (MessagingChild) child;
// Whenever we encounter the message first, it's always first in the layout
diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java
index 4031b2f..0f4615a 100644
--- a/core/java/com/android/internal/widget/NotificationRowIconView.java
+++ b/core/java/com/android/internal/widget/NotificationRowIconView.java
@@ -16,18 +16,27 @@
package com.android.internal.widget;
+import android.annotation.Nullable;
import android.app.Flags;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.util.AttributeSet;
+import android.view.RemotableViewMethod;
import android.widget.RemoteViews;
-import androidx.annotation.Nullable;
-
/**
* An image view that holds the icon displayed on the left side of a notification row.
*/
@RemoteViews.RemoteView
public class NotificationRowIconView extends CachingIconView {
+ private boolean mApplyCircularCrop = false;
+
public NotificationRowIconView(Context context) {
super(context);
}
@@ -57,4 +66,82 @@
super.onFinishInflate();
}
+
+ @Nullable
+ @Override
+ Drawable loadSizeRestrictedIcon(@Nullable Icon icon) {
+ final Drawable original = super.loadSizeRestrictedIcon(icon);
+ final Drawable result;
+ if (mApplyCircularCrop) {
+ result = makeCircularDrawable(original);
+ } else {
+ result = original;
+ }
+
+ return result;
+ }
+
+ /**
+ * Enables circle crop that makes given image circular
+ */
+ @RemotableViewMethod(asyncImpl = "setApplyCircularCropAsync")
+ public void setApplyCircularCrop(boolean applyCircularCrop) {
+ mApplyCircularCrop = applyCircularCrop;
+ }
+
+ /**
+ * Async version of {@link NotificationRowIconView#setApplyCircularCrop}
+ */
+ public Runnable setApplyCircularCropAsync(boolean applyCircularCrop) {
+ mApplyCircularCrop = applyCircularCrop;
+ return () -> {
+ };
+ }
+
+ @Nullable
+ private Drawable makeCircularDrawable(@Nullable Drawable original) {
+ if (original == null) {
+ return original;
+ }
+
+ final Bitmap source = drawableToBitmap(original);
+
+ int size = Math.min(source.getWidth(), source.getHeight());
+
+ Bitmap squared = Bitmap.createScaledBitmap(source, size, size, /* filter= */ false);
+ Bitmap result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+
+ final Canvas canvas = new Canvas(result);
+ final Paint paint = new Paint();
+ paint.setShader(
+ new BitmapShader(squared, BitmapShader.TileMode.CLAMP,
+ BitmapShader.TileMode.CLAMP));
+ paint.setAntiAlias(true);
+ float radius = size / 2f;
+ canvas.drawCircle(radius, radius, radius, paint);
+ return new BitmapDrawable(getResources(), result);
+ }
+
+ private static Bitmap drawableToBitmap(Drawable drawable) {
+ if (drawable instanceof BitmapDrawable bitmapDrawable) {
+ final Bitmap bitmap = bitmapDrawable.getBitmap();
+ if (bitmap.getConfig() == Bitmap.Config.HARDWARE) {
+ return bitmap.copy(Bitmap.Config.ARGB_8888, false);
+ } else {
+ return bitmap;
+ }
+ }
+
+ int width = drawable.getIntrinsicWidth();
+ width = width > 0 ? width : 1;
+ int height = drawable.getIntrinsicHeight();
+ height = height > 0 ? height : 1;
+
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+
+ return bitmap;
+ }
}
diff --git a/core/java/com/android/internal/widget/TEST_MAPPING b/core/java/com/android/internal/widget/TEST_MAPPING
new file mode 100644
index 0000000..91cecfd
--- /dev/null
+++ b/core/java/com/android/internal/widget/TEST_MAPPING
@@ -0,0 +1,19 @@
+{
+ // v2/sysui/suite/test-mapping-sysui-screenshot-test
+ "sysui-screenshot-test": [
+ {
+ "name": "SystemUIGoogleScreenshotTests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/core/jni/android_tracing_PerfettoDataSource.cpp b/core/jni/android_tracing_PerfettoDataSource.cpp
index f82ebfe..17129d8 100644
--- a/core/jni/android_tracing_PerfettoDataSource.cpp
+++ b/core/jni/android_tracing_PerfettoDataSource.cpp
@@ -244,8 +244,8 @@
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&nativeDestroy));
}
-void nativeFlush(JNIEnv* env, jclass clazz, jlong ds_ptr, jobjectArray packets) {
- ALOG(LOG_DEBUG, LOG_TAG, "nativeFlush(%p)", (void*)ds_ptr);
+void nativeWritePackets(JNIEnv* env, jclass clazz, jlong ds_ptr, jobjectArray packets) {
+ ALOG(LOG_DEBUG, LOG_TAG, "nativeWritePackets(%p)", (void*)ds_ptr);
sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(ds_ptr);
datasource->WritePackets(env, packets);
}
@@ -256,10 +256,12 @@
}
void nativeRegisterDataSource(JNIEnv* env, jclass clazz, jlong datasource_ptr,
- jint buffer_exhausted_policy) {
+ jint buffer_exhausted_policy, jboolean will_notify_on_stop,
+ jboolean no_flush) {
sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(datasource_ptr);
struct PerfettoDsParams params = PerfettoDsParamsDefault();
+ params.will_notify_on_stop = will_notify_on_stop;
params.buffer_exhausted_policy = (PerfettoDsBufferExhaustedPolicy)buffer_exhausted_policy;
params.user_arg = reinterpret_cast<void*>(datasource.get());
@@ -325,13 +327,15 @@
datasource_instance->onStart(env);
};
- params.on_flush_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex, void*, void* inst_ctx,
- struct PerfettoDsOnFlushArgs*) {
- JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
+ if (!no_flush) {
+ params.on_flush_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex, void*,
+ void* inst_ctx, struct PerfettoDsOnFlushArgs*) {
+ JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
- auto* datasource_instance = static_cast<PerfettoDataSourceInstance*>(inst_ctx);
- datasource_instance->onFlush(env);
- };
+ auto* datasource_instance = static_cast<PerfettoDataSourceInstance*>(inst_ctx);
+ datasource_instance->onFlush(env);
+ };
+ }
params.on_stop_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex inst_id, void* user_arg,
void* inst_ctx, struct PerfettoDsOnStopArgs*) {
@@ -422,7 +426,7 @@
(void*)nativeCreate},
{"nativeFlushAll", "(J)V", (void*)nativeFlushAll},
{"nativeGetFinalizer", "()J", (void*)nativeGetFinalizer},
- {"nativeRegisterDataSource", "(JI)V", (void*)nativeRegisterDataSource},
+ {"nativeRegisterDataSource", "(JIZZ)V", (void*)nativeRegisterDataSource},
{"nativeGetPerfettoInstanceLocked", "(JI)Landroid/tracing/perfetto/DataSourceInstance;",
(void*)nativeGetPerfettoInstanceLocked},
{"nativeReleasePerfettoInstanceLocked", "(JI)V",
@@ -431,11 +435,12 @@
{"nativePerfettoDsTraceIterateBegin", "(J)Z", (void*)nativePerfettoDsTraceIterateBegin},
{"nativePerfettoDsTraceIterateNext", "(J)Z", (void*)nativePerfettoDsTraceIterateNext},
{"nativePerfettoDsTraceIterateBreak", "(J)V", (void*)nativePerfettoDsTraceIterateBreak},
- {"nativeGetPerfettoDsInstanceIndex", "(J)I", (void*)nativeGetPerfettoDsInstanceIndex}};
+ {"nativeGetPerfettoDsInstanceIndex", "(J)I", (void*)nativeGetPerfettoDsInstanceIndex},
+
+ {"nativeWritePackets", "(J[[B)V", (void*)nativeWritePackets}};
const JNINativeMethod gMethodsTracingContext[] = {
/* name, signature, funcPtr */
- {"nativeFlush", "(J[[B)V", (void*)nativeFlush},
{"nativeGetCustomTls", "(J)Ljava/lang/Object;", (void*)nativeGetCustomTls},
{"nativeGetIncrementalState", "(J)Ljava/lang/Object;", (void*)nativeGetIncrementalState},
{"nativeSetCustomTls", "(JLjava/lang/Object;)V", (void*)nativeSetCustomTls},
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c92435f..654d83c 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -607,7 +607,7 @@
optional InsetsSourceProviderProto insets_source_provider = 1;
optional WindowStateProto ime_target_from_ime = 2;
- optional bool is_ime_layout_drawn = 3;
+ optional bool is_ime_layout_drawn = 3 [deprecated=true];
}
message BackNavigationProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 70d923b..8541704 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -299,6 +299,9 @@
<protected-broadcast android:name="android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED" />
+ <protected-broadcast android:name="android.hardware.hdmi.action.OSD_MESSAGE" />
+ <protected-broadcast android:name="android.hardware.hdmi.action.ON_ACTIVE_SOURCE_RECOVERED_DISMISS_UI" />
+
<protected-broadcast android:name="android.hardware.usb.action.USB_STATE" />
<protected-broadcast android:name="android.hardware.usb.action.USB_PORT_CHANGED" />
<protected-broadcast android:name="android.hardware.usb.action.USB_PORT_COMPLIANCE_CHANGED" />
diff --git a/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml b/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml
new file mode 100644
index 0000000..3b288d7
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 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
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_header_height"
+ android:clipChildren="false"
+ android:tag="compactMessagingHUN"
+ android:gravity="center_vertical"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ android:importantForAccessibility="no">
+ <com.android.internal.widget.NotificationRowIconView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/notification_icon_circle_size"
+ android:layout_height="@dimen/notification_icon_circle_size"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:background="@drawable/notification_icon_circle"
+ android:padding="@dimen/notification_icon_circle_padding"
+ android:maxDrawableWidth="@dimen/notification_icon_circle_size"
+ android:maxDrawableHeight="@dimen/notification_icon_circle_size"
+ />
+ <com.android.internal.widget.NotificationRowIconView
+ android:id="@+id/conversation_icon"
+ android:layout_width="@dimen/notification_icon_circle_size"
+ android:layout_height="@dimen/notification_icon_circle_size"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:maxDrawableWidth="@dimen/notification_icon_circle_size"
+ android:maxDrawableHeight="@dimen/notification_icon_circle_size"
+ android:scaleType="centerCrop"
+ android:importantForAccessibility="no"
+ />
+ <FrameLayout
+ android:id="@+id/alternate_expand_target"
+ android:layout_width="@dimen/notification_content_margin_start"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:importantForAccessibility="no"
+ android:focusable="false"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:orientation="horizontal"
+ >
+ <NotificationTopLineView
+ android:id="@+id/notification_top_line"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_centerVertical="true"
+ android:layout_weight="1"
+ android:clipChildren="false"
+ android:gravity="center_vertical"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ />
+ <include layout="@layout/notification_top_line_views" />
+ </NotificationTopLineView>
+ <FrameLayout
+ android:id="@+id/reply_action_container"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/notification_action_list_height"
+ android:gravity="center_vertical"
+ android:orientation="horizontal" />
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|end"
+ />
+ </FrameLayout>
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/side_fps_toast.xml b/core/res/res/layout/side_fps_toast.xml
index 2c35c9b..78299ab 100644
--- a/core/res/res/layout/side_fps_toast.xml
+++ b/core/res/res/layout/side_fps_toast.xml
@@ -25,6 +25,7 @@
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="6"
+ android:paddingBottom="10dp"
android:text="@string/fp_power_button_enrollment_title"
android:textColor="@color/side_fps_text_color"
android:paddingLeft="20dp"/>
@@ -36,6 +37,7 @@
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="3"
+ android:paddingBottom="10dp"
android:text="@string/fp_power_button_enrollment_button_text"
style="?android:attr/buttonBarNegativeButtonStyle"
android:textColor="@color/side_fps_button_color"
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 31acd9a..5266214 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -93,4 +93,7 @@
<!-- If this is true, allow wake from theater mode from motion. -->
<bool name="config_allowTheaterModeWakeFromMotion">true</bool>
+
+ <!-- True if the device supports system decorations on secondary displays. -->
+ <bool name="config_supportsSystemDecorsOnSecondaryDisplays">false</bool>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 59e4161..28678c1 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -794,6 +794,9 @@
<!-- The divider symbol between different parts of the notification header including spaces. not translatable [CHAR LIMIT=3] -->
<string name="notification_header_divider_symbol_with_spaces" translatable="false">" • "</string>
+ <!-- Text for inline reply button for compact conversation heads ups -->
+ <string name="notification_compact_heads_up_reply">Reply</string>
+
<!-- Text shown in place of notification contents when the notification is hidden on a secure lockscreen -->
<string name="notification_hidden_text">New notification</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d004b05..a9d03f5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2353,6 +2353,7 @@
<java-symbol type="layout" name="notification_template_material_base" />
<java-symbol type="layout" name="notification_template_material_heads_up_base" />
<java-symbol type="layout" name="notification_template_material_compact_heads_up_base" />
+ <java-symbol type="layout" name="notification_template_material_messaging_compact_heads_up" />
<java-symbol type="layout" name="notification_template_material_big_base" />
<java-symbol type="layout" name="notification_template_material_big_picture" />
<java-symbol type="layout" name="notification_template_material_inbox" />
@@ -3628,6 +3629,7 @@
<java-symbol type="drawable" name="lockscreen_selected" />
<java-symbol type="string" name="notification_header_divider_symbol_with_spaces" />
+ <java-symbol type="string" name="notification_compact_heads_up_reply" />
<java-symbol type="color" name="notification_primary_text_color_light" />
<java-symbol type="color" name="notification_primary_text_color_dark" />
@@ -4522,6 +4524,7 @@
<java-symbol type="id" name="expand_button_container" />
<java-symbol type="id" name="expand_button_a11y_container" />
<java-symbol type="id" name="expand_button_touch_container" />
+ <java-symbol type="id" name="reply_action_container" />
<java-symbol type="id" name="messaging_group_content_container" />
<java-symbol type="id" name="expand_button_and_content_container" />
<java-symbol type="id" name="conversation_header" />
diff --git a/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
index 6ae3d65..df9a89e 100644
--- a/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
+++ b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
@@ -674,8 +674,6 @@
protoOutputStream.write(SINGLE_INT, singleIntValue);
protoOutputStream.end(payloadToken);
protoOutputStream.end(forTestingToken);
-
- ctx.flush();
}),
(args) -> {}
);
diff --git a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
index 57bbb1c..5917cc1 100644
--- a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
+++ b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
@@ -37,6 +37,7 @@
import android.content.Context;
import android.graphics.Insets;
import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
import android.view.animation.BackGestureInterpolator;
import android.view.animation.Interpolator;
import android.view.inputmethod.InputMethodManager;
@@ -54,6 +55,8 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.lang.reflect.Field;
+
/**
* Tests for {@link ImeBackAnimationController}.
*
@@ -104,6 +107,13 @@
when(mInsetsController.getHost()).thenReturn(mViewRootInsetsControllerHost);
when(mViewRootInsetsControllerHost.getInputMethodManager()).thenReturn(
inputMethodManager);
+ try {
+ Field field = InsetsController.class.getDeclaredField("mSourceConsumers");
+ field.setAccessible(true);
+ field.set(mInsetsController, new SparseArray<InsetsSourceConsumer>());
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new RuntimeException("Unable to set mSourceConsumers", e);
+ }
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index bc0ae9f..0b1b40c 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -623,28 +623,6 @@
assertEquals(FRAME_RATE_CATEGORY_HIGH_HINT, mViewRoot.getLastPreferredFrameRateCategory());
}
- @Test
- @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
- FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
- })
- public void idleDetected() throws Throwable {
- waitForFrameRateCategoryToSettle();
- mActivityRule.runOnUiThread(() -> {
- mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
- mMovingView.setFrameContentVelocity(Float.MAX_VALUE);
- mMovingView.invalidate();
- runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_HIGH,
- mViewRoot.getLastPreferredFrameRateCategory()));
- });
- waitForAfterDraw();
-
- // Wait for idle timeout
- Thread.sleep(500);
- assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
- assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
- mViewRoot.getLastPreferredFrameRateCategory());
- }
-
private void runAfterDraw(@NonNull Runnable runnable) {
Handler handler = new Handler(Looper.getMainLooper());
mAfterDrawLatch = new CountDownLatch(1);
diff --git a/data/fonts/font_fallback_cjkvf.xml b/data/fonts/font_fallback_cjkvf.xml
index ac1b064..a4ee825 100644
--- a/data/fonts/font_fallback_cjkvf.xml
+++ b/data/fonts/font_fallback_cjkvf.xml
@@ -768,7 +768,7 @@
<font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
</family>
<family lang="zh-Hans">
- <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin"
+ <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"
supportedAxes="wght">
NotoSansCJK-Regular.ttc
<!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
@@ -780,7 +780,7 @@
</font>
</family>
<family lang="zh-Hant,zh-Bopo">
- <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin"
+ <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"
supportedAxes="wght">
NotoSansCJK-Regular.ttc
<!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
@@ -792,7 +792,7 @@
</font>
</family>
<family lang="ja">
- <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin"
+ <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"
supportedAxes="wght">
NotoSansCJK-Regular.ttc
<!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
@@ -810,7 +810,7 @@
</font>
</family>
<family lang="ko">
- <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"
+ <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"
supportedAxes="wght">
NotoSansCJK-Regular.ttc
<!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
diff --git a/data/fonts/fonts_cjkvf.xml b/data/fonts/fonts_cjkvf.xml
index 9545ae7..8cbc300 100644
--- a/data/fonts/fonts_cjkvf.xml
+++ b/data/fonts/fonts_cjkvf.xml
@@ -1409,39 +1409,39 @@
<font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
</family>
<family lang="zh-Hans">
- <font weight="100" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="100" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="100"/>
</font>
- <font weight="200" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="200" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="200"/>
</font>
- <font weight="300" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="300" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="300"/>
</font>
- <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="500" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="600" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="700" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="800" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="800" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="800"/>
</font>
- <font weight="900" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="900" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="900"/>
</font>
@@ -1450,39 +1450,39 @@
</font>
</family>
<family lang="zh-Hant,zh-Bopo">
- <font weight="100" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="100" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="100"/>
</font>
- <font weight="200" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="200" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="200"/>
</font>
- <font weight="300" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="300" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="300"/>
</font>
- <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="500" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="600" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="700" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="800" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="800" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="800"/>
</font>
- <font weight="900" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="900" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="900"/>
</font>
@@ -1491,39 +1491,39 @@
</font>
</family>
<family lang="ja">
- <font weight="100" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="100" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="100"/>
</font>
- <font weight="200" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="200" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="200"/>
</font>
- <font weight="300" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="300" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="300"/>
</font>
- <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="500" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="600" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="700" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="800" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="800" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="800"/>
</font>
- <font weight="900" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="900" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="900"/>
</font>
@@ -1542,39 +1542,39 @@
</font>
</family>
<family lang="ko">
- <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="100"/>
</font>
- <font weight="200" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="200" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="200"/>
</font>
- <font weight="300" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="300" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="300"/>
</font>
- <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="500" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="600" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="700" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="800" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="800" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="800"/>
</font>
- <font weight="900" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="900" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="900"/>
</font>
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index e8b4104..f8d3bff 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -438,9 +438,15 @@
key usage 0x0c0079 KEYBOARD_BACKLIGHT_UP FALLBACK_USAGE_MAPPING
key usage 0x0c007A KEYBOARD_BACKLIGHT_DOWN FALLBACK_USAGE_MAPPING
key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE FALLBACK_USAGE_MAPPING
+key usage 0x0c00D9 EMOJI_PICKER FALLBACK_USAGE_MAPPING
key usage 0x0c0173 MEDIA_AUDIO_TRACK FALLBACK_USAGE_MAPPING
key usage 0x0c019C PROFILE_SWITCH FALLBACK_USAGE_MAPPING
+key usage 0x0c019F SETTINGS FALLBACK_USAGE_MAPPING
key usage 0x0c01A2 ALL_APPS FALLBACK_USAGE_MAPPING
+key usage 0x0c0227 REFRESH FALLBACK_USAGE_MAPPING
+key usage 0x0c029D LANGUAGE_SWITCH FALLBACK_USAGE_MAPPING
+key usage 0x0c029F RECENT_APPS FALLBACK_USAGE_MAPPING
+key usage 0x0c02A2 ALL_APPS FALLBACK_USAGE_MAPPING
key usage 0x0d0044 STYLUS_BUTTON_PRIMARY FALLBACK_USAGE_MAPPING
key usage 0x0d005a STYLUS_BUTTON_SECONDARY FALLBACK_USAGE_MAPPING
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index e93b0bf..1fbaeea 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -256,8 +256,10 @@
static Color getContainerBackgroundColor(
@NonNull TaskFragmentContainer container, @NonNull Color defaultColor) {
final Activity activity = container.getTopNonFinishingActivity();
- if (activity == null || !activity.isResumed()) {
- // This can happen when the top activity in the container is from a different process.
+ if (activity == null) {
+ // This can happen when the activities in the container are from a different process.
+ // TODO(b/340984203) Report whether the top activity is in the same process. Use default
+ // color if not.
return defaultColor;
}
@@ -515,8 +517,11 @@
private void onStartDragging() {
mRenderer.mIsDragging = true;
mRenderer.mDragHandle.setPressed(mRenderer.mIsDragging);
+ mRenderer.updateSurface();
+
+ // Veil visibility change should be applied together with the surface boost transaction in
+ // the wct.
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- mRenderer.updateSurface(t);
mRenderer.showVeils(t);
// Callbacks must be executed on the executor to release mLock and prevent deadlocks.
@@ -532,18 +537,18 @@
@GuardedBy("mLock")
private void onDrag() {
- final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- mRenderer.updateSurface(t);
- t.apply();
+ mRenderer.updateSurface();
}
@GuardedBy("mLock")
private void onFinishDragging() {
mDividerPosition = adjustDividerPositionForSnapPoints(mDividerPosition);
mRenderer.setDividerPosition(mDividerPosition);
+ mRenderer.updateSurface();
+ // Veil visibility change should be applied together with the surface boost transaction in
+ // the wct.
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- mRenderer.updateSurface(t);
mRenderer.hideVeils(t);
// Callbacks must be executed on the executor to release mLock and prevent deadlocks.
@@ -994,6 +999,22 @@
* Updates the positions and crops of the divider surface and veil surfaces. This method
* should be called when {@link #mProperties} is changed or while dragging to update the
* position of the divider surface and the veil surfaces.
+ *
+ * This method applies the changes in a stand-alone surface transaction immediately.
+ */
+ private void updateSurface() {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ updateSurface(t);
+ t.apply();
+ }
+
+ /**
+ * Updates the positions and crops of the divider surface and veil surfaces. This method
+ * should be called when {@link #mProperties} is changed or while dragging to update the
+ * position of the divider surface and the veil surfaces.
+ *
+ * This method applies the changes in the provided surface transaction and can be synced
+ * with other changes.
*/
private void updateSurface(@NonNull SurfaceControl.Transaction t) {
final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index a23a4741..f9a6caf 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -21,6 +21,7 @@
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_PINNED;
import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
@@ -358,6 +359,13 @@
wct.addTaskFragmentOperation(fragmentToken, operation);
}
+ void setTaskFragmentPinned(@NonNull WindowContainerTransaction wct,
+ @NonNull IBinder fragmentToken, boolean pinned) {
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ OP_TYPE_SET_PINNED).setBooleanValue(pinned).build();
+ wct.addTaskFragmentOperation(fragmentToken, operation);
+ }
+
void setTaskFragmentDimOnTask(@NonNull WindowContainerTransaction wct,
@NonNull IBinder fragmentToken, boolean dimOnTask) {
final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 5b0e6b9..13c2d1f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -350,8 +350,7 @@
// Resets the isolated navigation and updates the container.
final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
final WindowContainerTransaction wct = transactionRecord.getTransaction();
- mPresenter.setTaskFragmentIsolatedNavigation(wct, containerToUnpin,
- false /* isolated */);
+ mPresenter.setTaskFragmentPinned(wct, containerToUnpin, false /* pinned */);
updateContainer(wct, containerToUnpin);
transactionRecord.apply(false /* shouldApplyIndependently */);
updateCallbackIfNecessary();
@@ -1078,8 +1077,7 @@
return true;
}
- // Skip resolving if the activity is on an isolated navigated TaskFragmentContainer.
- if (container != null && container.isIsolatedNavigationEnabled()) {
+ if (container != null && container.shouldSkipActivityResolving()) {
return true;
}
@@ -1535,8 +1533,7 @@
final TaskFragmentContainer taskFragmentContainer = getContainerWithActivity(
launchingActivity);
if (taskFragmentContainer != null
- && taskFragmentContainer.isIsolatedNavigationEnabled()) {
- // Skip resolving if started from an isolated navigated TaskFragmentContainer.
+ && taskFragmentContainer.shouldSkipActivityResolving()) {
return null;
}
if (isAssociatedWithOverlay(launchingActivity)) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 0e4fb30..2704813 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -401,18 +401,26 @@
return;
}
- setTaskFragmentIsolatedNavigation(wct, secondaryContainer, !isStacked /* isolatedNav */);
+ setTaskFragmentPinned(wct, secondaryContainer, !isStacked /* pinned */);
if (isStacked && !splitPinRule.isSticky()) {
secondaryContainer.getTaskContainer().removeSplitPinContainer();
}
}
/**
- * Sets whether to enable isolated navigation for this {@link TaskFragmentContainer}
+ * Sets whether to enable isolated navigation for this {@link TaskFragmentContainer}.
+ * <p>
+ * If a container enables isolated navigation, activities can't be launched to this container
+ * unless explicitly requested to be launched to.
+ *
+ * @see TaskFragmentContainer#isOverlayWithActivityAssociation()
*/
void setTaskFragmentIsolatedNavigation(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container,
boolean isolatedNavigationEnabled) {
+ if (!Flags.activityEmbeddingOverlayPresentationFlag() && container.isOverlay()) {
+ return;
+ }
if (container.isIsolatedNavigationEnabled() == isolatedNavigationEnabled) {
return;
}
@@ -422,6 +430,28 @@
}
/**
+ * Sets whether to pin this {@link TaskFragmentContainer}.
+ * <p>
+ * If a container is pinned, it won't be chosen as the launch target unless it's the launching
+ * container.
+ *
+ * @see TaskFragmentContainer#isAlwaysOnTopOverlay()
+ * @see TaskContainer#getSplitPinContainer()
+ */
+ void setTaskFragmentPinned(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskFragmentContainer container,
+ boolean pinned) {
+ if (!Flags.activityEmbeddingOverlayPresentationFlag() && container.isOverlay()) {
+ return;
+ }
+ if (container.isPinned() == pinned) {
+ return;
+ }
+ container.setPinned(pinned);
+ setTaskFragmentPinned(wct, container.getTaskFragmentToken(), pinned);
+ }
+
+ /**
* Resizes the task fragment if it was already registered. Skips the operation if the container
* creation has not been reported from the server yet.
*/
@@ -586,6 +616,11 @@
super.setCompanionTaskFragment(wct, primary, secondary);
}
+ /**
+ * Applies the {@code attributes} to a standalone {@code container}.
+ *
+ * @param minDimensions the minimum dimension of the container.
+ */
void applyActivityStackAttributes(
@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container,
@@ -594,16 +629,17 @@
final Rect relativeBounds = sanitizeBounds(attributes.getRelativeBounds(), minDimensions,
container);
final boolean isFillParent = relativeBounds.isEmpty();
- // Note that we only set isolated navigation for overlay container without activity
- // association. Activity will be launched to an expanded container on top of the overlay
- // if the overlay is associated with an activity. Thus, an overlay with activity association
- // will never be isolated navigated.
- final boolean isIsolatedNavigated = container.isAlwaysOnTopOverlay() && !isFillParent;
final boolean dimOnTask = !isFillParent
- && attributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK
- && Flags.fullscreenDimFlag();
+ && Flags.fullscreenDimFlag()
+ && attributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK;
final IBinder fragmentToken = container.getTaskFragmentToken();
+ if (container.isAlwaysOnTopOverlay()) {
+ setTaskFragmentPinned(wct, container, !isFillParent);
+ } else if (container.isOverlayWithActivityAssociation()) {
+ setTaskFragmentIsolatedNavigation(wct, container, !isFillParent);
+ }
+
// TODO(b/243518738): Update to resizeTaskFragment after we migrate WCT#setRelativeBounds
// and WCT#setWindowingMode to take fragmentToken.
resizeTaskFragmentIfRegistered(wct, container, relativeBounds);
@@ -612,7 +648,6 @@
updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode);
// Always use default animation for standalone ActivityStack.
updateAnimationParams(wct, fragmentToken, TaskFragmentAnimationParams.DEFAULT);
- setTaskFragmentIsolatedNavigation(wct, container, isIsolatedNavigated);
setTaskFragmentDimOnTask(wct, fragmentToken, dimOnTask);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index c952dfe..4825543 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -184,6 +184,11 @@
private boolean mIsIsolatedNavigationEnabled;
/**
+ * Whether this TaskFragment is pinned.
+ */
+ private boolean mIsPinned;
+
+ /**
* Whether to apply dimming on the parent Task that was requested last.
*/
private boolean mLastDimOnTask;
@@ -893,6 +898,34 @@
mIsIsolatedNavigationEnabled = isolatedNavigationEnabled;
}
+ /**
+ * Returns whether this container is pinned.
+ *
+ * @see android.window.TaskFragmentOperation#OP_TYPE_SET_PINNED
+ */
+ boolean isPinned() {
+ return mIsPinned;
+ }
+
+ /**
+ * Sets whether to pin this container or not.
+ *
+ * @see #isPinned()
+ */
+ void setPinned(boolean pinned) {
+ mIsPinned = pinned;
+ }
+
+ /**
+ * Indicates to skip activity resolving if the activity is from this container.
+ *
+ * @see #isIsolatedNavigationEnabled()
+ * @see #isPinned()
+ */
+ boolean shouldSkipActivityResolving() {
+ return isIsolatedNavigationEnabled() || isPinned();
+ }
+
/** Sets whether to apply dim on the parent Task. */
void setLastDimOnTask(boolean lastDimOnTask) {
mLastDimOnTask = lastDimOnTask;
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
index ad913c9..b0a45e2 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
@@ -631,15 +631,8 @@
assertEquals(defaultColor,
DividerPresenter.getContainerBackgroundColor(container, defaultColor));
- // When the top non-finishing activity is not resumed, the default color should be returned.
+ // When the top non-finishing activity is non-null, its background color should be returned.
when(container.getTopNonFinishingActivity()).thenReturn(activity);
- when(activity.isResumed()).thenReturn(false);
- assertEquals(defaultColor,
- DividerPresenter.getContainerBackgroundColor(container, defaultColor));
-
- // When the top non-finishing activity is resumed, its background color should be returned.
- when(container.getTopNonFinishingActivity()).thenReturn(activity);
- when(activity.isResumed()).thenReturn(true);
assertEquals(activityBackgroundColor,
DividerPresenter.getContainerBackgroundColor(container, defaultColor));
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 049a9e2..9ebcb759 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -188,6 +188,32 @@
}
@Test
+ public void testSetIsolatedNavigation_overlayFeatureDisabled_earlyReturn() {
+ mSetFlagRule.disableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG);
+
+ final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, "test");
+
+ mSplitPresenter.setTaskFragmentIsolatedNavigation(mTransaction, container,
+ !container.isIsolatedNavigationEnabled());
+
+ verify(mSplitPresenter, never()).setTaskFragmentIsolatedNavigation(any(),
+ any(IBinder.class), anyBoolean());
+ }
+
+ @Test
+ public void testSetPinned_overlayFeatureDisabled_earlyReturn() {
+ mSetFlagRule.disableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG);
+
+ final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, "test");
+
+ mSplitPresenter.setTaskFragmentPinned(mTransaction, container,
+ !container.isPinned());
+
+ verify(mSplitPresenter, never()).setTaskFragmentPinned(any(), any(IBinder.class),
+ anyBoolean());
+ }
+
+ @Test
public void testGetAllNonFinishingOverlayContainers() {
assertThat(mSplitController.getAllNonFinishingOverlayContainers()).isEmpty();
@@ -608,8 +634,11 @@
WINDOWING_MODE_UNDEFINED);
verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
TaskFragmentAnimationParams.DEFAULT);
- verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, false);
verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, false);
+ verify(mSplitPresenter, never()).setTaskFragmentPinned(any(),
+ any(TaskFragmentContainer.class), anyBoolean());
+ verify(mSplitPresenter, never()).setTaskFragmentIsolatedNavigation(any(),
+ any(TaskFragmentContainer.class), anyBoolean());
}
@Test
@@ -630,9 +659,9 @@
WINDOWING_MODE_MULTI_WINDOW);
verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
TaskFragmentAnimationParams.DEFAULT);
- // Set isolated navigation to false if the overlay container is associated with
- // the launching activity.
- verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, false);
+ verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, true);
+ verify(mSplitPresenter, never()).setTaskFragmentPinned(any(),
+ any(TaskFragmentContainer.class), anyBoolean());
verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, true);
}
@@ -655,10 +684,9 @@
container, WINDOWING_MODE_MULTI_WINDOW);
verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
TaskFragmentAnimationParams.DEFAULT);
- // Set isolated navigation to false if the overlay container is associated with
- // the launching activity.
- verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction,
- container, true);
+ verify(mSplitPresenter, never()).setTaskFragmentIsolatedNavigation(any(),
+ any(TaskFragmentContainer.class), anyBoolean());
+ verify(mSplitPresenter).setTaskFragmentPinned(mTransaction, container, true);
verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, true);
}
@@ -678,6 +706,8 @@
verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
TaskFragmentAnimationParams.DEFAULT);
verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, false);
+ verify(mSplitPresenter, never()).setTaskFragmentPinned(any(),
+ any(TaskFragmentContainer.class), anyBoolean());
verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, false);
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index c677484..3fbce9ec 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_PINNED;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_PRIMARY_WITH_SECONDARY;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_SECONDARY_WITH_PRIMARY;
@@ -285,6 +286,28 @@
}
@Test
+ public void testSetTaskFragmentPinned() {
+ final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+
+ // Verify the default.
+ assertFalse(container.isPinned());
+
+ mPresenter.setTaskFragmentPinned(mTransaction, container, true);
+
+ final TaskFragmentOperation expectedOperation = new TaskFragmentOperation.Builder(
+ OP_TYPE_SET_PINNED).setBooleanValue(true).build();
+ verify(mTransaction).addTaskFragmentOperation(container.getTaskFragmentToken(),
+ expectedOperation);
+ assertTrue(container.isPinned());
+
+ // No request to set the same animation params.
+ clearInvocations(mTransaction);
+ mPresenter.setTaskFragmentPinned(mTransaction, container, true);
+
+ verify(mTransaction, never()).addTaskFragmentOperation(any(), any());
+ }
+
+ @Test
public void testGetMinDimensionsForIntent() {
final Intent intent = new Intent(ApplicationProvider.getApplicationContext(),
MinimumDimensionActivity.class);
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index fe68123..8977d5c 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -71,3 +71,10 @@
description: "Hides the bubble overflow if there aren't any overflowed bubbles"
bug: "334175587"
}
+
+flag {
+ name: "enable_retrievable_bubbles"
+ namespace: "multitasking"
+ description: "Allow opening bubbles overflow UI without bubbles being visible"
+ bug: "340337839"
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
index 5af4c3b..bdd89c0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.desktopmode;
+package com.android.wm.shell.shared;
import android.annotation.NonNull;
import android.content.Context;
@@ -127,7 +127,7 @@
/**
* Return the maximum limit on the number of Tasks to show in Desktop Mode at any one time.
*/
- static int getMaxTaskLimit() {
+ public static int getMaxTaskLimit() {
return MAX_TASK_LIMIT;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index dba0a98..579a794 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -152,7 +152,8 @@
"org.chromium.arc", 0)
val isTv = AppGlobals.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK, 0)
- isPip2ExperimentEnabled = SystemProperties.getBoolean("wm_shell.pip2", false) ||
+ isPip2ExperimentEnabled = SystemProperties.getBoolean(
+ "persist.wm_shell.pip2", false) ||
(Flags.enablePip2Implementation() && !isArc && !isTv)
}
return isPip2ExperimentEnabled as Boolean
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index e729c7d..991fbaf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -72,7 +72,6 @@
import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.compatui.CompatUIShellCommandHandler;
import com.android.wm.shell.desktopmode.DesktopMode;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
@@ -88,6 +87,7 @@
import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.shared.ShellTransitions;
import com.android.wm.shell.shared.annotations.ShellAnimationThread;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index a1910c5..fb0a1ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -57,7 +57,6 @@
import com.android.wm.shell.dagger.pip.PipModule;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
@@ -77,6 +76,7 @@
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.shared.annotations.ShellAnimationThread;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index 9038aaa..0b7a3e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -39,6 +39,7 @@
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 6bbc8fe..7e0234e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -446,11 +446,6 @@
* Called when the desktop changes the number of visible freeform tasks.
*/
fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {}
-
- /**
- * Called when the desktop stashed status changes.
- */
- fun onStashedChanged(displayId: Int, stashed: Boolean) {}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
new file mode 100644
index 0000000..aa11a7d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.util.Log
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.InstanceIdSequence
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.wm.shell.dagger.WMSingleton
+import javax.inject.Inject
+
+/**
+ * Log Aster UIEvents for desktop windowing mode.
+ */
+@WMSingleton
+class DesktopModeUiEventLogger @Inject constructor(
+ private val mUiEventLogger: UiEventLogger,
+ private val mInstanceIdSequence: InstanceIdSequence
+) {
+ /**
+ * Logs an event for a CUI, on a particular package.
+ *
+ * @param uid The user id associated with the package the user is interacting with
+ * @param packageName The name of the package the user is interacting with
+ * @param event The event type to generate
+ */
+ fun log(uid: Int, packageName: String, event: DesktopUiEventEnum) {
+ if (packageName.isEmpty() || uid < 0) {
+ Log.d(TAG, "Skip logging since package name is empty or bad uid")
+ return
+ }
+ mUiEventLogger.log(event, uid, packageName)
+ }
+
+ /**
+ * Retrieves a new instance id for a new interaction.
+ */
+ fun getNewInstanceId(): InstanceId = mInstanceIdSequence.newInstanceId()
+
+ /**
+ * Logs an event as part of a particular CUI, on a particular package.
+ *
+ * @param instanceId The id identifying an interaction, potentially taking place across multiple
+ * surfaces. There should be a new id generated for each distinct CUI.
+ * @param uid The user id associated with the package the user is interacting with
+ * @param packageName The name of the package the user is interacting with
+ * @param event The event type to generate
+ */
+ fun logWithInstanceId(
+ instanceId: InstanceId,
+ uid: Int,
+ packageName: String,
+ event: DesktopUiEventEnum
+ ) {
+ if (packageName.isEmpty() || uid < 0) {
+ Log.d(TAG, "Skip logging since package name is empty or bad uid")
+ return
+ }
+ mUiEventLogger.logWithInstanceId(event, uid, packageName, instanceId)
+ }
+
+ companion object {
+ /**
+ * Enums for logging desktop windowing mode UiEvents.
+ */
+ enum class DesktopUiEventEnum(private val mId: Int) : UiEventLogger.UiEventEnum {
+
+ @UiEvent(doc = "Resize the window in desktop windowing mode by dragging the edge")
+ DESKTOP_WINDOW_EDGE_DRAG_RESIZE(1721),
+
+ @UiEvent(doc = "Resize the window in desktop windowing mode by dragging the corner")
+ DESKTOP_WINDOW_CORNER_DRAG_RESIZE(1722),
+
+ @UiEvent(doc = "Tap on the window header maximize button in desktop windowing mode")
+ DESKTOP_WINDOW_MAXIMIZE_BUTTON_TAP(1723),
+
+ @UiEvent(doc = "Double tap on window header to maximize it in desktop windowing mode")
+ DESKTOP_WINDOW_HEADER_DOUBLE_TAP_TO_MAXIMIZE(1724);
+
+ override fun getId(): Int = mId
+ }
+
+ private const val TAG = "DesktopModeUiEventLogger"
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 091685e..2dc4573 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -72,6 +72,7 @@
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.shared.annotations.ExternalThread
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.splitscreen.SplitScreenController
@@ -1374,16 +1375,6 @@
l -> l.onTasksVisibilityChanged(displayId, visibleTasksCount)
}
}
-
- override fun onStashedChanged(displayId: Int, stashed: Boolean) {
- KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "IDesktopModeImpl: onStashedChanged display=%d stashed=%b",
- displayId,
- stashed
- )
- remoteListener.call { l -> l.onStashedChanged(displayId, stashed) }
- }
}
init {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 3404d37..0f88384 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -25,6 +25,7 @@
import androidx.annotation.VisibleForTesting
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionObserver
import com.android.wm.shell.util.KtProtoLog
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index 451e09c..dae75f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -23,6 +23,7 @@
import android.window.TransitionInfo
import com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.util.KtProtoLog
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
index 8ed87f2..8ebdfdc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
@@ -25,6 +25,6 @@
/** Desktop tasks visibility has changed. Visible if at least 1 task is visible. */
oneway void onTasksVisibilityChanged(int displayId, int visibleTasksCount);
- /** Desktop task stashed status has changed. */
+ /** @deprecated this is no longer supported. */
oneway void onStashedChanged(int displayId, boolean stashed);
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index a414a55..e0e2e706 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -27,9 +27,9 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index 9eaf7e4..c79eef7e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -22,6 +22,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss;
import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_OCCLUDING;
@@ -83,6 +84,7 @@
* @see KeyguardTransitions
*/
private IRemoteTransition mExitTransition = null;
+ private IRemoteTransition mAppearTransition = null;
private IRemoteTransition mOccludeTransition = null;
private IRemoteTransition mOccludeByDreamTransition = null;
private IRemoteTransition mUnoccludeTransition = null;
@@ -170,26 +172,28 @@
// Choose a transition applicable for the changes and keyguard state.
if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) {
- return startAnimation(mExitTransition,
- "going-away",
+ return startAnimation(mExitTransition, "going-away",
transition, info, startTransaction, finishTransaction, finishCallback);
}
+ if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_APPEARING) != 0) {
+ return startAnimation(mAppearTransition, "appearing",
+ transition, info, startTransaction, finishTransaction, finishCallback);
+ }
+
+
// Occlude/unocclude animations are only played if the keyguard is locked.
if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_OCCLUDING) != 0) {
if (hasOpeningDream(info)) {
- return startAnimation(mOccludeByDreamTransition,
- "occlude-by-dream",
+ return startAnimation(mOccludeByDreamTransition, "occlude-by-dream",
transition, info, startTransaction, finishTransaction, finishCallback);
} else {
- return startAnimation(mOccludeTransition,
- "occlude",
+ return startAnimation(mOccludeTransition, "occlude",
transition, info, startTransaction, finishTransaction, finishCallback);
}
} else if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) {
- return startAnimation(mUnoccludeTransition,
- "unocclude",
+ return startAnimation(mUnoccludeTransition, "unocclude",
transition, info, startTransaction, finishTransaction, finishCallback);
}
}
@@ -359,11 +363,13 @@
@Override
public void register(
IRemoteTransition exitTransition,
+ IRemoteTransition appearTransition,
IRemoteTransition occludeTransition,
IRemoteTransition occludeByDreamTransition,
IRemoteTransition unoccludeTransition) {
mMainExecutor.execute(() -> {
mExitTransition = exitTransition;
+ mAppearTransition = appearTransition;
mOccludeTransition = occludeTransition;
mOccludeByDreamTransition = occludeByDreamTransition;
mUnoccludeTransition = unoccludeTransition;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java
index 4215b2c..b7245b9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java
@@ -35,6 +35,7 @@
*/
default void register(
@NonNull IRemoteTransition unlockTransition,
+ @NonNull IRemoteTransition appearTransition,
@NonNull IRemoteTransition occludeTransition,
@NonNull IRemoteTransition occludeByDreamTransition,
@NonNull IRemoteTransition unoccludeTransition) {}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index fdde3ee..2082756 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -824,12 +824,10 @@
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull TaskInfo taskInfo) {
startTransaction.apply();
- if (info.getChanges().isEmpty()) {
+ final TransitionInfo.Change pipChange = findCurrentPipTaskChange(info);
+ if (pipChange == null) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "removePipImmediately is called with empty changes");
- } else {
- finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
- mPipDisplayLayoutState.getDisplayBounds());
+ "removePipImmediately is called without pip change");
}
mPipOrganizer.onExitPipFinished(taskInfo);
finishCallback.onTransitionFinished(null);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index a8611d9..c53e7fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -50,9 +50,9 @@
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.shared.annotations.ExternalThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ShellCommandHandler;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
index e419462..e07e1b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -45,6 +45,7 @@
import com.android.internal.R;
+import java.io.Closeable;
import java.util.function.LongConsumer;
/**
@@ -100,7 +101,7 @@
* Drawable pre-drawing the scaled icon in a separate thread to increase the speed of the
* final drawing.
*/
- private static class ImmobileIconDrawable extends Drawable {
+ private static class ImmobileIconDrawable extends Drawable implements Closeable {
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG
| Paint.FILTER_BITMAP_FLAG);
private final Matrix mMatrix = new Matrix();
@@ -154,6 +155,16 @@
public int getOpacity() {
return 1;
}
+
+ @Override
+ public void close() {
+ synchronized (mPaint) {
+ if (mIconBitmap != null) {
+ mIconBitmap.recycle();
+ mIconBitmap = null;
+ }
+ }
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
index 1897560..6adbe4f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
@@ -49,8 +49,12 @@
public PerfettoTransitionTracer() {
Producer.init(InitArguments.DEFAULTS);
- mDataSource.register(
- new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT));
+ DataSourceParams params =
+ new DataSourceParams.Builder()
+ .setBufferExhaustedPolicy(
+ PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+ .build();
+ mDataSource.register(params);
}
/**
@@ -214,8 +218,6 @@
}
os.end(mappingsToken);
-
- ctx.flush();
});
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index dfdb58a..9afb057 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -84,12 +84,12 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController;
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 4c347ad..4d4dc3c 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
@@ -66,8 +66,8 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.viewholder.DesktopModeAppControlsWindowDecorationViewHolder;
import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder;
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 de6c035..5418254 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
@@ -52,7 +52,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement;
import java.util.ArrayList;
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
index 4dd14f4..f69a90c 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
@@ -91,6 +91,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
index 5c86a38..b76d065 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
@@ -91,6 +91,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
index aa70c09..041978c 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
@@ -91,6 +91,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
index c7c804f..a66dfb4 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
@@ -91,6 +91,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
index 214bdfa..85715db 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
@@ -91,6 +91,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index 60a7dcd..2a2483d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -41,6 +41,7 @@
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.TransitionInfoBuilder
import com.android.wm.shell.transition.Transitions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt
new file mode 100644
index 0000000..285e5b6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.InstanceIdSequence
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.Companion.DesktopUiEventEnum.DESKTOP_WINDOW_EDGE_DRAG_RESIZE
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Test class for [DesktopModeUiEventLogger]
+ *
+ * Usage: atest WMShellUnitTests:DesktopModeUiEventLoggerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopModeUiEventLoggerTest : ShellTestCase() {
+ private lateinit var uiEventLoggerFake: UiEventLoggerFake
+ private lateinit var logger: DesktopModeUiEventLogger
+ private val instanceIdSequence = InstanceIdSequence(10)
+
+
+ @Before
+ fun setUp() {
+ uiEventLoggerFake = UiEventLoggerFake()
+ logger = DesktopModeUiEventLogger(uiEventLoggerFake, instanceIdSequence)
+ }
+
+ @Test
+ fun log_invalidUid_eventNotLogged() {
+ logger.log(-1, PACKAGE_NAME, DESKTOP_WINDOW_EDGE_DRAG_RESIZE)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0)
+ }
+
+ @Test
+ fun log_emptyPackageName_eventNotLogged() {
+ logger.log(UID, "", DESKTOP_WINDOW_EDGE_DRAG_RESIZE)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0)
+ }
+
+ @Test
+ fun log_eventLogged() {
+ val event =
+ DESKTOP_WINDOW_EDGE_DRAG_RESIZE
+ logger.log(UID, PACKAGE_NAME, event)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(event.id)
+ assertThat(uiEventLoggerFake[0].instanceId).isNull()
+ assertThat(uiEventLoggerFake[0].uid).isEqualTo(UID)
+ assertThat(uiEventLoggerFake[0].packageName).isEqualTo(PACKAGE_NAME)
+ }
+
+ @Test
+ fun getNewInstanceId() {
+ val first = logger.getNewInstanceId()
+ assertThat(first).isNotEqualTo(logger.getNewInstanceId())
+ }
+
+ @Test
+ fun logWithInstanceId_invalidUid_eventNotLogged() {
+ logger.logWithInstanceId(INSTANCE_ID, -1, PACKAGE_NAME, DESKTOP_WINDOW_EDGE_DRAG_RESIZE)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0)
+ }
+
+ @Test
+ fun logWithInstanceId_emptyPackageName_eventNotLogged() {
+ logger.logWithInstanceId(INSTANCE_ID, UID, "", DESKTOP_WINDOW_EDGE_DRAG_RESIZE)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0)
+ }
+
+ @Test
+ fun logWithInstanceId_eventLogged() {
+ val event =
+ DESKTOP_WINDOW_EDGE_DRAG_RESIZE
+ logger.logWithInstanceId(INSTANCE_ID, UID, PACKAGE_NAME, event)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(event.id)
+ assertThat(uiEventLoggerFake[0].instanceId).isEqualTo(INSTANCE_ID)
+ assertThat(uiEventLoggerFake[0].uid).isEqualTo(UID)
+ assertThat(uiEventLoggerFake[0].packageName).isEqualTo(PACKAGE_NAME)
+ }
+
+
+ companion object {
+ private val INSTANCE_ID = InstanceId.fakeInstanceId(0)
+ private const val UID = 10
+ private const val PACKAGE_NAME = "com.foo"
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 7e55628..f67da55 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -79,6 +79,7 @@
import com.android.wm.shell.draganddrop.DragAndDropController
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index 539d5b8..3c488ca 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -32,6 +32,7 @@
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.transition.TransitionInfoBuilder
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.util.StubTransaction
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index 665077b..cd68c69 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -35,8 +35,8 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 240324b..884cb6e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -67,8 +67,8 @@
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 7d19f3c..aa2cee7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -60,8 +60,8 @@
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
-import com.android.wm.shell.desktopmode.DesktopModeStatus
import com.android.wm.shell.desktopmode.DesktopTasksController
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.sysui.KeyguardChangeListener
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
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 4eb44d7..8b8cd11 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
@@ -75,7 +75,7 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.tests.R;
import org.junit.Before;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index d25d5dc..ff09084 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -785,6 +785,9 @@
} else {
onPrinterUnavailable(printerInfo);
}
+ if (mPrinterRegistry != null) {
+ mPrinterRegistry.setTrackedPrinter(mCurrentPrinter.getId());
+ }
mDestinationSpinnerAdapter.ensurePrinterInVisibleAdapterPosition(printerInfo);
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index e7823df..45667f5 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -41,7 +41,7 @@
defaultConfig {
minSdk = 21
- targetSdk = 34
+ targetSdk = 35
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index da1ee77..e867a8f 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -21,6 +21,7 @@
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
@@ -30,7 +31,6 @@
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
-import androidx.core.view.WindowCompat
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavGraphBuilder
@@ -82,7 +82,7 @@
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.Theme_SpaLib)
super.onCreate(savedInstanceState)
- WindowCompat.setDecorFitsSystemWindows(window, false)
+ enableEdgeToEdge()
spaEnvironment.logger.message(TAG, "onCreate", category = LogCategory.FRAMEWORK)
setContent {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
index 36cd136..9a344c3 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
@@ -35,6 +35,7 @@
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalContentColor
@@ -42,11 +43,11 @@
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
-import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.TopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.NonRestartableComposable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableFloatStateOf
@@ -79,7 +80,12 @@
import kotlin.math.max
import kotlin.math.roundToInt
-@OptIn(ExperimentalMaterial3Api::class)
+private val windowInsets: WindowInsets
+ @Composable
+ @NonRestartableComposable
+ get() = WindowInsets.safeDrawing
+ .only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top)
+
@Composable
internal fun CustomizedTopAppBar(
title: @Composable () -> Unit,
@@ -91,7 +97,7 @@
titleTextStyle = MaterialTheme.typography.titleMedium,
navigationIcon = navigationIcon,
actions = actions,
- windowInsets = TopAppBarDefaults.windowInsets,
+ windowInsets = windowInsets,
colors = topAppBarColors(),
)
}
@@ -118,7 +124,7 @@
navigationIcon = navigationIcon,
actions = actions,
colors = topAppBarColors(),
- windowInsets = TopAppBarDefaults.windowInsets,
+ windowInsets = windowInsets,
pinnedHeight = ContainerHeight,
scrollBehavior = scrollBehavior,
)
@@ -336,7 +342,7 @@
Modifier.draggable(
orientation = Orientation.Vertical,
state = rememberDraggableState { delta ->
- scrollBehavior.state.heightOffset = scrollBehavior.state.heightOffset + delta
+ scrollBehavior.state.heightOffset += delta
},
onDragStopped = { velocity ->
settleAppBar(
@@ -411,6 +417,7 @@
* (leading icon), a title (header), and action icons (trailing icons). Note that the navigation and
* the actions are optional.
*
+ * @param modifier a [Modifier]
* @param heightPx the total height this layout is capped to
* @param navigationIconContentColor the content color that will be applied via a
* [LocalContentColor] when composing the navigation icon
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
index a49b358..4a7937a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
@@ -22,9 +22,11 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -92,6 +94,7 @@
)
},
containerColor = MaterialTheme.colorScheme.settingsBackground,
+ contentWindowInsets = WindowInsets.safeDrawing,
) { paddingValues ->
Box(
Modifier
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
index af7a146..4cf741e 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
@@ -23,7 +23,9 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
@@ -57,6 +59,7 @@
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = { SettingsTopAppBar(title, scrollBehavior, actions) },
containerColor = MaterialTheme.colorScheme.settingsBackground,
+ contentWindowInsets = WindowInsets.safeDrawing,
) { paddingValues ->
Box(Modifier.padding(paddingValues.horizontalValues())) {
content(paddingValues.verticalValues())
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
index fc40930..4726dad 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
@@ -21,7 +21,9 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
@@ -55,7 +57,10 @@
content: @Composable () -> Unit,
) {
ActivityTitle(title)
- Scaffold(containerColor = MaterialTheme.colorScheme.settingsBackground) { innerPadding ->
+ Scaffold(
+ containerColor = MaterialTheme.colorScheme.settingsBackground,
+ contentWindowInsets = WindowInsets.safeDrawing,
+ ) { innerPadding ->
BoxWithConstraints(
Modifier
.padding(innerPadding)
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
index 57bde56..c365142 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
@@ -128,7 +128,7 @@
final long now = mClock.millis();
final UserManager um = mContext.getSystemService(UserManager.class);
final List<UserHandle> profiles = um.getUserProfiles();
- ArrayMap<UserHandle, Boolean> shouldIncludeAppsByUsers = new ArrayMap<>();
+ ArrayMap<UserHandle, Boolean> shouldHideAppsByUsers = new ArrayMap<>();
for (int i = 0; i < appOpsCount; ++i) {
AppOpsManager.PackageOps ops = appOps.get(i);
@@ -136,13 +136,13 @@
int uid = ops.getUid();
UserHandle user = UserHandle.getUserHandleForUid(uid);
- if (!shouldIncludeAppsByUsers.containsKey(user)) {
- shouldIncludeAppsByUsers.put(user, shouldHideUser(um, user));
+ if (!shouldHideAppsByUsers.containsKey(user)) {
+ shouldHideAppsByUsers.put(user, shouldHideUser(um, user));
}
// Don't show apps belonging to background users except for profiles that shouldn't
// be shown in quiet mode.
- if (!profiles.contains(user) || !shouldIncludeAppsByUsers.get(user)) {
+ if (!profiles.contains(user) || shouldHideAppsByUsers.get(user)) {
continue;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt
deleted file mode 100644
index 1f037c0..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2024 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.settingslib.volume.data.repository
-
-import android.media.MediaMetadata
-import android.media.session.MediaController
-import android.media.session.MediaSession
-import android.media.session.PlaybackState
-import android.os.Bundle
-import android.os.Handler
-import kotlinx.coroutines.channels.ProducerScope
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.launch
-
-/** [MediaController.Callback] flow representation. */
-fun MediaController.stateChanges(handler: Handler): Flow<MediaControllerChange> {
- return callbackFlow {
- val callback = MediaControllerCallbackProducer(this)
- registerCallback(callback, handler)
- awaitClose { unregisterCallback(callback) }
- }
-}
-
-/** Models particular change event received by [MediaController.Callback]. */
-sealed interface MediaControllerChange {
-
- data object SessionDestroyed : MediaControllerChange
-
- data class SessionEvent(val event: String, val extras: Bundle?) : MediaControllerChange
-
- data class PlaybackStateChanged(val state: PlaybackState?) : MediaControllerChange
-
- data class MetadataChanged(val metadata: MediaMetadata?) : MediaControllerChange
-
- data class QueueChanged(val queue: MutableList<MediaSession.QueueItem>?) :
- MediaControllerChange
-
- data class QueueTitleChanged(val title: CharSequence?) : MediaControllerChange
-
- data class ExtrasChanged(val extras: Bundle?) : MediaControllerChange
-
- data class AudioInfoChanged(val info: MediaController.PlaybackInfo?) : MediaControllerChange
-}
-
-private class MediaControllerCallbackProducer(
- private val producingScope: ProducerScope<MediaControllerChange>
-) : MediaController.Callback() {
-
- override fun onSessionDestroyed() {
- send(MediaControllerChange.SessionDestroyed)
- }
-
- override fun onSessionEvent(event: String, extras: Bundle?) {
- send(MediaControllerChange.SessionEvent(event, extras))
- }
-
- override fun onPlaybackStateChanged(state: PlaybackState?) {
- send(MediaControllerChange.PlaybackStateChanged(state))
- }
-
- override fun onMetadataChanged(metadata: MediaMetadata?) {
- send(MediaControllerChange.MetadataChanged(metadata))
- }
-
- override fun onQueueChanged(queue: MutableList<MediaSession.QueueItem>?) {
- send(MediaControllerChange.QueueChanged(queue))
- }
-
- override fun onQueueTitleChanged(title: CharSequence?) {
- send(MediaControllerChange.QueueTitleChanged(title))
- }
-
- override fun onExtrasChanged(extras: Bundle?) {
- send(MediaControllerChange.ExtrasChanged(extras))
- }
-
- override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
- send(MediaControllerChange.AudioInfoChanged(info))
- }
-
- private fun send(change: MediaControllerChange) {
- producingScope.launch { producingScope.send(change) }
- }
-}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index be3f410..888e395 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -274,6 +274,9 @@
Settings.Secure.SCREEN_RESOLUTION_MODE,
Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS,
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
- Settings.Secure.CHARGE_OPTIMIZATION_MODE
+ Settings.Secure.CHARGE_OPTIMIZATION_MODE,
+ Settings.Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS,
+ Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
+ Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index b1feede..b992ddc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -18,6 +18,7 @@
import static android.provider.settings.validators.SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.ANY_LONG_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.AUTOFILL_SERVICE_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
@@ -433,5 +434,8 @@
VALIDATORS.put(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
new InclusiveIntegerRangeValidator(0, 10));
VALIDATORS.put(Secure.CHARGE_OPTIMIZATION_MODE, new InclusiveIntegerRangeValidator(0, 10));
+ VALIDATORS.put(Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS, ANY_LONG_VALIDATOR);
+ VALIDATORS.put(Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS, ANY_LONG_VALIDATOR);
+ VALIDATORS.put(Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, NONE_NEGATIVE_LONG_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
index 677c81a..255b1ad 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
@@ -239,6 +239,18 @@
}
};
+ static final Validator ANY_LONG_VALIDATOR = value -> {
+ if (value == null) {
+ return true;
+ }
+ try {
+ Long.parseLong(value);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ };
+
static final Validator CREDENTIAL_SERVICE_VALIDATOR = new Validator() {
@Override
public boolean validate(String value) {
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index c04ec4f..d2ca112 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -78,11 +78,23 @@
visibility: ["//visibility:private"],
}
+filegroup {
+ name: "SystemUI-tests-broken-robofiles-run",
+ srcs: [
+ "tests/src/**/systemui/util/LifecycleFragmentTest.java",
+ "tests/src/**/systemui/util/TestableAlertDialogTest.kt",
+ "tests/src/**/systemui/util/kotlin/PairwiseFlowTest",
+ "tests/src/**/systemui/util/sensors/AsyncManagerTest.java",
+ "tests/src/**/systemui/util/sensors/ThresholdSensorImplTest.java",
+ "tests/src/**/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java",
+ ],
+}
+
// We are running robolectric tests in the tests directory as well as
// multivalent tests. If you add a test, and it doesn't run in robolectric,
// it should be added to this exclusion list. go/multivalent-tests
filegroup {
- name: "SystemUI-tests-broken-robofiles",
+ name: "SystemUI-tests-broken-robofiles-compile",
srcs: [
"tests/src/**/*DeviceOnlyTest.java",
"tests/src/**/*DeviceOnlyTest.kt",
@@ -703,7 +715,8 @@
":SystemUI-tests-robofiles",
],
exclude_srcs: [
- ":SystemUI-tests-broken-robofiles",
+ ":SystemUI-tests-broken-robofiles-compile",
+ ":SystemUI-tests-broken-robofiles-run",
],
static_libs: [
"RoboTestLibraries",
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 204429b..aff86e8a1 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -405,6 +405,16 @@
}
flag {
+ name: "fix_image_wallpaper_crash_surface_already_released"
+ namespace: "systemui"
+ description: "Make sure ImageWallpaper doesn't return from OnSurfaceDestroyed until any drawing is finished"
+ bug: "337287154"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "activity_transition_use_largest_window"
namespace: "systemui"
description: "Target largest opening window during activity transitions."
@@ -862,6 +872,9 @@
namespace: "systemui"
description: "Enforce BaseUserRestriction for DISALLOW_CONFIG_BRIGHTNESS."
bug: "329205638"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -920,3 +933,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "validate_keyboard_shortcut_helper_icon_uri"
+ namespace: "systemui"
+ description: "Adds a check that the caller can access the content URI of an icon in the shortcut helper."
+ bug: "331180422"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/flag_check.py b/packages/SystemUI/flag_check.py
index bac3553..95a25c5 100755
--- a/packages/SystemUI/flag_check.py
+++ b/packages/SystemUI/flag_check.py
@@ -12,19 +12,20 @@
%s
The Flag: stanza is regex matched and should describe whether your change is behind a flag or flags.
-
-As a CL author, you'll have a consistent place to describe the risk of the proposed change by explicitly calling out the name of the
-flag in addition to its state (ENABLED|DISABLED|DEVELOPMENT|STAGING|TEAMFOOD|TRUNKFOOD|NEXTFOOD).
+As a CL author, you'll have a consistent place to describe the risk of the proposed change by explicitly calling out the name of the flag.
+For legacy flags use EXEMPT with your flag name.
Some examples below:
-Flag: NONE
-Flag: NA
-Flag: LEGACY ENABLE_ONE_SEARCH DISABLED
-Flag: ACONFIG com.android.launcher3.enable_twoline_allapps DEVELOPMENT
-Flag: ACONFIG com.android.launcher3.enable_twoline_allapps TRUNKFOOD
+Flag: NONE Repohook Update
+Flag: TEST_ONLY
+Flag: EXEMPT resource only update
+Flag: EXEMPT bugfix
+Flag: EXEMPT refactor
+Flag: com.android.launcher3.enable_twoline_allapps
+Flag: com.google.android.apps.nexuslauncher.zero_state_web_data_loader
-Check the git history for more examples. It's a regex matched field.
+Check the git history for more examples. It's a regex matched field. See go/android-flag-directive for more details on various formats.
"""
def main():
@@ -63,28 +64,31 @@
return
field = 'Flag'
- none = '(NONE|NA|N\/A)' # NONE|NA|N/A
+ none = 'NONE'
+ testOnly = 'TEST_ONLY'
+ docsOnly = 'DOCS_ONLY'
+ exempt = 'EXEMPT'
+ justification = '<justification>'
- typeExpression = '\s*(LEGACY|ACONFIG)' # [type:LEGACY|ACONFIG]
-
- # legacyFlagName contains only uppercase alphabets with '_' - Ex: ENABLE_ONE_SEARCH
- # Aconfig Flag name format = "packageName"."flagName"
+ # Aconfig Flag name format = <packageName>.<flagName>
# package name - Contains only lowercase alphabets + digits + '.' - Ex: com.android.launcher3
- # For now alphabets, digits, "_", "." characters are allowed in flag name and not adding stricter format check.
+ # For now alphabets, digits, "_", "." characters are allowed in flag name.
+ # Checks if there is "one dot" between packageName and flagName and not adding stricter format check
#common_typos_disable
- flagName = '([a-zA-z0-9_.])+'
+ flagName = '([a-zA-Z0-9.]+)([.]+)([a-zA-Z0-9_.]+)'
- #[state:ENABLED|DISABLED|DEVELOPMENT|TEAM*(TEAMFOOD)|STAGING|TRUNK*(TRUNK_STAGING, TRUNK_FOOD)|NEXT*(NEXTFOOD)]
- stateExpression = '\s*(ENABLED|DISABLED|DEVELOPMENT|TEAM[a-zA-z]*|STAGING|TRUNK[a-zA-z]*|NEXT[a-zA-z]*)'
+ # None and Exempt needs justification
+ exemptRegex = fr'{exempt}\s*[a-zA-Z]+'
+ noneRegex = fr'{none}\s*[a-zA-Z]+'
#common_typos_enable
- readableRegexMsg = '\n\tFlag: (NONE|NA)\n\tFlag: LEGACY|ACONFIG FlagName|packageName.flagName ENABLED|DISABLED|DEVELOPMENT|TEAMFOOD|STAGING|TRUNKFOOD|NEXTFOOD'
+ readableRegexMsg = '\n\tFlag: '+none+' '+justification+'\n\tFlag: <packageName>.<flagName>\n\tFlag: ' +exempt+' '+justification+'\n\tFlag: '+testOnly+'\n\tFlag: '+docsOnly
flagRegex = fr'^{field}: .*$'
check_flag = re.compile(flagRegex) #Flag:
# Ignore case for flag name format.
- flagNameRegex = fr'(?i)^{field}:\s*({none}|{typeExpression}\s*{flagName}\s*{stateExpression})\s*'
+ flagNameRegex = fr'(?i)^{field}:\s*({noneRegex}|{flagName}|{testOnly}|{docsOnly}|{exemptRegex})\s*'
check_flagName = re.compile(flagNameRegex) #Flag: <flag name format>
flagError = False
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
index 11a4241..27bffd0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
@@ -18,10 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.view.GestureDetector;
import android.view.MotionEvent;
@@ -30,6 +28,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -37,7 +36,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -51,89 +49,66 @@
CentralSurfaces mCentralSurfaces;
@Mock
+ ShadeViewController mShadeViewController;
+
+ @Mock
TouchHandler.TouchSession mTouchSession;
ShadeTouchHandler mTouchHandler;
- @Captor
- ArgumentCaptor<GestureDetector.OnGestureListener> mGestureListenerCaptor;
- @Captor
- ArgumentCaptor<InputChannelCompat.InputEventListener> mInputListenerCaptor;
-
private static final int TOUCH_HEIGHT = 20;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
-
- mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces), TOUCH_HEIGHT);
- }
-
- // Verifies that a swipe down in the gesture region is captured by the shade touch handler.
- @Test
- public void testSwipeDown_captured() {
- final boolean captured = swipe(Direction.DOWN);
-
- assertThat(captured).isTrue();
- }
-
- // Verifies that a swipe in the upward direction is not catpured.
- @Test
- public void testSwipeUp_notCaptured() {
- final boolean captured = swipe(Direction.UP);
-
- // Motion events not captured as the swipe is going in the wrong direction.
- assertThat(captured).isFalse();
- }
-
- // Verifies that a swipe down forwards captured touches to the shade window for handling.
- @Test
- public void testSwipeDown_sentToShadeWindow() {
- swipe(Direction.DOWN);
-
- // Both motion events are sent for the shade window to process.
- verify(mCentralSurfaces, times(2)).handleExternalShadeWindowTouch(any());
- }
-
- // Verifies that a swipe down is not forwarded to the shade window.
- @Test
- public void testSwipeUp_touchesNotSent() {
- swipe(Direction.UP);
-
- // Motion events are not sent for the shade window to process as the swipe is going in the
- // wrong direction.
- verify(mCentralSurfaces, never()).handleExternalShadeWindowTouch(any());
+ mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces), mShadeViewController,
+ TOUCH_HEIGHT);
}
/**
- * Simulates a swipe in the given direction and returns true if the touch was intercepted by the
- * touch handler's gesture listener.
- * <p>
- * Swipe down starts from a Y coordinate of 0 and goes downward. Swipe up starts from the edge
- * of the gesture region, {@link #TOUCH_HEIGHT}, and goes upward to 0.
+ * Verify that touches aren't handled when the bouncer is showing.
*/
- private boolean swipe(Direction direction) {
- Mockito.clearInvocations(mTouchSession);
+ @Test
+ public void testInactiveOnBouncer() {
+ when(mCentralSurfaces.isBouncerShowing()).thenReturn(true);
mTouchHandler.onSessionStart(mTouchSession);
-
- verify(mTouchSession).registerGestureListener(mGestureListenerCaptor.capture());
- verify(mTouchSession).registerInputListener(mInputListenerCaptor.capture());
-
- final float startY = direction == Direction.UP ? TOUCH_HEIGHT : 0;
- final float endY = direction == Direction.UP ? 0 : TOUCH_HEIGHT;
-
- // Send touches to the input and gesture listener.
- final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, startY, 0);
- final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, endY, 0);
- mInputListenerCaptor.getValue().onInputEvent(event1);
- mInputListenerCaptor.getValue().onInputEvent(event2);
- final boolean captured = mGestureListenerCaptor.getValue().onScroll(event1, event2, 0,
- startY - endY);
-
- return captured;
+ verify(mTouchSession).pop();
}
- private enum Direction {
- DOWN, UP,
+ /**
+ * Make sure {@link ShadeTouchHandler}
+ */
+ @Test
+ public void testTouchPilferingOnScroll() {
+ final MotionEvent motionEvent1 = Mockito.mock(MotionEvent.class);
+ final MotionEvent motionEvent2 = Mockito.mock(MotionEvent.class);
+
+ final ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerArgumentCaptor =
+ ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+
+ mTouchHandler.onSessionStart(mTouchSession);
+ verify(mTouchSession).registerGestureListener(gestureListenerArgumentCaptor.capture());
+
+ assertThat(gestureListenerArgumentCaptor.getValue()
+ .onScroll(motionEvent1, motionEvent2, 1, 1))
+ .isTrue();
}
+
+ /**
+ * Ensure touches are propagated to the {@link ShadeViewController}.
+ */
+ @Test
+ public void testEventPropagation() {
+ final MotionEvent motionEvent = Mockito.mock(MotionEvent.class);
+
+ final ArgumentCaptor<InputChannelCompat.InputEventListener>
+ inputEventListenerArgumentCaptor =
+ ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
+
+ mTouchHandler.onSessionStart(mTouchSession);
+ verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture());
+ inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent);
+ verify(mShadeViewController).handleExternalTouch(motionEvent);
+ }
+
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index b4b812d..0ab0959 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -265,7 +265,7 @@
with(kosmos) {
testScope.runTest {
// Device is dreaming and on communal.
- fakeKeyguardRepository.setDreaming(true)
+ updateDreaming(true)
communalInteractor.changeScene(CommunalScenes.Communal)
val scene by collectLastValue(communalInteractor.desiredScene)
@@ -282,7 +282,7 @@
with(kosmos) {
testScope.runTest {
// Device is not dreaming and on communal.
- fakeKeyguardRepository.setDreaming(false)
+ updateDreaming(false)
communalInteractor.changeScene(CommunalScenes.Communal)
// Scene stays as Communal
@@ -297,7 +297,7 @@
with(kosmos) {
testScope.runTest {
// Device is dreaming and on communal.
- fakeKeyguardRepository.setDreaming(true)
+ updateDreaming(true)
communalInteractor.changeScene(CommunalScenes.Communal)
val scene by collectLastValue(communalInteractor.desiredScene)
@@ -309,7 +309,7 @@
// Dream stops, timeout is cancelled and device stays on hub, because the regular
// screen timeout will take effect at this point.
- fakeKeyguardRepository.setDreaming(false)
+ updateDreaming(false)
advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
}
@@ -320,7 +320,7 @@
with(kosmos) {
testScope.runTest {
// Device is on communal, but not dreaming.
- fakeKeyguardRepository.setDreaming(false)
+ updateDreaming(false)
communalInteractor.changeScene(CommunalScenes.Communal)
val scene by collectLastValue(communalInteractor.desiredScene)
@@ -328,7 +328,7 @@
// Wait a bit, but not long enough to timeout, then start dreaming.
advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- fakeKeyguardRepository.setDreaming(true)
+ updateDreaming(true)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
// Device times out after one screen timeout interval, dream doesn't reset timeout.
@@ -338,11 +338,31 @@
}
@Test
+ fun hubTimeout_dreamAfterInitialTimeout_goesToBlank() =
+ with(kosmos) {
+ testScope.runTest {
+ // Device is on communal.
+ communalInteractor.changeScene(CommunalScenes.Communal)
+
+ // Device stays on the hub after the timeout since we're not dreaming.
+ advanceTimeBy(SCREEN_TIMEOUT.milliseconds * 2)
+ val scene by collectLastValue(communalInteractor.desiredScene)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
+
+ // Start dreaming.
+ updateDreaming(true)
+
+ // Hub times out immediately.
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
+ }
+ }
+
+ @Test
fun hubTimeout_userActivityTriggered_resetsTimeout() =
with(kosmos) {
testScope.runTest {
// Device is dreaming and on communal.
- fakeKeyguardRepository.setDreaming(true)
+ updateDreaming(true)
communalInteractor.changeScene(CommunalScenes.Communal)
val scene by collectLastValue(communalInteractor.desiredScene)
@@ -371,7 +391,7 @@
fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT * 2)
// Device is dreaming and on communal.
- fakeKeyguardRepository.setDreaming(true)
+ updateDreaming(true)
communalInteractor.changeScene(CommunalScenes.Communal)
val scene by collectLastValue(communalInteractor.desiredScene)
@@ -395,6 +415,12 @@
runCurrent()
}
+ private fun TestScope.updateDreaming(dreaming: Boolean) =
+ with(kosmos) {
+ fakeKeyguardRepository.setDreaming(dreaming)
+ runCurrent()
+ }
+
private suspend fun TestScope.enableCommunal() =
with(kosmos) {
setCommunalAvailable(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
index e3c6dee..29fbee0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
@@ -108,7 +108,7 @@
mTouchHandler.onSessionStart(mTouchSession);
verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture());
inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent);
- verify(mCentralSurfaces).handleExternalShadeWindowTouch(motionEvent);
+ verify(mCentralSurfaces).handleDreamTouch(motionEvent);
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index bf0939c..99cccb2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -19,9 +19,13 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
@@ -29,36 +33,74 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertThrows
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
-@android.platform.test.annotations.EnabledOnRavenwood
class KeyguardTransitionInteractorTest : SysuiTestCase() {
val kosmos = testKosmos()
val underTest = kosmos.keyguardTransitionInteractor
val repository = kosmos.fakeKeyguardTransitionRepository
val testScope = kosmos.testScope
+ private val sceneTransitions =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(Scenes.Lockscreen)
+ )
+
+ private val lsToGone =
+ ObservableTransitionState.Transition(
+ Scenes.Lockscreen,
+ Scenes.Gone,
+ flowOf(Scenes.Lockscreen),
+ flowOf(0f),
+ false,
+ flowOf(false)
+ )
+
+ private val goneToLs =
+ ObservableTransitionState.Transition(
+ Scenes.Gone,
+ Scenes.Lockscreen,
+ flowOf(Scenes.Lockscreen),
+ flowOf(0f),
+ false,
+ flowOf(false)
+ )
+
+ @Before
+ fun setUp() {
+ kosmos.sceneContainerRepository.setTransitionState(sceneTransitions)
+ }
+
@Test
fun transitionCollectorsReceivesOnlyAppropriateEvents() =
testScope.runTest {
- val lockscreenToAodSteps by collectValues(underTest.transition(LOCKSCREEN, AOD))
- val aodToLockscreenSteps by collectValues(underTest.transition(AOD, LOCKSCREEN))
+ val lockscreenToAodSteps by
+ collectValues(underTest.transition(Edge.create(LOCKSCREEN, AOD)))
+ val aodToLockscreenSteps by
+ collectValues(underTest.transition(Edge.create(AOD, LOCKSCREEN)))
val steps = mutableListOf<TransitionStep>()
steps.add(TransitionStep(AOD, GONE, 0f, STARTED))
@@ -482,6 +524,7 @@
}
@Test
+ @DisableSceneContainer
fun isInTransitionToState() =
testScope.runTest {
val results by collectValues(underTest.isInTransitionToState(GONE))
@@ -586,7 +629,7 @@
)
sendSteps(
- TransitionStep(DOZING, GONE, 0f, STARTED),
+ TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED),
)
assertThat(results)
@@ -598,7 +641,7 @@
)
sendSteps(
- TransitionStep(DOZING, GONE, 0f, RUNNING),
+ TransitionStep(DOZING, LOCKSCREEN, 0f, RUNNING),
)
assertThat(results)
@@ -610,7 +653,7 @@
)
sendSteps(
- TransitionStep(DOZING, GONE, 0f, FINISHED),
+ TransitionStep(DOZING, LOCKSCREEN, 0f, FINISHED),
)
assertThat(results)
@@ -623,9 +666,9 @@
)
sendSteps(
- TransitionStep(GONE, DOZING, 0f, STARTED),
- TransitionStep(GONE, DOZING, 0f, RUNNING),
- TransitionStep(GONE, DOZING, 1f, FINISHED),
+ TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED),
+ TransitionStep(LOCKSCREEN, DOZING, 0f, RUNNING),
+ TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED),
)
assertThat(results)
@@ -638,8 +681,8 @@
)
sendSteps(
- TransitionStep(DOZING, GONE, 0f, STARTED),
- TransitionStep(DOZING, GONE, 0f, RUNNING),
+ TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED),
+ TransitionStep(DOZING, LOCKSCREEN, 0f, RUNNING),
)
assertThat(results)
@@ -1404,6 +1447,143 @@
)
}
+ @Test
+ @DisableSceneContainer
+ fun transition_no_conversion_with_flag_off() =
+ testScope.runTest {
+ val currentStates by
+ collectValues(underTest.transition(Edge.create(PRIMARY_BOUNCER, GONE)))
+
+ val sendStep1 = TransitionStep(PRIMARY_BOUNCER, GONE, 0f, STARTED)
+ sendSteps(sendStep1)
+
+ assertEquals(listOf(sendStep1), currentStates)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_conversion_with_flag_on() =
+ testScope.runTest {
+ val currentStates by
+ collectValues(underTest.transition(Edge.create(PRIMARY_BOUNCER, GONE)))
+
+ val sendStep1 = TransitionStep(PRIMARY_BOUNCER, GONE, 0f, STARTED)
+ sendSteps(sendStep1)
+
+ assertEquals(listOf<TransitionStep>(), currentStates)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_conversion_emits_values_with_sceneContainer_in_correct_state() =
+ testScope.runTest {
+ val currentStates by collectValues(underTest.transition(Edge.create(LOCKSCREEN, GONE)))
+ val currentStatesConverted by
+ collectValues(underTest.transition(Edge.create(LOCKSCREEN, UNDEFINED)))
+
+ sceneTransitions.value = lsToGone
+ val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+ val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
+ sendSteps(sendStep1, sendStep2, sendStep3)
+
+ assertEquals(listOf(sendStep1, sendStep2), currentStates)
+ assertEquals(listOf(sendStep1, sendStep2), currentStatesConverted)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_conversion_emits_nothing_with_sceneContainer_in_wrong_state() =
+ testScope.runTest {
+ val currentStates by collectValues(underTest.transition(Edge.create(LOCKSCREEN, GONE)))
+
+ sceneTransitions.value = goneToLs
+ val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+ val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
+ sendSteps(sendStep1, sendStep2, sendStep3)
+
+ assertEquals(listOf<TransitionStep>(), currentStates)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_conversion_emits_values_when_edge_within_lockscreen_scene() =
+ testScope.runTest {
+ val currentStates by
+ collectValues(underTest.transition(Edge.create(LOCKSCREEN, DOZING)))
+
+ sceneTransitions.value = goneToLs
+ val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED)
+ val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED)
+ val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
+ sendSteps(sendStep1, sendStep2, sendStep3)
+
+ assertEquals(listOf(sendStep1, sendStep2), currentStates)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_conversion_emits_values_with_null_edge_within_lockscreen_scene() =
+ testScope.runTest {
+ val currentStates by collectValues(underTest.transition(Edge.create(LOCKSCREEN, null)))
+ val currentStatesReversed by
+ collectValues(underTest.transition(Edge.create(null, LOCKSCREEN)))
+
+ sceneTransitions.value = goneToLs
+ val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED)
+ val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED)
+ val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
+ val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
+ sendSteps(sendStep1, sendStep2, sendStep3, sendStep4)
+
+ assertEquals(listOf(sendStep1, sendStep2, sendStep3), currentStates)
+ assertEquals(listOf(sendStep4), currentStatesReversed)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_conversion_emits_values_with_null_edge_out_of_lockscreen_scene() =
+ testScope.runTest {
+ val currentStates by collectValues(underTest.transition(Edge.create(null, UNDEFINED)))
+ val currentStatesMapped by collectValues(underTest.transition(Edge.create(null, GONE)))
+
+ sceneTransitions.value = lsToGone
+ val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+ val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED)
+ val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
+ sendSteps(sendStep1, sendStep2, sendStep3, sendStep4)
+
+ assertEquals(listOf(sendStep1, sendStep2), currentStates)
+ assertEquals(listOf(sendStep1, sendStep2), currentStatesMapped)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_conversion_does_not_emit_with_null_edge_with_wrong_stl_state() =
+ testScope.runTest {
+ val currentStatesMapped by collectValues(underTest.transition(Edge.create(null, GONE)))
+
+ sceneTransitions.value = goneToLs
+ val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+ val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED)
+ val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
+ sendSteps(sendStep1, sendStep2, sendStep3, sendStep4)
+
+ assertEquals(listOf<TransitionStep>(), currentStatesMapped)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_null_edges_throw() =
+ testScope.runTest {
+ assertThrows(IllegalStateException::class.java) {
+ underTest.transition(Edge.create(null, null))
+ }
+ }
+
private suspend fun sendSteps(vararg steps: TransitionStep) {
steps.forEach {
repository.sendTransitionStep(it)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
index 0ac7ff5..a0fed6b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
@@ -23,11 +23,13 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.time.Duration.Companion.milliseconds
@@ -50,11 +52,14 @@
@Before
fun setUp() {
underTest =
- animationFlow.setup(
- duration = 1000.milliseconds,
- from = GONE,
- to = DREAMING,
- )
+ animationFlow
+ .setup(
+ duration = 1000.milliseconds,
+ edge = Edge.create(from = Scenes.Gone, to = DREAMING),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GONE, to = DREAMING),
+ )
}
@Test(expected = IllegalArgumentException::class)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
index d632936..7a9bd92 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
@@ -87,6 +87,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun scrimAlpha_runDimissFromKeyguard_shadeExpanded() =
testScope.runTest {
val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
@@ -137,6 +138,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun scrimBehindAlpha_leaveShadeOpen() =
testScope.runTest {
val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
@@ -161,6 +163,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun showAllNotifications_isTrue_whenLeaveShadeOpen() =
testScope.runTest {
val showAllNotifications by
@@ -177,6 +180,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun showAllNotifications_isFalse_whenLeaveShadeIsNotOpen() =
testScope.runTest {
val showAllNotifications by
@@ -193,6 +197,7 @@
}
@Test
+ @BrokenWithSceneContainer(330311871)
fun scrimBehindAlpha_doNotLeaveShadeOpen() =
testScope.runTest {
val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 838b2a7..20ffa33 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -30,6 +30,8 @@
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.parameterizeSceneContainerFlag
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -37,7 +39,9 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.sceneContainerRepository
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.dozeParameters
@@ -49,6 +53,8 @@
import com.android.systemui.util.ui.value
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -75,6 +81,11 @@
private val viewState = ViewStateAccessor()
+ private val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(Scenes.Lockscreen)
+ )
+
companion object {
@JvmStatic
@Parameters(name = "{0}")
@@ -96,6 +107,7 @@
AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
)
}
+ kosmos.sceneContainerRepository.setTransitionState(transitionState)
}
@Test
@@ -309,6 +321,32 @@
}
@Test
+ @EnableSceneContainer
+ fun alpha_transitionToHub_isZero_scene_container() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha(viewState))
+
+ transitionState.value =
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Communal,
+ emptyFlow(),
+ emptyFlow(),
+ false,
+ emptyFlow()
+ )
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ testScope,
+ )
+
+ assertThat(alpha).isEqualTo(0f)
+ }
+
+ @Test
+ @DisableSceneContainer
fun alpha_transitionToHub_isZero() =
testScope.runTest {
val alpha by collectLastValue(underTest.alpha(viewState))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index 58c6817..1c1fcc4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -18,8 +18,10 @@
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -30,11 +32,16 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.testKosmos
import com.google.common.collect.Range
import com.google.common.truth.Truth
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -58,6 +65,11 @@
private val keyguardRepository = kosmos.fakeKeyguardRepository
private lateinit var underTest: LockscreenToPrimaryBouncerTransitionViewModel
+ private val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(Scenes.Lockscreen)
+ )
+
companion object {
@JvmStatic
@Parameters(name = "{0}")
@@ -76,6 +88,7 @@
}
@Test
+ @BrokenWithSceneContainer(330311871)
fun deviceEntryParentViewAlpha_shadeExpanded() =
testScope.runTest {
val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
@@ -107,6 +120,17 @@
shadeExpanded(false)
runCurrent()
+ kosmos.sceneContainerRepository.setTransitionState(transitionState)
+ transitionState.value =
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Bouncer,
+ emptyFlow(),
+ emptyFlow(),
+ false,
+ emptyFlow()
+ )
+ runCurrent()
// fade out
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
runCurrent()
@@ -132,7 +156,9 @@
): TransitionStep {
return TransitionStep(
from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.PRIMARY_BOUNCER,
+ to =
+ if (SceneContainerFlag.isEnabled) KeyguardState.UNDEFINED
+ else KeyguardState.PRIMARY_BOUNCER,
value = value,
transitionState = state,
ownerName = "LockscreenToPrimaryBouncerTransitionViewModelTest"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
index bd3b77a..365a7c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
@@ -45,16 +45,16 @@
import com.android.systemui.statusbar.notificationLockscreenUserManager
import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -121,7 +121,7 @@
whenever(kosmos.activityIntentHelper.wouldPendingShowOverLockscreen(any(), any()))
.thenReturn(true)
- val clickIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+ val clickIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
val expandable = mock<Expandable>()
underTest.startClickIntent(expandable, clickIntent)
@@ -133,7 +133,7 @@
fun startClickIntent_hideOverLockscreen() {
whenever(keyguardStateController.isShowing).thenReturn(false)
- val clickIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+ val clickIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
val expandable = mock<Expandable>()
val activityController = mock<ActivityTransitionAnimator.Controller>()
whenever(expandable.activityTransitionController(any())).thenReturn(activityController)
@@ -150,7 +150,7 @@
whenever(kosmos.activityIntentHelper.wouldPendingShowOverLockscreen(any(), any()))
.thenReturn(true)
- val deviceIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+ val deviceIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
underTest.startDeviceIntent(deviceIntent)
@@ -163,7 +163,7 @@
whenever(kosmos.activityIntentHelper.wouldPendingShowOverLockscreen(any(), any()))
.thenReturn(true)
- val deviceIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(false) }
+ val deviceIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(false) }
underTest.startDeviceIntent(deviceIntent)
@@ -174,7 +174,7 @@
fun startDeviceIntent_hideOverLockscreen() {
whenever(keyguardStateController.isShowing).thenReturn(false)
- val deviceIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+ val deviceIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
underTest.startDeviceIntent(deviceIntent)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index da17366..82e2bb7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
@@ -107,18 +108,25 @@
val testScope = kosmos.testScope
val configurationRepository
get() = kosmos.fakeConfigurationRepository
+
val keyguardRepository
get() = kosmos.fakeKeyguardRepository
+
val keyguardInteractor
get() = kosmos.keyguardInteractor
+
val keyguardRootViewModel
get() = kosmos.keyguardRootViewModel
+
val keyguardTransitionRepository
get() = kosmos.fakeKeyguardTransitionRepository
+
val shadeTestUtil
get() = kosmos.shadeTestUtil
+
val sharedNotificationContainerInteractor
get() = kosmos.sharedNotificationContainerInteractor
+
val largeScreenHeaderHelper
get() = kosmos.mockLargeScreenHeaderHelper
@@ -814,6 +822,7 @@
}
@Test
+ @BrokenWithSceneContainer(330311871)
fun alphaDoesNotUpdateWhileGoneTransitionIsRunning() =
testScope.runTest {
val viewState = ViewStateAccessor()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt
index 64c9429..46df0c2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt
@@ -16,17 +16,16 @@
package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
-import android.os.Handler
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.android.systemui.volume.localMediaController
import com.android.systemui.volume.mediaControllerRepository
+import com.android.systemui.volume.mediaDeviceSessionInteractor
import com.android.systemui.volume.mediaOutputInteractor
import com.android.systemui.volume.panel.shared.model.filterData
import com.android.systemui.volume.remoteMediaController
@@ -55,12 +54,7 @@
listOf(localMediaController, remoteMediaController)
)
- underTest =
- MediaDeviceSessionInteractor(
- testScope.testScheduler,
- Handler(TestableLooper.get(kosmos.testCase).looper),
- mediaControllerRepository,
- )
+ underTest = mediaDeviceSessionInteractor
}
}
diff --git a/packages/SystemUI/res/layout/screenshot_shelf.xml b/packages/SystemUI/res/layout/screenshot_shelf.xml
index 2cb4b02..49d3a8e 100644
--- a/packages/SystemUI/res/layout/screenshot_shelf.xml
+++ b/packages/SystemUI/res/layout/screenshot_shelf.xml
@@ -135,11 +135,12 @@
android:id="@+id/screenshot_scrollable_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:clipToOutline="true"
android:scaleType="matrix"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@id/screenshot_preview"
app:layout_constraintTop_toTopOf="@id/screenshot_preview"
- android:elevation="7dp"/>
+ android:elevation="3dp"/>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
@@ -170,6 +171,13 @@
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
+ android:id="@+id/screenshot_scrolling_scrim"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ android:clickable="true"
+ android:importantForAccessibility="no"/>
+ <ImageView
android:id="@+id/screenshot_flash"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index f33acf2..3f3bb0b 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -42,6 +42,7 @@
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
@@ -544,10 +545,10 @@
internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
return scope.launch {
merge(
- keyguardTransitionInteractor.transition(AOD, LOCKSCREEN).map { step ->
- step.copy(value = 1f - step.value)
+ keyguardTransitionInteractor.transition(Edge.create(AOD, LOCKSCREEN)).map {
+ it.copy(value = 1f - it.value)
},
- keyguardTransitionInteractor.transition(LOCKSCREEN, AOD),
+ keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, AOD)),
).filter {
it.transitionState != TransitionState.FINISHED
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 905a98c..42838ae 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -326,7 +326,8 @@
}
if (KeyguardWmStateRefactor.isEnabled()) {
- mKeyguardTransitionInteractor.startDismissKeyguardTransition();
+ mKeyguardTransitionInteractor.startDismissKeyguardTransition(
+ "KeyguardSecurityContainerController#finish");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
index 9c7fc9d..9ef9938 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
@@ -23,8 +23,7 @@
import android.view.GestureDetector;
import android.view.MotionEvent;
-import androidx.annotation.NonNull;
-
+import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.util.Optional;
@@ -38,34 +37,29 @@
*/
public class ShadeTouchHandler implements TouchHandler {
private final Optional<CentralSurfaces> mSurfaces;
+ private final ShadeViewController mShadeViewController;
private final int mInitiationHeight;
- /**
- * Tracks whether or not we are capturing a given touch. Will be null before and after a touch.
- */
- private Boolean mCapture;
-
@Inject
ShadeTouchHandler(Optional<CentralSurfaces> centralSurfaces,
+ ShadeViewController shadeViewController,
@Named(NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT) int initiationHeight) {
mSurfaces = centralSurfaces;
+ mShadeViewController = shadeViewController;
mInitiationHeight = initiationHeight;
}
@Override
public void onSessionStart(TouchSession session) {
- if (mSurfaces.isEmpty()) {
+ if (mSurfaces.map(CentralSurfaces::isBouncerShowing).orElse(false)) {
session.pop();
return;
}
- session.registerCallback(() -> mCapture = null);
-
session.registerInputListener(ev -> {
+ mShadeViewController.handleExternalTouch((MotionEvent) ev);
+
if (ev instanceof MotionEvent) {
- if (mCapture != null && mCapture) {
- mSurfaces.get().handleExternalShadeWindowTouch((MotionEvent) ev);
- }
if (((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
session.pop();
}
@@ -74,25 +68,15 @@
session.registerGestureListener(new GestureDetector.SimpleOnGestureListener() {
@Override
- public boolean onScroll(MotionEvent e1, @NonNull MotionEvent e2, float distanceX,
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
- if (mCapture == null) {
- // Only capture swipes that are going downwards.
- mCapture = Math.abs(distanceY) > Math.abs(distanceX) && distanceY < 0;
- if (mCapture) {
- // Send the initial touches over, as the input listener has already
- // processed these touches.
- mSurfaces.get().handleExternalShadeWindowTouch(e1);
- mSurfaces.get().handleExternalShadeWindowTouch(e2);
- }
- }
- return mCapture;
+ return true;
}
@Override
- public boolean onFling(MotionEvent e1, @NonNull MotionEvent e2, float velocityX,
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
- return mCapture;
+ return true;
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 9816896..298b87d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -32,11 +32,18 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState
@@ -131,6 +138,7 @@
override fun onUnlockedChanged() {
updatePauseAuth()
}
+
override fun onLaunchTransitionFadingAwayChanged() {
launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway
updatePauseAuth()
@@ -211,7 +219,10 @@
suspend fun listenForPrimaryBouncerToAodTransitions(scope: CoroutineScope): Job {
return scope.launch {
transitionInteractor
- .transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.AOD)
+ .transition(
+ edge = Edge.create(Scenes.Bouncer, AOD),
+ edgeWithoutSceneContainer = Edge.create(PRIMARY_BOUNCER, AOD)
+ )
.collect { transitionStep ->
view.onDozeAmountChanged(
transitionStep.value,
@@ -225,8 +236,7 @@
@VisibleForTesting
suspend fun listenForDreamingToAodTransitions(scope: CoroutineScope): Job {
return scope.launch {
- transitionInteractor.transition(KeyguardState.DREAMING, KeyguardState.AOD).collect {
- transitionStep ->
+ transitionInteractor.transition(Edge.create(DREAMING, AOD)).collect { transitionStep ->
view.onDozeAmountChanged(
transitionStep.value,
transitionStep.value,
@@ -239,23 +249,21 @@
@VisibleForTesting
suspend fun listenForAlternateBouncerToAodTransitions(scope: CoroutineScope): Job {
return scope.launch {
- transitionInteractor
- .transition(KeyguardState.ALTERNATE_BOUNCER, KeyguardState.AOD)
- .collect { transitionStep ->
- view.onDozeAmountChanged(
- transitionStep.value,
- transitionStep.value,
- UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
- )
- }
+ transitionInteractor.transition(Edge.create(ALTERNATE_BOUNCER, AOD)).collect {
+ transitionStep ->
+ view.onDozeAmountChanged(
+ transitionStep.value,
+ transitionStep.value,
+ UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
+ )
+ }
}
}
@VisibleForTesting
suspend fun listenForAodToOccludedTransitions(scope: CoroutineScope): Job {
return scope.launch {
- transitionInteractor.transition(KeyguardState.AOD, KeyguardState.OCCLUDED).collect {
- transitionStep ->
+ transitionInteractor.transition(Edge.create(AOD, OCCLUDED)).collect { transitionStep ->
view.onDozeAmountChanged(
1f - transitionStep.value,
1f - transitionStep.value,
@@ -268,8 +276,7 @@
@VisibleForTesting
suspend fun listenForOccludedToAodTransition(scope: CoroutineScope): Job {
return scope.launch {
- transitionInteractor.transition(KeyguardState.OCCLUDED, KeyguardState.AOD).collect {
- transitionStep ->
+ transitionInteractor.transition(Edge.create(OCCLUDED, AOD)).collect { transitionStep ->
view.onDozeAmountChanged(
transitionStep.value,
transitionStep.value,
@@ -282,14 +289,18 @@
@VisibleForTesting
suspend fun listenForGoneToAodTransition(scope: CoroutineScope): Job {
return scope.launch {
- transitionInteractor.transition(KeyguardState.GONE, KeyguardState.AOD).collect {
- transitionStep ->
- view.onDozeAmountChanged(
- transitionStep.value,
- transitionStep.value,
- ANIMATE_APPEAR_ON_SCREEN_OFF,
+ transitionInteractor
+ .transition(
+ edge = Edge.create(Scenes.Gone, AOD),
+ edgeWithoutSceneContainer = Edge.create(GONE, AOD)
)
- }
+ .collect { transitionStep ->
+ view.onDozeAmountChanged(
+ transitionStep.value,
+ transitionStep.value,
+ ANIMATE_APPEAR_ON_SCREEN_OFF,
+ )
+ }
}
}
@@ -298,13 +309,10 @@
return scope.launch {
transitionInteractor.dozeAmountTransition.collect { transitionStep ->
if (
- transitionStep.from == KeyguardState.AOD &&
+ transitionStep.from == AOD &&
transitionStep.transitionState == TransitionState.CANCELED
) {
- if (
- transitionInteractor.startedKeyguardTransitionStep.first().to !=
- KeyguardState.AOD
- ) {
+ if (transitionInteractor.startedKeyguardTransitionStep.first().to != AOD) {
// If the next started transition isn't transitioning back to AOD, force
// doze amount to be 0f (as if the transition to the lockscreen completed).
view.onDozeAmountChanged(
@@ -557,6 +565,7 @@
private fun updateScaleFactor() {
udfpsController.mOverlayParams?.scaleFactor?.let { view.setScaleFactor(it) }
}
+
companion object {
const val TAG = "UdfpsKeyguardViewController"
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index 971ab11..6f20a8d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -42,6 +42,7 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
@@ -73,6 +74,10 @@
) : CoreStartable {
private var screenTimeout: Int = DEFAULT_SCREEN_TIMEOUT
+ private var timeoutJob: Job? = null
+
+ private var isDreaming: Boolean = false
+
override fun start() {
// Handle automatically switching based on keyguard state.
keyguardTransitionInteractor.startedKeyguardTransitionStep
@@ -112,31 +117,35 @@
}
.launchIn(bgScope)
- // Handle timing out back to the dream.
+ // The hub mode timeout should start as soon as the user enters hub mode. At the end of the
+ // timer, if the device is dreaming, hub mode should closed and reveal the dream. If the
+ // dream is not running, nothing will happen. However if the dream starts again underneath
+ // hub mode after the initial timeout expires, such as if the device is docked or the dream
+ // app is updated by the Play store, a new timeout should be started.
bgScope.launch {
combine(
communalInteractor.desiredScene,
// Emit a value on start so the combine starts.
communalInteractor.userActivity.emitOnStart()
) { scene, _ ->
- // Time out should run whenever we're dreaming and the hub is open, even if not
- // docked.
+ // Only timeout if we're on the hub is open.
scene == CommunalScenes.Communal
}
- // mapLatest cancels the previous action block when new values arrive, so any
- // already running timeout gets cancelled when conditions change or user interaction
- // is detected.
- .mapLatest { shouldTimeout ->
- if (!shouldTimeout) {
- return@mapLatest false
+ .collectLatest { shouldTimeout ->
+ cancelHubTimeout()
+ if (shouldTimeout) {
+ startHubTimeout()
}
-
- delay(screenTimeout.milliseconds)
- true
}
- .sample(keyguardInteractor.isDreaming, ::Pair)
- .collect { (shouldTimeout, isDreaming) ->
- if (isDreaming && shouldTimeout) {
+ }
+ bgScope.launch {
+ keyguardInteractor.isDreaming
+ .sample(communalInteractor.desiredScene, ::Pair)
+ .collectLatest { (isDreaming, scene) ->
+ this@CommunalSceneStartable.isDreaming = isDreaming
+ if (scene == CommunalScenes.Communal && isDreaming && timeoutJob == null) {
+ // If dreaming starts after timeout has expired, ex. if dream restarts under
+ // the hub, just close the hub immediately.
communalInteractor.changeScene(CommunalScenes.Blank)
}
}
@@ -151,6 +160,24 @@
}
}
+ private fun cancelHubTimeout() {
+ timeoutJob?.cancel()
+ timeoutJob = null
+ }
+
+ private fun startHubTimeout() {
+ if (timeoutJob == null) {
+ timeoutJob =
+ bgScope.launch {
+ delay(screenTimeout.milliseconds)
+ if (isDreaming) {
+ communalInteractor.changeScene(CommunalScenes.Blank)
+ }
+ timeoutJob = null
+ }
+ }
+ }
+
private suspend fun determineSceneAfterTransition(
lastStartedTransition: TransitionStep,
): SceneKey? {
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index 30a56a2..813fccf 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -48,6 +48,7 @@
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions
@@ -302,7 +303,7 @@
private fun listenForSchedulingWatchdog() {
keyguardTransitionInteractor
- .transition(to = KeyguardState.GONE)
+ .transition(Edge.create(to = KeyguardState.GONE))
.filter { it.transitionState == TransitionState.FINISHED }
.onEach {
// We deliberately want to run this in background because scheduleWatchdog does
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index 6c6683a..669cd94 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -38,6 +38,7 @@
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.DevicePosture
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
@@ -126,9 +127,9 @@
.launchIn(applicationScope)
merge(
- keyguardTransitionInteractor.transition(AOD, LOCKSCREEN),
- keyguardTransitionInteractor.transition(OFF, LOCKSCREEN),
- keyguardTransitionInteractor.transition(DOZING, LOCKSCREEN),
+ keyguardTransitionInteractor.transition(Edge.create(AOD, LOCKSCREEN)),
+ keyguardTransitionInteractor.transition(Edge.create(OFF, LOCKSCREEN)),
+ keyguardTransitionInteractor.transition(Edge.create(DOZING, LOCKSCREEN)),
)
.filter { it.transitionState == TransitionState.STARTED }
.sample(powerInteractor.detailedWakefulness)
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
index fff0c58..1c047dd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
@@ -98,7 +98,7 @@
// Notification shade window has its own logic to be visible if the hub is open, no need to
// do anything here other than send touch events over.
session.registerInputListener(ev -> {
- surfaces.handleExternalShadeWindowTouch((MotionEvent) ev);
+ surfaces.handleDreamTouch((MotionEvent) ev);
if (ev != null && ((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
var unused = session.pop();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
index 221f790..c5b3c53 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
@@ -23,6 +23,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel
@@ -97,7 +98,7 @@
.distinctUntilChanged()
val transitionEnded =
- keyguardTransitionInteractor.transition(from = DREAMING).filter { step ->
+ keyguardTransitionInteractor.transition(Edge.create(from = DREAMING)).filter { step ->
step.transitionState == TransitionState.FINISHED ||
step.transitionState == TransitionState.CANCELED
}
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
index 9876fe4..f04cbb8 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
@@ -477,7 +477,7 @@
}
private inline fun PrintWriter.wrapSection(entry: DumpsysEntry, block: () -> Unit) {
- Trace.beginSection(entry.name)
+ Trace.beginSection(entry.name.take(Trace.MAX_SECTION_NAME_LEN))
preamble(entry)
val dumpTime = measureTimeMillis(block)
footer(entry, dumpTime)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt
index d3f7e24..44f1c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt
@@ -17,19 +17,43 @@
package com.android.systemui.keyboard.shortcut.domain.interactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperRepository
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
+import com.android.systemui.model.SysUiState
+import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.shared.system.QuickStepContract
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
@SysUISingleton
class ShortcutHelperInteractor
@Inject
-constructor(private val repository: ShortcutHelperRepository) {
+constructor(
+ private val displayTracker: DisplayTracker,
+ @Background private val backgroundScope: CoroutineScope,
+ private val sysUiState: SysUiState,
+ private val repository: ShortcutHelperRepository
+) {
val state: Flow<ShortcutHelperState> = repository.state
- fun onUserLeave() {
+ fun onViewClosed() {
repository.hide()
+ setSysUiStateFlagEnabled(false)
+ }
+
+ fun onViewOpened() {
+ setSysUiStateFlagEnabled(true)
+ }
+
+ private fun setSysUiStateFlagEnabled(enabled: Boolean) {
+ backgroundScope.launch {
+ sysUiState
+ .setFlag(QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING, enabled)
+ .commitUpdate(displayTracker.defaultDisplayId)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
index 934f9ee..ef4156d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
@@ -63,12 +63,13 @@
setUpSheetDismissListener()
setUpDismissOnTouchOutside()
observeFinishRequired()
+ viewModel.onViewOpened()
}
override fun onDestroy() {
super.onDestroy()
if (isFinishing) {
- viewModel.onUserLeave()
+ viewModel.onViewClosed()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
index 7e48c65..c623f5c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
@@ -38,7 +38,11 @@
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
- fun onUserLeave() {
- interactor.onUserLeave()
+ fun onViewClosed() {
+ interactor.onViewClosed()
+ }
+
+ fun onViewOpened() {
+ interactor.onViewOpened()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2cda728..81c2d92 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1076,6 +1076,33 @@
}
};
+ /**
+ * For now, the keyguard-appearing animation is a no-op, because we assume that this is
+ * happening while the screen is already off or turning off.
+ *
+ * TODO(b/278086361): create an animation for keyguard appearing over a non-showWhenLocked
+ * activity.
+ */
+ private final IRemoteAnimationRunner.Stub mAppearAnimationRunner =
+ new IRemoteAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to finish transition", e);
+ }
+ }
+
+ @Override
+ public void onAnimationCancelled() {
+ }
+ };
+
private final IRemoteAnimationRunner mOccludeAnimationRunner =
new OccludeActivityLaunchRemoteAnimationRunner(mOccludeAnimationController);
@@ -1164,7 +1191,7 @@
finishedCallback.onAnimationFinished();
mOccludeByDreamAnimator = null;
} catch (RemoteException e) {
- e.printStackTrace();
+ Log.e(TAG, "Failed to finish transition", e);
}
}
});
@@ -1279,7 +1306,7 @@
mInteractionJankMonitor.end(CUJ_LOCKSCREEN_OCCLUSION);
} catch (RemoteException e) {
- e.printStackTrace();
+ Log.e(TAG, "Failed to finish transition", e);
}
}
});
@@ -1545,6 +1572,7 @@
mKeyguardTransitions.register(
KeyguardService.wrap(this, getExitAnimationRunner()),
+ KeyguardService.wrap(this, getAppearAnimationRunner()),
KeyguardService.wrap(this, getOccludeAnimationRunner()),
KeyguardService.wrap(this, getOccludeByDreamAnimationRunner()),
KeyguardService.wrap(this, getUnoccludeAnimationRunner()));
@@ -2123,6 +2151,10 @@
return validatingRemoteAnimationRunner(mExitAnimationRunner);
}
+ public IRemoteAnimationRunner getAppearAnimationRunner() {
+ return validatingRemoteAnimationRunner(mAppearAnimationRunner);
+ }
+
public IRemoteAnimationRunner getOccludeAnimationRunner() {
if (KeyguardWmStateRefactor.isEnabled()) {
return validatingRemoteAnimationRunner(mWmOcclusionManager.getOccludeAnimationRunner());
@@ -3356,7 +3388,7 @@
}
} catch (RemoteException e) {
mSurfaceBehindRemoteAnimationRequested = false;
- e.printStackTrace();
+ Log.e(TAG, "Failed to report keyguardGoingAway", e);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
index a65a882..6e364d1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
@@ -29,6 +29,7 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -84,7 +85,7 @@
applicationScope.launch(bgDispatcher) {
// We drop 1 to avoid triggering on initial collect().
- keyguardTransitionInteractor.transition(to = GONE).collect { transition ->
+ keyguardTransitionInteractor.transition(Edge.create(to = GONE)).collect { transition ->
if (transition.transitionState == TransitionState.FINISHED) {
onKeyguardGone()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 00f5002..1b342ed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -23,6 +23,7 @@
import android.view.WindowManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier
import com.android.systemui.statusbar.policy.KeyguardStateController
import java.util.concurrent.Executor
@@ -40,6 +41,7 @@
private val activityTaskManagerService: IActivityTaskManager,
private val keyguardStateController: KeyguardStateController,
private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
) {
/**
@@ -141,6 +143,14 @@
finishedCallback: IRemoteAnimationFinishedCallback
) {
if (apps.isNotEmpty()) {
+ // Ensure that we've started a dismiss keyguard transition. WindowManager can start the
+ // going away animation on its own, if an activity launches and then requests dismissing
+ // the keyguard. In this case, this is the first and only signal we'll receive to start
+ // a transition to GONE.
+ keyguardTransitionInteractor.startDismissKeyguardTransition(
+ reason = "Going away remote animation started"
+ )
+
goingAwayRemoteAnimationFinishedCallback = finishedCallback
keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index dad2d96..f1e98f3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -263,7 +263,9 @@
}
fun dismissKeyguard() {
- scope.launch("$TAG#dismissKeyguard") { startTransitionTo(KeyguardState.GONE) }
+ scope.launch("$TAG#dismissKeyguard") {
+ startTransitionTo(KeyguardState.GONE, ownerReason = "#dismissKeyguard()")
+ }
}
private fun listenForLockscreenToGone() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 7285739..8065c0f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -322,7 +322,11 @@
}
}
}
- .distinctUntilChanged()
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = 0f,
+ )
val clockShouldBeCentered: Flow<Boolean> = repository.clockShouldBeCentered
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index e711edc..75c4d6f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -19,6 +19,7 @@
import com.android.keyguard.logging.KeyguardLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.log.core.LogLevel.VERBOSE
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -26,6 +27,7 @@
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
private val TAG = KeyguardTransitionAuditLogger::class.simpleName!!
@@ -41,6 +43,7 @@
private val logger: KeyguardLogger,
private val powerInteractor: PowerInteractor,
private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+ private val keyguardRootViewModel: KeyguardRootViewModel,
private val shadeInteractor: ShadeInteractor,
private val keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) {
@@ -72,8 +75,8 @@
if (!SceneContainerFlag.isEnabled) {
scope.launch {
- sharedNotificationContainerViewModel.bounds.collect {
- logger.log(TAG, VERBOSE, "Notif: bounds", it)
+ sharedNotificationContainerViewModel.bounds.debounce(20L).collect {
+ logger.log(TAG, VERBOSE, "Notif: bounds (debounced)", it)
}
}
}
@@ -113,6 +116,18 @@
}
scope.launch {
+ keyguardInteractor.keyguardTranslationY.collect {
+ logger.log(TAG, VERBOSE, "keyguardTranslationY", it)
+ }
+ }
+
+ scope.launch {
+ keyguardRootViewModel.burnInModel.debounce(20L).collect {
+ logger.log(TAG, VERBOSE, "BurnInModel (debounced)", it)
+ }
+ }
+
+ scope.launch {
keyguardInteractor.isKeyguardDismissible.collect {
logger.log(TAG, VERBOSE, "isDismissible", it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 8f9a70980..c65dc30 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -20,6 +20,7 @@
import android.annotation.FloatRange
import android.annotation.SuppressLint
import android.util.Log
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardRepository
@@ -31,10 +32,13 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.util.kotlin.pairwise
import java.util.UUID
import javax.inject.Inject
@@ -71,8 +75,9 @@
private val fromAlternateBouncerTransitionInteractor:
dagger.Lazy<FromAlternateBouncerTransitionInteractor>,
private val fromDozingTransitionInteractor: dagger.Lazy<FromDozingTransitionInteractor>,
+ private val sceneInteractor: dagger.Lazy<SceneInteractor>,
) {
- private val transitionMap = mutableMapOf<Edge, MutableSharedFlow<TransitionStep>>()
+ private val transitionMap = mutableMapOf<Edge.StateToState, MutableSharedFlow<TransitionStep>>()
/**
* Numerous flows are derived from, or care directly about, the transition value in and out of a
@@ -128,11 +133,11 @@
scope.launch {
repository.transitions.collect {
// FROM->TO
- transitionMap[Edge(it.from, it.to)]?.emit(it)
+ transitionMap[Edge.create(it.from, it.to)]?.emit(it)
// FROM->(ANY)
- transitionMap[Edge(it.from, null)]?.emit(it)
+ transitionMap[Edge.create(it.from, null)]?.emit(it)
// (ANY)->TO
- transitionMap[Edge(null, it.to)]?.emit(it)
+ transitionMap[Edge.create(null, it.to)]?.emit(it)
}
}
@@ -152,26 +157,70 @@
}
}
- /** Given an [edge], return a SharedFlow to collect only relevant [TransitionStep]. */
+ fun transition(edge: Edge, edgeWithoutSceneContainer: Edge): Flow<TransitionStep> {
+ return transition(if (SceneContainerFlag.isEnabled) edge else edgeWithoutSceneContainer)
+ }
+
+ /** Given an [edge], return a Flow to collect only relevant [TransitionStep]s. */
@SuppressLint("SharedFlowCreation")
- fun getOrCreateFlow(edge: Edge): MutableSharedFlow<TransitionStep> {
- return transitionMap.getOrPut(edge) {
- MutableSharedFlow(
- extraBufferCapacity = 10,
- onBufferOverflow = BufferOverflow.DROP_OLDEST
- )
+ fun transition(edge: Edge): Flow<TransitionStep> {
+ edge.verifyValidKeyguardStates()
+ val mappedEdge = getMappedEdge(edge)
+
+ val flow: Flow<TransitionStep> =
+ transitionMap.getOrPut(mappedEdge) {
+ MutableSharedFlow(
+ extraBufferCapacity = 10,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST
+ )
+ }
+
+ return if (SceneContainerFlag.isEnabled) {
+ flow.filter {
+ val fromScene =
+ when (edge) {
+ is Edge.StateToState -> edge.from?.mapToSceneContainerScene()
+ is Edge.StateToScene -> edge.from.mapToSceneContainerScene()
+ is Edge.SceneToState -> edge.from
+ }
+
+ val toScene =
+ when (edge) {
+ is Edge.StateToState -> edge.to?.mapToSceneContainerScene()
+ is Edge.StateToScene -> edge.to
+ is Edge.SceneToState -> edge.to.mapToSceneContainerScene()
+ }
+
+ fun SceneKey?.isLockscreenOrNull() = this == Scenes.Lockscreen || this == null
+
+ return@filter (fromScene.isLockscreenOrNull() && toScene.isLockscreenOrNull()) ||
+ sceneInteractor.get().transitionState.value.isTransitioning(fromScene, toScene)
+ }
+ } else {
+ flow
}
}
/**
- * Receive all [TransitionStep] matching a filter of [from]->[to]. Allow nulls in order to match
- * any transition, for instance (any)->GONE.
+ * Converts old KTF states to UNDEFINED when [SceneContainerFlag] is enabled.
+ *
+ * Does nothing otherwise.
+ *
+ * This method should eventually be removed when new code is only written for scene container.
+ * Even when all edges are ported today, there is still development on going in production that
+ * might utilize old states.
*/
- fun transition(from: KeyguardState? = null, to: KeyguardState? = null): Flow<TransitionStep> {
- if (from == null && to == null) {
- throw IllegalArgumentException("from and to cannot both be null")
+ private fun getMappedEdge(edge: Edge): Edge.StateToState {
+ if (!SceneContainerFlag.isEnabled) return edge as Edge.StateToState
+ return when (edge) {
+ is Edge.StateToState ->
+ Edge.create(
+ from = edge.from?.mapToSceneContainerState(),
+ to = edge.to?.mapToSceneContainerState()
+ )
+ is Edge.SceneToState -> Edge.create(UNDEFINED, edge.to)
+ is Edge.StateToScene -> Edge.create(edge.from, UNDEFINED)
}
- return getOrCreateFlow(Edge(from = from, to = to))
}
/**
@@ -367,32 +416,37 @@
val isInTransitionToAnyState = isInTransitionWhere({ true }, { true })
fun transitionStepsFromState(fromState: KeyguardState): Flow<TransitionStep> {
- return getOrCreateFlow(Edge(from = fromState, to = null))
+ return transition(Edge.create(from = fromState, to = null))
}
fun transitionStepsToState(toState: KeyguardState): Flow<TransitionStep> {
- return getOrCreateFlow(Edge(from = null, to = toState))
+ return transition(Edge.create(from = null, to = toState))
}
/**
* Called to start a transition that will ultimately dismiss the keyguard from the current
* state.
+ *
+ * This is called exclusively by sources that can authoritatively say we should be unlocked,
+ * including KeyguardSecurityContainerController and WindowManager.
*/
- fun startDismissKeyguardTransition() {
+ fun startDismissKeyguardTransition(reason: String = "") {
// TODO(b/336576536): Check if adaptation for scene framework is needed
if (SceneContainerFlag.isEnabled) return
- when (val startedState = startedKeyguardState.replayCache.last()) {
+ Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)")
+ when (val startedState = currentTransitionInfoInternal.value.to) {
LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard()
PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer()
ALTERNATE_BOUNCER ->
fromAlternateBouncerTransitionInteractor.get().dismissAlternateBouncer()
AOD -> fromAodTransitionInteractor.get().dismissAod()
DOZING -> fromDozingTransitionInteractor.get().dismissFromDozing()
- else ->
- Log.e(
- "KeyguardTransitionInteractor",
- "We don't know how to dismiss keyguard from state $startedState."
+ KeyguardState.GONE ->
+ Log.i(
+ TAG,
+ "Already transitioning to GONE; ignoring startDismissKeyguardTransition."
)
+ else -> Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.")
}
}
@@ -400,7 +454,7 @@
fun isInTransitionToState(
state: KeyguardState,
): Flow<Boolean> {
- return getOrCreateFlow(Edge(from = null, to = state))
+ return transition(Edge.create(from = null, to = state))
.mapLatest { it.transitionState.isTransitioning() }
.onStart { emit(false) }
.distinctUntilChanged()
@@ -409,12 +463,16 @@
/**
* Whether we're in a transition to and from the given [KeyguardState]s, but haven't yet
* completed it.
+ *
+ * Provide [edgeWithoutSceneContainer] when the edge is different from what it is without it. If
+ * the edges are equal before and after the flag it is sufficient to provide just [edge].
*/
- fun isInTransition(
- from: KeyguardState,
- to: KeyguardState,
- ): Flow<Boolean> {
- return getOrCreateFlow(Edge(from = from, to = to))
+ fun isInTransition(edge: Edge, edgeWithoutSceneContainer: Edge? = null): Flow<Boolean> {
+ return if (SceneContainerFlag.isEnabled) {
+ transition(edge)
+ } else {
+ transition(edgeWithoutSceneContainer ?: edge)
+ }
.mapLatest { it.transitionState.isTransitioning() }
.onStart { emit(false) }
.distinctUntilChanged()
@@ -426,7 +484,7 @@
fun isInTransitionFromState(
state: KeyguardState,
): Flow<Boolean> {
- return getOrCreateFlow(Edge(from = state, to = null))
+ return transition(Edge.create(from = state, to = null))
.mapLatest { it.transitionState.isTransitioning() }
.onStart { emit(false) }
.distinctUntilChanged()
@@ -477,7 +535,7 @@
* If you only care about a single state for both from and to, instead use the optimized
* [isInTransition].
*/
- fun isInTransitionWhere(
+ private fun isInTransitionWhere(
fromToStatePredicate: (KeyguardState, KeyguardState) -> Boolean
): Flow<Boolean> {
return repository.transitions
@@ -529,4 +587,8 @@
@FloatRange(from = 0.0, to = 1.0) value: Float,
state: TransitionState
) = repository.updateTransition(transitionId, value, state)
+
+ companion object {
+ private val TAG = KeyguardTransitionInteractor::class.simpleName
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index dc35e43..1e2db7c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -19,6 +19,7 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.scene.domain.interactor.SceneInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt
index a0f9be6..4f516f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt
@@ -15,8 +15,98 @@
*/
package com.android.systemui.keyguard.shared.model
-/** FROM -> TO keyguard transition. null values are allowed to signify FROM -> *, or * -> TO */
-data class Edge(
- val from: KeyguardState?,
- val to: KeyguardState?,
-)
+import android.util.Log
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+
+/**
+ * Represents an edge either between two Keyguard Transition Framework states (KTF) or a KTF state
+ * and a scene container scene. Passing [null] in either [from] or [to] indicates a wildcard.
+ *
+ * Wildcards are not allowed for transitions involving a scene. Use [sceneInteractor] directly
+ * instead. Reason: [TransitionStep]s are not emitted for every edge leading into/out of a scene.
+ * For example: Lockscreen -> Gone would be emitted as LOCKSCREEN -> UNDEFINED but Bouncer -> Gone
+ * would not emit anything.
+ */
+sealed class Edge {
+
+ fun verifyValidKeyguardStates() {
+ when (this) {
+ is StateToState -> verifyValidKeyguardStates(from, to)
+ is SceneToState -> verifyValidKeyguardStates(null, to)
+ is StateToScene -> verifyValidKeyguardStates(from, null)
+ }
+ }
+
+ private fun verifyValidKeyguardStates(from: KeyguardState?, to: KeyguardState?) {
+ val mappedFrom = from?.mapToSceneContainerState()
+ val mappedTo = to?.mapToSceneContainerState()
+
+ val fromChanged = from != mappedFrom
+ val toChanged = to != mappedTo
+
+ if (SceneContainerFlag.isEnabled) {
+ if (fromChanged && toChanged) {
+ // TODO:(b/330311871) As we come close to having all current edges converted these
+ // error messages can be converted to throw such that future developers fail early
+ // when they introduce invalid edges.
+ Log.e(
+ TAG,
+ """
+ The edge ${from?.name} => ${to?.name} was automatically converted to
+ ${mappedFrom?.name} => ${mappedTo?.name} but does not exist anymore in KTF.
+ Please remove or port this edge to scene container."""
+ .trimIndent(),
+ )
+ } else if ((fromChanged && to == null) || (toChanged && from == null)) {
+ Log.e(
+ TAG,
+ """
+ The edge ${from?.name} => ${to?.name} was automatically converted to
+ ${mappedFrom?.name} => ${mappedTo?.name}. Wildcards are not allowed together
+ with UNDEFINED because it will only be tracking edges leading in and out of
+ the Lockscreen scene but miss others. Please remove or port this edge."""
+ .trimIndent(),
+ Exception()
+ )
+ } else if (fromChanged || toChanged) {
+ Log.w(
+ TAG,
+ """
+ The edge ${from?.name} => ${to?.name} was automatically converted to
+ ${mappedFrom?.name} => ${mappedTo?.name} it probably exists but needs explicit
+ conversion. Please remove or port this edge to scene container."""
+ .trimIndent(),
+ )
+ }
+ } else {
+ if (from == UNDEFINED || to == UNDEFINED) {
+ Log.e(
+ TAG,
+ "UNDEFINED should not be used when scene container is disabled",
+ )
+ }
+ }
+ }
+
+ data class StateToState(val from: KeyguardState?, val to: KeyguardState?) : Edge() {
+ init {
+ check(!(from == null && to == null)) { "to and from can't both be null" }
+ }
+ }
+
+ data class StateToScene(val from: KeyguardState, val to: SceneKey) : Edge()
+
+ data class SceneToState(val from: SceneKey, val to: KeyguardState) : Edge()
+
+ companion object {
+ private const val TAG = "Edge"
+
+ fun create(from: KeyguardState? = null, to: KeyguardState? = null) = StateToState(from, to)
+
+ fun create(from: KeyguardState, to: SceneKey) = StateToScene(from, to)
+
+ fun create(from: SceneKey, to: KeyguardState) = SceneToState(from, to)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index 6d96db3..6a2bb5f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -15,6 +15,9 @@
*/
package com.android.systemui.keyguard.shared.model
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
+
/** List of all possible states to transition to/from */
enum class KeyguardState {
/**
@@ -84,6 +87,40 @@
/** An activity is displaying over the keyguard. */
OCCLUDED;
+ fun mapToSceneContainerState(): KeyguardState {
+ return when (this) {
+ OFF,
+ DOZING,
+ DREAMING,
+ DREAMING_LOCKSCREEN_HOSTED,
+ AOD,
+ ALTERNATE_BOUNCER,
+ OCCLUDED,
+ LOCKSCREEN -> this
+ GLANCEABLE_HUB,
+ PRIMARY_BOUNCER,
+ GONE,
+ UNDEFINED -> UNDEFINED
+ }
+ }
+
+ fun mapToSceneContainerScene(): SceneKey? {
+ return when (this) {
+ OFF,
+ DOZING,
+ DREAMING,
+ DREAMING_LOCKSCREEN_HOSTED,
+ AOD,
+ ALTERNATE_BOUNCER,
+ OCCLUDED,
+ LOCKSCREEN -> Scenes.Lockscreen
+ GLANCEABLE_HUB -> Scenes.Communal
+ PRIMARY_BOUNCER -> Scenes.Bouncer
+ GONE -> Scenes.Gone
+ UNDEFINED -> null
+ }
+ }
+
companion object {
/** Whether the lockscreen is visible when we're FINISHED in the given state. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
index 735b109..23aa21c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -28,6 +28,7 @@
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import javax.inject.Inject
import kotlin.math.max
import kotlin.math.min
@@ -52,20 +53,20 @@
/** Invoke once per transition between FROM->TO states to get access to a shared flow. */
fun setup(
duration: Duration,
- from: KeyguardState?,
- to: KeyguardState?,
+ edge: Edge,
): FlowBuilder {
- if (from == null && to == null) {
- throw IllegalArgumentException("from and to are both null")
- }
-
- return FlowBuilder(duration, Edge(from, to))
+ return FlowBuilder(duration, edge)
}
inner class FlowBuilder(
private val transitionDuration: Duration,
private val edge: Edge,
) {
+ fun setupWithoutSceneContainer(edge: Edge.StateToState): FlowBuilder {
+ if (SceneContainerFlag.isEnabled) return this
+ return setup(this.transitionDuration, edge)
+ }
+
/**
* Transitions will occur over a [transitionDuration] with [TransitionStep]s being emitted
* in the range of [0, 1]. View animations should begin and end within a subset of this
@@ -117,7 +118,7 @@
if (!duration.isPositive()) {
throw IllegalArgumentException("duration must be a positive number: $duration")
}
- if ((startTime + duration).compareTo(transitionDuration) > 0) {
+ if ((startTime + duration) > transitionDuration) {
throw IllegalArgumentException(
"startTime($startTime) + duration($duration) must be" +
" <= transitionDuration($transitionDuration)"
@@ -153,7 +154,7 @@
}
return transitionInteractor
- .getOrCreateFlow(edge)
+ .transition(edge)
.map { step ->
StateToValue(
from = step.from,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
index 4fd92d7..9da11ce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
@@ -19,7 +19,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -42,8 +44,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromAlternateBouncerTransitionInteractor.TO_AOD_DURATION,
- from = KeyguardState.ALTERNATE_BOUNCER,
- to = KeyguardState.AOD,
+ edge = Edge.create(from = ALTERNATE_BOUNCER, to = AOD),
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt
index 9649af73..55a48b6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt
@@ -19,7 +19,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -42,8 +44,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromAlternateBouncerTransitionInteractor.TO_DOZING_DURATION,
- from = KeyguardState.ALTERNATE_BOUNCER,
- to = KeyguardState.DOZING,
+ edge = Edge.create(from = ALTERNATE_BOUNCER, to = DOZING),
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
index 8c6be98..bb4fb79 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
@@ -19,11 +19,13 @@
import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_GONE_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.SysuiStatusBarStateController
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -44,11 +46,14 @@
private val statusBarStateController: SysuiStatusBarStateController,
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_GONE_DURATION,
- from = ALTERNATE_BOUNCER,
- to = KeyguardState.GONE,
- )
+ animationFlow
+ .setup(
+ duration = TO_GONE_DURATION,
+ edge = Edge.create(from = ALTERNATE_BOUNCER, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = ALTERNATE_BOUNCER, to = GONE),
+ )
fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
var startAlpha = 1f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
index 27febd3..3f2ef29 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
@@ -18,8 +18,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_OCCLUDED_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+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 javax.inject.Inject
@@ -40,8 +41,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_OCCLUDED_DURATION,
- from = ALTERNATE_BOUNCER,
- to = KeyguardState.OCCLUDED,
+ edge = Edge.create(from = ALTERNATE_BOUNCER, to = OCCLUDED),
)
override val deviceEntryParentViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
index 7592881..f0bccac 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
@@ -18,9 +18,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -37,11 +40,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
- from = KeyguardState.ALTERNATE_BOUNCER,
- to = KeyguardState.PRIMARY_BOUNCER,
- )
+ animationFlow
+ .setup(
+ duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+ edge = Edge.create(from = ALTERNATE_BOUNCER, to = Scenes.Bouncer),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = ALTERNATE_BOUNCER, to = PRIMARY_BOUNCER),
+ )
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
index 5cf100e..4128c52 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
@@ -34,8 +34,8 @@
alternateBouncerInteractor: AlternateBouncerInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
) {
- private val deviceSupportsAlternateBouncer: Flow<Boolean> =
- alternateBouncerInteractor.alternateBouncerSupported
+ val canShowAlternateBouncer: Flow<Boolean> = alternateBouncerInteractor.canShowAlternateBouncer
+
private val isTransitioningToOrFromOrShowingAlternateBouncer: Flow<Boolean> =
keyguardTransitionInteractor
.transitionValue(KeyguardState.ALTERNATE_BOUNCER)
@@ -43,8 +43,8 @@
.distinctUntilChanged()
val alternateBouncerWindowRequired: Flow<Boolean> =
- deviceSupportsAlternateBouncer.flatMapLatest { deviceSupportsAlternateBouncer ->
- if (deviceSupportsAlternateBouncer) {
+ canShowAlternateBouncer.flatMapLatest { canShowAlternateBouncer ->
+ if (canShowAlternateBouncer) {
isTransitioningToOrFromOrShowingAlternateBouncer
} else {
flowOf(false)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
index adc090d..8e8b09d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
@@ -19,9 +19,12 @@
import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -37,11 +40,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = FromAodTransitionInteractor.TO_GONE_DURATION,
- from = KeyguardState.AOD,
- to = KeyguardState.GONE,
- )
+ animationFlow
+ .setup(
+ duration = FromAodTransitionInteractor.TO_GONE_DURATION,
+ edge = Edge.create(from = AOD, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = AOD, to = GONE),
+ )
/**
* AOD -> GONE should fade out the lockscreen contents. This transition plays both during wake
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
index cbbb820..b267ecb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
@@ -19,9 +19,10 @@
import android.util.MathUtils
import com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
@@ -39,7 +40,6 @@
class AodToLockscreenTransitionViewModel
@Inject
constructor(
- deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
shadeInteractor: ShadeInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
@@ -47,8 +47,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_LOCKSCREEN_DURATION,
- from = KeyguardState.AOD,
- to = KeyguardState.LOCKSCREEN,
+ edge = Edge.create(from = AOD, to = LOCKSCREEN),
)
private var isShadeExpanded = false
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 445575f..2497def 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
@@ -19,7 +19,9 @@
import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+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 javax.inject.Inject
@@ -36,8 +38,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
- from = KeyguardState.AOD,
- to = KeyguardState.OCCLUDED,
+ edge = Edge.create(from = AOD, to = OCCLUDED),
)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
index 9a23007..35f05f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
@@ -18,9 +18,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -37,11 +40,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = FromAodTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
- from = KeyguardState.AOD,
- to = KeyguardState.PRIMARY_BOUNCER,
- )
+ animationFlow
+ .setup(
+ duration = FromAodTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+ edge = Edge.create(from = AOD, to = Scenes.Bouncer),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = AOD, to = PRIMARY_BOUNCER),
+ )
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
index 570f377..caa0436 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
@@ -19,11 +19,13 @@
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.SysuiStatusBarStateController
import dagger.Lazy
@@ -73,8 +75,12 @@
return animationFlow
.setup(
duration = duration,
- from = from,
- to = GONE,
+ // TODO(b/330311871): from can be PRIMARY_BOUNCER which would be a scene -> scene
+ // transition
+ edge = Edge.create(from = from, to = Scenes.Gone)
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = from, to = GONE),
)
.sharedFlow(
duration = duration,
@@ -96,11 +102,16 @@
var leaveShadeOpen: Boolean = false
var willRunDismissFromKeyguard: Boolean = false
val transitionAnimation =
- animationFlow.setup(
- duration = duration,
- from = fromState,
- to = GONE,
- )
+ animationFlow
+ .setup(
+ duration = duration,
+ // TODO(b/330311871): from can be PRIMARY_BOUNCER which would be a scene ->
+ // scene transition
+ edge = Edge.create(from = fromState, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = fromState, to = GONE),
+ )
return shadeInteractor.anyExpansion
.map { it > 0f }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
index 8851a51..77ebfce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
@@ -19,9 +19,12 @@
import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_GONE_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -37,11 +40,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_GONE_DURATION,
- from = KeyguardState.DOZING,
- to = KeyguardState.GONE,
- )
+ animationFlow
+ .setup(
+ duration = TO_GONE_DURATION,
+ edge = Edge.create(from = DOZING, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = DOZING, to = GONE),
+ )
fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
var startAlpha = 1f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
index 168d6e1..a460d51 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
@@ -18,7 +18,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -39,8 +41,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromDozingTransitionInteractor.TO_LOCKSCREEN_DURATION,
- from = KeyguardState.DOZING,
- to = KeyguardState.LOCKSCREEN,
+ edge = Edge.create(from = DOZING, to = LOCKSCREEN),
)
val shortcutsAlpha: Flow<Float> =
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 c0b1195..f33752f 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
@@ -19,7 +19,9 @@
import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+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 javax.inject.Inject
@@ -38,8 +40,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
- from = KeyguardState.DOZING,
- to = KeyguardState.OCCLUDED,
+ edge = Edge.create(from = DOZING, to = OCCLUDED),
)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
index 4395c34..7ddf641 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
@@ -18,9 +18,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_PRIMARY_BOUNCER_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -38,11 +41,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_PRIMARY_BOUNCER_DURATION,
- from = KeyguardState.DOZING,
- to = KeyguardState.PRIMARY_BOUNCER,
- )
+ animationFlow
+ .setup(
+ duration = TO_PRIMARY_BOUNCER_DURATION,
+ edge = Edge.create(from = DOZING, to = Scenes.Bouncer),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = DOZING, to = PRIMARY_BOUNCER),
+ )
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
index 67568e1..57ed455 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
@@ -18,7 +18,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDreamingLockscreenHostedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -34,8 +36,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_LOCKSCREEN_DURATION,
- from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
- to = KeyguardState.LOCKSCREEN,
+ edge = Edge.create(from = DREAMING_LOCKSCREEN_HOSTED, to = LOCKSCREEN),
)
val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt
index 0fa7475..754ed6c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt
@@ -19,7 +19,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -40,12 +42,12 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromDreamingTransitionInteractor.TO_AOD_DURATION,
- from = KeyguardState.DREAMING,
- to = KeyguardState.AOD,
+ edge = Edge.create(from = DREAMING, to = AOD),
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
+
override val deviceEntryParentViewAlpha: Flow<Float> =
deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolledAndEnabled
->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
index a083c24e..00aa102 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
@@ -19,10 +19,13 @@
import com.android.app.animation.Interpolators.EMPHASIZED
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
@@ -40,11 +43,14 @@
configurationInteractor: ConfigurationInteractor,
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_GLANCEABLE_HUB_DURATION,
- from = KeyguardState.DREAMING,
- to = KeyguardState.GLANCEABLE_HUB,
- )
+ animationFlow
+ .setup(
+ duration = TO_GLANCEABLE_HUB_DURATION,
+ edge = Edge.create(from = DREAMING, to = Scenes.Communal),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = DREAMING, to = GLANCEABLE_HUB),
+ )
val dreamOverlayTranslationX: Flow<Float> =
configurationInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt
index ec7b931..1bdf6d29 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt
@@ -18,10 +18,13 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
-import kotlinx.coroutines.flow.Flow
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
@SysUISingleton
class DreamingToGoneTransitionViewModel
@@ -31,13 +34,15 @@
) {
private val transitionAnimation =
- animationFlow.setup(
+ animationFlow
+ .setup(
duration = FromDreamingTransitionInteractor.TO_GONE_DURATION,
- from = KeyguardState.DREAMING,
- to = KeyguardState.GONE,
+ edge = Edge.create(from = DREAMING, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = DREAMING, to = GONE),
)
/** Lockscreen views alpha */
val lockscreenAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f)
-
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index f191aa7..82381eb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -20,7 +20,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -45,8 +47,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_LOCKSCREEN_DURATION,
- from = KeyguardState.DREAMING,
- to = KeyguardState.LOCKSCREEN,
+ edge = Edge.create(from = DREAMING, to = LOCKSCREEN),
)
/** Dream overlay y-translation on exit */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
index 3716458..d594488 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
@@ -19,10 +19,13 @@
import com.android.app.animation.Interpolators
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
@@ -41,11 +44,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = FROM_GLANCEABLE_HUB_DURATION,
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.DREAMING,
- )
+ animationFlow
+ .setup(
+ duration = FROM_GLANCEABLE_HUB_DURATION,
+ edge = Edge.create(from = Scenes.Communal, to = DREAMING),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GLANCEABLE_HUB, to = DREAMING),
+ )
val dreamOverlayAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
index e05b500..046b95f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
@@ -20,10 +20,13 @@
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,11 +48,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_LOCKSCREEN_DURATION,
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.LOCKSCREEN,
- )
+ animationFlow
+ .setup(
+ duration = TO_LOCKSCREEN_DURATION,
+ edge = Edge.create(from = Scenes.Communal, to = LOCKSCREEN),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GLANCEABLE_HUB, to = LOCKSCREEN),
+ )
val keyguardAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt
index 300121f..cd98bb0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt
@@ -18,9 +18,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor.Companion.TO_OCCLUDED_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+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.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -32,11 +35,12 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_OCCLUDED_DURATION,
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.OCCLUDED,
- )
+ animationFlow
+ .setup(
+ duration = TO_OCCLUDED_DURATION,
+ edge = Edge.create(from = Scenes.Communal, to = OCCLUDED),
+ )
+ .setupWithoutSceneContainer(edge = Edge.create(from = GLANCEABLE_HUB, to = OCCLUDED))
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
index 3540bec..74f7d75 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
@@ -20,10 +20,13 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_AOD_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,11 +45,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_AOD_DURATION,
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
- )
+ animationFlow
+ .setup(
+ duration = TO_AOD_DURATION,
+ edge = Edge.create(from = Scenes.Gone, to = AOD),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GONE, to = AOD),
+ )
/** y-translation from the top of the screen for AOD */
fun enterFromTopTranslationY(translatePx: Int): Flow<StateToValue> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
index 80a6bda..70c0032 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
@@ -19,9 +19,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DOZING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -40,11 +43,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_DOZING_DURATION,
- from = KeyguardState.GONE,
- to = KeyguardState.DOZING,
- )
+ animationFlow
+ .setup(
+ duration = TO_DOZING_DURATION,
+ edge = Edge.create(from = Scenes.Gone, to = DOZING),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GONE, to = DOZING),
+ )
val lockscreenAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
index b527463..627f0de 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
@@ -18,8 +18,11 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -36,11 +39,14 @@
) {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_DREAMING_DURATION,
- from = KeyguardState.GONE,
- to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
- )
+ animationFlow
+ .setup(
+ duration = TO_DREAMING_DURATION,
+ edge = Edge.create(from = Scenes.Gone, to = DREAMING_LOCKSCREEN_HOSTED),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GONE, to = DREAMING_LOCKSCREEN_HOSTED),
+ )
/** Lockscreen views alpha - hide immediately */
val lockscreenAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
index 102242a..f8b6e28 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
@@ -19,8 +19,11 @@
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -34,11 +37,14 @@
) {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_DREAMING_DURATION,
- from = KeyguardState.GONE,
- to = KeyguardState.DREAMING,
- )
+ animationFlow
+ .setup(
+ duration = TO_DREAMING_DURATION,
+ edge = Edge.create(from = Scenes.Gone, to = DREAMING),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GONE, to = DREAMING),
+ )
/** Lockscreen views y-translation */
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
index a2ce408..08ec43f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
@@ -18,9 +18,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -33,11 +36,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_LOCKSCREEN_DURATION,
- from = KeyguardState.GONE,
- to = KeyguardState.LOCKSCREEN
- )
+ animationFlow
+ .setup(
+ duration = TO_LOCKSCREEN_DURATION,
+ edge = Edge.create(from = Scenes.Gone, to = LOCKSCREEN),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GONE, to = LOCKSCREEN),
+ )
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index bbcea56..f405b9d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -30,6 +30,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.BurnInModel
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -38,6 +39,7 @@
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.ui.StateToValue
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.DozeParameters
@@ -115,14 +117,18 @@
private val shadeInteractor: ShadeInteractor,
) {
private var burnInJob: Job? = null
- private val burnInModel = MutableStateFlow(BurnInModel())
+ internal val burnInModel = MutableStateFlow(BurnInModel())
val burnInLayerVisibility: Flow<Int> =
keyguardTransitionInteractor.startedKeyguardState
.filter { it == AOD || it == LOCKSCREEN }
.map { VISIBLE }
- val goneToAodTransition = keyguardTransitionInteractor.transition(from = GONE, to = AOD)
+ val goneToAodTransition =
+ keyguardTransitionInteractor.transition(
+ edge = Edge.create(Scenes.Gone, AOD),
+ edgeWithoutSceneContainer = Edge.create(GONE, AOD)
+ )
private val goneToAodTransitionRunning: Flow<Boolean> =
goneToAodTransition
@@ -144,7 +150,10 @@
private val alphaOnShadeExpansion: Flow<Float> =
combineTransform(
- keyguardTransitionInteractor.isInTransition(from = LOCKSCREEN, to = GONE),
+ keyguardTransitionInteractor.isInTransition(
+ edge = Edge.create(from = LOCKSCREEN, to = Scenes.Gone),
+ edgeWithoutSceneContainer = Edge.create(from = LOCKSCREEN, to = GONE),
+ ),
isOnLockscreen,
shadeInteractor.qsExpansion,
shadeInteractor.shadeExpansion,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
index 1f9f304..8b5b347 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -20,7 +20,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -45,8 +47,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromLockscreenTransitionInteractor.TO_AOD_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
+ edge = Edge.create(from = LOCKSCREEN, to = AOD),
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
index c836f01..27a1f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
@@ -19,7 +19,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DOZING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -40,8 +42,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_DOZING_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.DOZING,
+ edge = Edge.create(from = LOCKSCREEN, to = DOZING),
)
val lockscreenAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
index 19b9cf47..778dbed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
@@ -18,7 +18,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_HOSTED_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -34,8 +36,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_DREAMING_HOSTED_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ edge = Edge.create(from = LOCKSCREEN, to = DREAMING_LOCKSCREEN_HOSTED),
)
val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
index 13522a6..579abeb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
@@ -19,7 +19,9 @@
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -40,8 +42,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_DREAMING_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.DREAMING,
+ edge = Edge.create(from = LOCKSCREEN, to = DREAMING),
)
/** Lockscreen views y-translation */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
index dae7897..c7273b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
@@ -20,10 +20,13 @@
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,11 +48,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
- animationFlow.setup(
- duration = FromLockscreenTransitionInteractor.TO_GLANCEABLE_HUB_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.GLANCEABLE_HUB,
- )
+ animationFlow
+ .setup(
+ duration = FromLockscreenTransitionInteractor.TO_GLANCEABLE_HUB_DURATION,
+ edge = Edge.create(from = LOCKSCREEN, to = Scenes.Communal),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = LOCKSCREEN, to = GLANCEABLE_HUB),
+ )
val keyguardAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
index f03625e..1314e88 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
@@ -19,10 +19,13 @@
import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow.FlowBuilder
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.SysuiStatusBarStateController
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -42,11 +45,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation: FlowBuilder =
- animationFlow.setup(
- duration = FromLockscreenTransitionInteractor.TO_GONE_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.GONE,
- )
+ animationFlow
+ .setup(
+ duration = FromLockscreenTransitionInteractor.TO_GONE_DURATION,
+ edge = Edge.create(from = LOCKSCREEN, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = LOCKSCREEN, to = GONE),
+ )
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
index dd6652e..fcf8c14f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
@@ -20,7 +20,9 @@
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
+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.res.R
@@ -45,8 +47,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_OCCLUDED_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.OCCLUDED,
+ edge = Edge.create(from = KeyguardState.LOCKSCREEN, to = OCCLUDED),
)
/** Lockscreen views alpha */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
index 0cfc757..23c44b0a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
@@ -18,9 +18,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -39,11 +42,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.PRIMARY_BOUNCER,
- )
+ animationFlow
+ .setup(
+ duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+ edge = Edge.create(from = LOCKSCREEN, to = Scenes.Bouncer),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = LOCKSCREEN, to = PRIMARY_BOUNCER),
+ )
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
index d7ba46b..706a3c4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
@@ -19,7 +19,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+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 javax.inject.Inject
@@ -41,8 +43,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromOccludedTransitionInteractor.TO_AOD_DURATION,
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.AOD,
+ edge = Edge.create(from = OCCLUDED, to = AOD),
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
index 91554e3..af01930 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
@@ -18,7 +18,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -38,8 +40,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromOccludedTransitionInteractor.TO_DOZING_DURATION,
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.DOZING,
+ edge = Edge.create(from = OCCLUDED, to = DOZING),
)
/** Lockscreen views alpha */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt
index 73a4a9d..47e202b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt
@@ -18,9 +18,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_GLANCEABLE_HUB_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+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.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -32,11 +35,12 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_GLANCEABLE_HUB_DURATION,
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.GLANCEABLE_HUB,
- )
+ animationFlow
+ .setup(
+ duration = TO_GLANCEABLE_HUB_DURATION,
+ edge = Edge.create(OCCLUDED, Scenes.Communal)
+ )
+ .setupWithoutSceneContainer(edge = Edge.create(OCCLUDED, GLANCEABLE_HUB))
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(1f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt
index d2c9cfb..98dba39 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt
@@ -17,8 +17,11 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -33,11 +36,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
- animationFlow.setup(
- duration = DEFAULT_DURATION,
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.GONE,
- )
+ animationFlow
+ .setup(
+ duration = DEFAULT_DURATION,
+ edge = Edge.create(from = OCCLUDED, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = OCCLUDED, to = GONE),
+ )
fun notificationAlpha(viewState: ViewStateAccessor): Flow<Float> {
var currentAlpha = 0f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index a09d58a..36c7d5b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -23,7 +23,9 @@
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+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.res.R
@@ -56,8 +58,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_LOCKSCREEN_DURATION,
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.LOCKSCREEN,
+ edge = Edge.create(from = OCCLUDED, to = LOCKSCREEN),
)
/** Lockscreen views y-translation */
@@ -101,7 +102,7 @@
.filter { (wasOccluded, isOccluded) ->
wasOccluded &&
!isOccluded &&
- keyguardTransitionInteractor.getCurrentState() == KeyguardState.OCCLUDED
+ keyguardTransitionInteractor.getCurrentState() == OCCLUDED
}
.map { 0f }
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
index cf6a533..1eecbd5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
@@ -17,7 +17,9 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -34,8 +36,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = 250.milliseconds,
- from = KeyguardState.OFF,
- to = KeyguardState.LOCKSCREEN,
+ edge = Edge.create(from = OFF, to = LOCKSCREEN),
)
val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
index 942903b..009f85d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
@@ -19,9 +19,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,11 +45,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.AOD,
- )
+ animationFlow
+ .setup(
+ duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
+ edge = Edge.create(from = Scenes.Bouncer, to = AOD),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = PRIMARY_BOUNCER, to = AOD),
+ )
val deviceEntryBackgroundViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
index 13f651a..e5bb464 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
@@ -19,9 +19,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_DOZING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -42,11 +45,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_DOZING_DURATION,
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.DOZING,
- )
+ animationFlow
+ .setup(
+ duration = TO_DOZING_DURATION,
+ edge = Edge.create(from = Scenes.Bouncer, to = DOZING),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = PRIMARY_BOUNCER, to = DOZING),
+ )
val deviceEntryBackgroundViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index b1fa710..7ae4558 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -20,11 +20,13 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.SysuiStatusBarStateController
import dagger.Lazy
import javax.inject.Inject
@@ -49,11 +51,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_GONE_DURATION,
- from = PRIMARY_BOUNCER,
- to = GONE,
- )
+ animationFlow
+ .setup(
+ duration = TO_GONE_DURATION,
+ edge = Edge.create(from = PRIMARY_BOUNCER, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = PRIMARY_BOUNCER, to = GONE),
+ )
private var leaveShadeOpen: Boolean = false
private var willRunDismissFromKeyguard: Boolean = false
@@ -88,6 +93,7 @@
} else {
createBouncerAlphaFlow(primaryBouncerInteractor::willRunDismissFromKeyguard)
}
+
private fun createBouncerAlphaFlow(willRunAnimationOnKeyguard: () -> Boolean): Flow<Float> {
return transitionAnimation.sharedFlow(
duration = 200.milliseconds,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
index 2575041..7511101 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -19,9 +19,12 @@
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -39,11 +42,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.LOCKSCREEN,
- )
+ animationFlow
+ .setup(
+ duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
+ edge = Edge.create(from = Scenes.Bouncer, to = LOCKSCREEN),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN),
+ )
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
index d1fee90..1a0f582 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
@@ -21,9 +21,7 @@
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
-import android.media.session.MediaController
import android.media.session.MediaSession
-import android.media.session.PlaybackState
import android.provider.Settings
import android.util.Log
import com.android.internal.jank.Cuj
@@ -42,7 +40,6 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.kotlin.pairwiseBy
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
@@ -70,19 +67,6 @@
.map { entries -> entries[instanceId]?.let { toMediaControlModel(it) } }
.distinctUntilChanged()
- val isStartedPlaying: Flow<Boolean> =
- mediaControl
- .map { mediaControl ->
- mediaControl?.token?.let { token ->
- MediaController(applicationContext, token).playbackState?.let {
- it.state == PlaybackState.STATE_PLAYING
- }
- }
- ?: false
- }
- .pairwiseBy(initialValue = false) { wasPlaying, isPlaying -> !wasPlaying && isPlaying }
- .distinctUntilChanged()
-
val onAnyMediaConfigurationChange: Flow<Unit> = repository.onAnyMediaConfigurationChange
fun removeMediaControl(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
index 73fb558..fed93f0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -260,44 +260,50 @@
}
SEMANTIC_ACTIONS_ALL.forEachIndexed { index, id ->
- val button = viewHolder.getAction(id)
- val actionViewModel = viewModel.actionButtons[index]
- if (button.id == R.id.actionPrev) {
- actionViewModel?.let {
- viewController.setUpPrevButtonInfo(true, it.notVisibleValue)
- }
- } else if (button.id == R.id.actionNext) {
- actionViewModel?.let {
- viewController.setUpNextButtonInfo(true, it.notVisibleValue)
- }
+ val buttonView = viewHolder.getAction(id)
+ val buttonModel = viewModel.actionButtons[index]
+ if (buttonView.id == R.id.actionPrev) {
+ viewController.setUpPrevButtonInfo(
+ buttonModel.isEnabled,
+ buttonModel.notVisibleValue
+ )
+ } else if (buttonView.id == R.id.actionNext) {
+ viewController.setUpNextButtonInfo(
+ buttonModel.isEnabled,
+ buttonModel.notVisibleValue
+ )
}
- actionViewModel?.let { action ->
- val animHandler = (button.tag ?: AnimationBindHandler()) as AnimationBindHandler
- animHandler.tryExecute {
- if (animHandler.updateRebindId(action.rebindId)) {
+ val animHandler = (buttonView.tag ?: AnimationBindHandler()) as AnimationBindHandler
+ animHandler.tryExecute {
+ if (buttonModel.isEnabled) {
+ if (animHandler.updateRebindId(buttonModel.rebindId)) {
animHandler.unregisterAll()
- animHandler.tryRegister(action.icon)
- animHandler.tryRegister(action.background)
+ animHandler.tryRegister(buttonModel.icon)
+ animHandler.tryRegister(buttonModel.background)
bindButtonCommon(
- button,
+ buttonView,
viewHolder.multiRippleView,
- action,
+ buttonModel,
viewController,
falsingManager,
)
}
- val visible = action.isVisibleWhenScrubbing || !viewController.isScrubbing
- setSemanticButtonVisibleAndAlpha(
- viewHolder.getAction(id),
- viewController.expandedLayout,
- viewController.collapsedLayout,
- visible,
- action.notVisibleValue,
- action.showInCollapsed
- )
+ } else {
+ animHandler.unregisterAll()
+ clearButton(buttonView)
}
+ val visible =
+ buttonModel.isEnabled &&
+ (buttonModel.isVisibleWhenScrubbing || !viewController.isScrubbing)
+ setSemanticButtonVisibleAndAlpha(
+ viewHolder.getAction(id),
+ viewController.expandedLayout,
+ viewController.collapsedLayout,
+ visible,
+ buttonModel.notVisibleValue,
+ buttonModel.showInCollapsed
+ )
}
- ?: clearButton(button)
}
} else {
// Hide buttons that only appear for semantic actions
@@ -309,22 +315,16 @@
// Set all generic buttons
genericButtons.forEachIndexed { index, button ->
if (index < viewModel.actionButtons.size) {
- viewModel.actionButtons[index]?.let { action ->
- bindButtonCommon(
- button,
- viewHolder.multiRippleView,
- action,
- viewController,
- falsingManager,
- )
- setVisibleAndAlpha(expandedSet, button.id, visible = true)
- setVisibleAndAlpha(
- collapsedSet,
- button.id,
- visible = action.showInCollapsed
- )
- }
- ?: clearButton(button)
+ val action = viewModel.actionButtons[index]
+ bindButtonCommon(
+ button,
+ viewHolder.multiRippleView,
+ action,
+ viewController,
+ falsingManager,
+ )
+ setVisibleAndAlpha(expandedSet, button.id, visible = true)
+ setVisibleAndAlpha(collapsedSet, button.id, visible = action.showInCollapsed)
} else {
// Hide any unused buttons
clearButton(button)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index d09e997..188516c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -46,6 +46,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
@@ -190,6 +191,7 @@
@VisibleForTesting
lateinit var settingsButton: View
private set
+
private val mediaContent: ViewGroup
@VisibleForTesting var pageIndicator: PageIndicator
private var needsReordering: Boolean = false
@@ -640,7 +642,7 @@
internal fun listenForAnyStateToGoneKeyguardTransition(scope: CoroutineScope): Job {
return scope.launch {
keyguardTransitionInteractor
- .transition(to = GONE)
+ .transition(Edge.create(to = GONE))
.filter { it.transitionState == TransitionState.FINISHED }
.collect {
showMediaCarousel()
@@ -653,7 +655,7 @@
internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job {
return scope.launch {
keyguardTransitionInteractor
- .transition(to = LOCKSCREEN)
+ .transition(Edge.create(to = LOCKSCREEN))
.filter { it.transitionState == TransitionState.FINISHED }
.collect {
if (!allowMediaPlayerOnLockScreen) {
@@ -1598,6 +1600,7 @@
// Whether should prioritize Smartspace card.
internal var shouldPrioritizeSs: Boolean = false
private set
+
internal var smartspaceMediaData: SmartspaceMediaData? = null
private set
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index 2b59858..3837708 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -709,12 +709,6 @@
// For Turbulence noise.
val loadingEffectView = mediaViewHolder.loadingEffectView
- turbulenceNoiseAnimationConfig =
- createTurbulenceNoiseConfig(
- loadingEffectView,
- turbulenceNoiseView,
- colorSchemeTransition
- )
noiseDrawCallback =
object : PaintDrawCallback {
override fun onDraw(paint: Paint) {
@@ -809,6 +803,14 @@
fun setUpTurbulenceNoise() {
if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+ if (!this::turbulenceNoiseAnimationConfig.isInitialized) {
+ turbulenceNoiseAnimationConfig =
+ createTurbulenceNoiseConfig(
+ mediaViewHolder.loadingEffectView,
+ mediaViewHolder.turbulenceNoiseView,
+ colorSchemeTransition
+ )
+ }
if (Flags.shaderlibLoadingEffectRefactor()) {
if (!this::loadingEffect.isInitialized) {
loadingEffect =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index bc364c3..1944f07 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -20,6 +20,7 @@
import android.content.pm.PackageManager
import android.media.session.MediaController
import android.media.session.MediaSession.Token
+import android.media.session.PlaybackState
import android.text.TextUtils
import android.util.Log
import androidx.constraintlayout.widget.ConstraintSet
@@ -40,16 +41,14 @@
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
import com.android.systemui.res.R
-import com.android.systemui.util.kotlin.sample
import java.util.concurrent.Executor
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
/** Models UI state and handles user input for a media control. */
class MediaControlViewModel(
@@ -60,31 +59,20 @@
private val logger: MediaUiEventLogger,
) {
- private val isAnyButtonClicked: MutableStateFlow<Boolean> = MutableStateFlow(false)
-
- private val playTurbulenceNoise: Flow<Boolean> =
- interactor.mediaControl.sample(
- combine(isAnyButtonClicked, interactor.isStartedPlaying) {
- isButtonClicked,
- isStartedPlaying ->
- isButtonClicked && isStartedPlaying
- }
- .distinctUntilChanged()
- )
-
@OptIn(ExperimentalCoroutinesApi::class)
val player: Flow<MediaPlayerViewModel?> =
interactor.onAnyMediaConfigurationChange
.flatMapLatest {
- combine(playTurbulenceNoise, interactor.mediaControl) {
- playTurbulenceNoise,
- mediaControl ->
- mediaControl?.let { toViewModel(it, playTurbulenceNoise) }
+ interactor.mediaControl.map { mediaControl ->
+ mediaControl?.let { toViewModel(it) }
}
}
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
+ private var isPlaying = false
+ private var isAnyButtonClicked = false
+
private fun onDismissMediaData(
token: Token?,
uid: Int,
@@ -95,10 +83,8 @@
interactor.removeMediaControl(token, instanceId, MEDIA_PLAYER_ANIMATION_DELAY)
}
- private suspend fun toViewModel(
- model: MediaControlModel,
- playTurbulenceNoise: Boolean
- ): MediaPlayerViewModel? {
+ private suspend fun toViewModel(model: MediaControlModel): MediaPlayerViewModel? {
+ val mediaController = model.token?.let { MediaController(applicationContext, it) }
val wallpaperColors =
MediaArtworkHelper.getWallpaperColor(
applicationContext,
@@ -118,8 +104,14 @@
val gutsViewModel = toGutsViewModel(model, scheme)
+ // Set playing state
+ val wasPlaying = isPlaying
+ isPlaying =
+ mediaController?.playbackState?.let { it.state == PlaybackState.STATE_PLAYING } ?: false
+
// Resetting button clicks state.
- isAnyButtonClicked.value = false
+ val wasButtonClicked = isAnyButtonClicked
+ isAnyButtonClicked = false
return MediaPlayerViewModel(
contentDescription = { gutsVisible ->
@@ -144,7 +136,7 @@
shouldAddGradient = wallpaperColors != null,
colorScheme = scheme,
canShowTime = canShowScrubbingTimeViews(model.semanticActionButtons),
- playTurbulenceNoise = playTurbulenceNoise,
+ playTurbulenceNoise = isPlaying && !wasPlaying && wasButtonClicked,
useSemanticActions = model.semanticActionButtons != null,
actionButtons = toActionViewModels(model),
outputSwitcher = toOutputSwitcherViewModel(model),
@@ -168,9 +160,7 @@
seekBarViewModel.updateStaticProgress(model.resumeProgress)
} else {
backgroundExecutor.execute {
- seekBarViewModel.updateController(
- model.token?.let { MediaController(applicationContext, it) }
- )
+ seekBarViewModel.updateController(mediaController)
}
}
}
@@ -283,16 +273,17 @@
)
}
- private fun toActionViewModels(model: MediaControlModel): List<MediaActionViewModel?> {
+ private fun toActionViewModels(model: MediaControlModel): List<MediaActionViewModel> {
val semanticActionButtons =
model.semanticActionButtons?.let { mediaButton ->
- with(mediaButton) {
- val isScrubbingTimeEnabled = canShowScrubbingTimeViews(mediaButton)
- SEMANTIC_ACTIONS_ALL.map { buttonId ->
- getActionById(buttonId)?.let {
- toSemanticActionViewModel(model, it, buttonId, isScrubbingTimeEnabled)
- }
- }
+ val isScrubbingTimeEnabled = canShowScrubbingTimeViews(mediaButton)
+ SEMANTIC_ACTIONS_ALL.map { buttonId ->
+ toSemanticActionViewModel(
+ model,
+ mediaButton.getActionById(buttonId),
+ buttonId,
+ isScrubbingTimeEnabled
+ )
}
}
val notifActionButtons =
@@ -304,7 +295,7 @@
private fun toSemanticActionViewModel(
model: MediaControlModel,
- mediaAction: MediaAction,
+ mediaAction: MediaAction?,
buttonId: Int,
canShowScrubbingTimeViews: Boolean
): MediaActionViewModel {
@@ -312,9 +303,9 @@
val hideWhenScrubbing = SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.contains(buttonId)
val shouldHideWhenScrubbing = canShowScrubbingTimeViews && hideWhenScrubbing
return MediaActionViewModel(
- icon = mediaAction.icon,
- contentDescription = mediaAction.contentDescription,
- background = mediaAction.background,
+ icon = mediaAction?.icon,
+ contentDescription = mediaAction?.contentDescription,
+ background = mediaAction?.background,
isVisibleWhenScrubbing = !shouldHideWhenScrubbing,
notVisibleValue =
if (
@@ -326,11 +317,11 @@
ConstraintSet.GONE
},
showInCollapsed = showInCollapsed,
- rebindId = mediaAction.rebindId,
+ rebindId = mediaAction?.rebindId,
buttonId = buttonId,
- isEnabled = mediaAction.action != null,
+ isEnabled = mediaAction?.action != null,
onClicked = { id ->
- mediaAction.action?.let {
+ mediaAction?.action?.let {
onButtonClicked(id, model.uid, model.packageName, model.instanceId, it)
}
},
@@ -366,7 +357,7 @@
) {
logger.logTapAction(id, uid, packageName, instanceId)
// TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
- isAnyButtonClicked.value = true
+ isAnyButtonClicked = true
action.run()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
index d1014e8..4334341 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
@@ -35,7 +35,7 @@
val canShowTime: Boolean,
val playTurbulenceNoise: Boolean,
val useSemanticActions: Boolean,
- val actionButtons: List<MediaActionViewModel?>,
+ val actionButtons: List<MediaActionViewModel>,
val outputSwitcher: MediaOutputSwitcherViewModel,
val gutsMenu: GutsViewModel,
val onClicked: (Expandable) -> Unit,
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
index 481b476..67fe0e9 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
@@ -73,6 +73,10 @@
return mFlags;
}
+ public boolean isFlagEnabled(@SystemUiStateFlags long flag) {
+ return (mFlags & flag) != 0;
+ }
+
/** Methods to this call can be chained together before calling {@link #commitUpdate(int)}. */
public SysUiState setFlag(@SystemUiStateFlags long flag, boolean enabled) {
final Boolean overrideOrNull = mSceneContainerPlugin.flagValueOverride(flag);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index e4cb211..0327ec7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -115,7 +115,7 @@
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInterface;
import dagger.Lazy;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
index caa67df..1868b4a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
@@ -25,7 +25,6 @@
import android.os.UserHandle
import android.util.Log
import android.util.Pair
-import android.view.View
import android.view.Window
import com.android.app.tracing.coroutines.launch
import com.android.internal.app.ChooserActivity
@@ -41,8 +40,8 @@
private val intentExecutor: ActionIntentExecutor,
@Application private val applicationScope: CoroutineScope,
@Assisted val window: Window,
- @Assisted val transitionView: View,
- @Assisted val onDismiss: (() -> Unit)
+ @Assisted val viewProxy: ScreenshotViewProxy,
+ @Assisted val finishDismiss: () -> Unit,
) {
var isPendingSharedTransition = false
@@ -50,6 +49,7 @@
fun startSharedTransition(intent: Intent, user: UserHandle, overrideTransition: Boolean) {
isPendingSharedTransition = true
+ viewProxy.fadeForSharedTransition()
val windowTransition = createWindowTransition()
applicationScope.launch("$TAG#launchIntentAsync") {
intentExecutor.launchIntent(
@@ -70,7 +70,7 @@
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
)
pendingIntent.send(options.toBundle())
- onDismiss.invoke()
+ viewProxy.requestDismissal(null)
} catch (e: PendingIntent.CanceledException) {
Log.e(TAG, "Intent cancelled", e)
}
@@ -89,7 +89,7 @@
override fun hideSharedElements() {
isPendingSharedTransition = false
- onDismiss.invoke()
+ finishDismiss.invoke()
}
override fun onFinish() {}
@@ -98,13 +98,20 @@
window,
callbacks,
null,
- Pair.create(transitionView, ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME)
+ Pair.create(
+ viewProxy.screenshotPreview,
+ ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME
+ )
)
}
@AssistedFactory
interface Factory {
- fun create(window: Window, transitionView: View, onDismiss: (() -> Unit)): ActionExecutor
+ fun create(
+ window: Window,
+ viewProxy: ScreenshotViewProxy,
+ finishDismiss: (() -> Unit)
+ ): ActionExecutor
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
index a0cef52..15638d3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
@@ -97,6 +97,7 @@
.putExtra(LongScreenshotActivity.EXTRA_SCREENSHOT_USER_HANDLE, owner)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ .addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
}
private const val EXTRA_EDIT_SOURCE = "edit_source"
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
index 4cf18fb..3d024a6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
@@ -157,6 +157,8 @@
override fun restoreNonScrollingUi() = view.restoreNonScrollingUi()
+ override fun fadeForSharedTransition() {} // unused
+
override fun stopInputListening() = view.stopInputListening()
override fun requestFocus() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 2f026ae..9ad6d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -317,9 +317,9 @@
mConfigChanges.applyNewConfig(context.getResources());
reloadAssets();
- mActionExecutor = actionExecutorFactory.create(mWindow, mViewProxy.getScreenshotPreview(),
+ mActionExecutor = actionExecutorFactory.create(mWindow, mViewProxy,
() -> {
- requestDismissal(null);
+ finishDismiss();
return Unit.INSTANCE;
});
@@ -623,9 +623,11 @@
(response) -> {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION,
0, response.getPackageName());
- if (screenshotShelfUi2() && mActionsProvider != null) {
- mActionsProvider.onScrollChipReady(
- () -> onScrollButtonClicked(owner, response));
+ if (screenshotShelfUi2()) {
+ if (mActionsProvider != null) {
+ mActionsProvider.onScrollChipReady(
+ () -> onScrollButtonClicked(owner, response));
+ }
} else {
mViewProxy.showScrollChip(response.getPackageName(),
() -> onScrollButtonClicked(owner, response));
@@ -657,9 +659,7 @@
() -> {
final Intent intent = ActionIntentCreator.INSTANCE.createLongScreenshotIntent(
owner, mContext);
- mActionIntentExecutor.launchIntentAsync(intent, owner, true,
- ActivityOptions.makeCustomAnimation(mContext, 0, 0), null);
-
+ mActionIntentExecutor.launchIntentAsync(intent, owner, true, null, null);
},
mViewProxy::restoreNonScrollingUi,
mViewProxy::startLongScreenshotTransition);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
index 846884f..3ac070a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
@@ -90,15 +90,15 @@
override var isDismissing = false
override var isPendingSharedTransition = false
- private val animationController = ScreenshotAnimationController(view)
+ private val animationController = ScreenshotAnimationController(view, viewModel)
init {
shelfViewBinder.bind(
view,
viewModel,
+ animationController,
LayoutInflater.from(context),
onDismissalRequested = { event, velocity -> requestDismissal(event, velocity) },
- onDismissalCancelled = { animationController.getSwipeReturnAnimation().start() },
onUserInteraction = { callbacks?.onUserInteraction() }
)
view.updateInsets(windowManager.currentWindowMetrics.windowInsets)
@@ -188,24 +188,53 @@
override fun prepareScrollingTransition(
response: ScrollCaptureResponse,
- screenBitmap: Bitmap,
+ screenBitmap: Bitmap, // unused
newScreenshot: Bitmap,
screenshotTakenInPortrait: Boolean,
onTransitionPrepared: Runnable,
) {
- onTransitionPrepared.run()
+ viewModel.setScrollingScrimBitmap(newScreenshot)
+ viewModel.setScrollableRect(scrollableAreaOnScreen(response))
+ animationController.fadeForLongScreenshotTransition()
+ view.post { onTransitionPrepared.run() }
+ }
+
+ private fun scrollableAreaOnScreen(response: ScrollCaptureResponse): Rect {
+ val r = Rect(response.boundsInWindow)
+ val windowInScreen = response.windowBounds
+ r.offset(windowInScreen?.left ?: 0, windowInScreen?.top ?: 0)
+ r.intersect(
+ Rect(
+ 0,
+ 0,
+ context.resources.displayMetrics.widthPixels,
+ context.resources.displayMetrics.heightPixels
+ )
+ )
+ return r
}
override fun startLongScreenshotTransition(
transitionDestination: Rect,
onTransitionEnd: Runnable,
- longScreenshot: ScrollCaptureController.LongScreenshot
+ longScreenshot: ScrollCaptureController.LongScreenshot,
) {
- onTransitionEnd.run()
- callbacks?.onDismiss()
+ val transitionAnimation =
+ animationController.runLongScreenshotTransition(
+ transitionDestination,
+ longScreenshot,
+ onTransitionEnd
+ )
+ transitionAnimation.doOnEnd { callbacks?.onDismiss() }
+ transitionAnimation.start()
}
- override fun restoreNonScrollingUi() {}
+ override fun restoreNonScrollingUi() {
+ viewModel.setScrollableRect(null)
+ viewModel.setScrollingScrimBitmap(null)
+ animationController.restoreUI()
+ callbacks?.onUserInteraction() // reset the timeout
+ }
override fun stopInputListening() {}
@@ -228,6 +257,10 @@
)
}
+ override fun fadeForSharedTransition() {
+ animationController.fadeForSharedTransition()
+ }
+
private fun addPredictiveBackListener(onDismissRequested: (ScreenshotEvent) -> Unit) {
val onBackInvokedCallback = OnBackInvokedCallback {
debugLog(DEBUG_INPUT) { "Predictive Back callback dispatched" }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
index a4069d1..df93a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
@@ -63,6 +63,7 @@
longScreenshot: ScrollCaptureController.LongScreenshot
)
fun restoreNonScrollingUi()
+ fun fadeForSharedTransition()
fun stopInputListening()
fun requestFocus()
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
index 06e88f4..a4906c1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
@@ -20,6 +20,10 @@
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
+import android.content.res.ColorStateList
+import android.graphics.BlendMode
+import android.graphics.Color
+import android.graphics.Matrix
import android.graphics.PointF
import android.graphics.Rect
import android.util.MathUtils
@@ -29,13 +33,21 @@
import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
import com.android.systemui.res.R
+import com.android.systemui.screenshot.scroll.ScrollCaptureController
+import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.sign
-class ScreenshotAnimationController(private val view: ScreenshotShelfView) {
+class ScreenshotAnimationController(
+ private val view: ScreenshotShelfView,
+ private val viewModel: ScreenshotViewModel
+) {
private var animator: Animator? = null
private val screenshotPreview = view.requireViewById<ImageView>(R.id.screenshot_preview)
+ private val scrollingScrim = view.requireViewById<ImageView>((R.id.screenshot_scrolling_scrim))
+ private val scrollTransitionPreview =
+ view.requireViewById<ImageView>(R.id.screenshot_scrollable_preview)
private val flashView = view.requireViewById<View>(R.id.screenshot_flash)
private val actionContainer = view.requireViewById<View>(R.id.actions_container_background)
private val fastOutSlowIn =
@@ -46,6 +58,14 @@
view.requireViewById(R.id.screenshot_badge),
view.requireViewById(R.id.screenshot_dismiss_button)
)
+ private val fadeUI =
+ listOf<View>(
+ view.requireViewById(R.id.screenshot_preview_border),
+ view.requireViewById(R.id.actions_container_background),
+ view.requireViewById(R.id.screenshot_badge),
+ view.requireViewById(R.id.screenshot_dismiss_button),
+ view.requireViewById(R.id.screenshot_message_container),
+ )
fun getEntranceAnimation(
bounds: Rect,
@@ -96,15 +116,108 @@
}
entranceAnimation.play(fadeInAnimator).after(previewAnimator)
entranceAnimation.doOnStart {
+ viewModel.setIsAnimating(true)
for (child in staticUI) {
child.alpha = 0f
}
}
+ entranceAnimation.doOnEnd { viewModel.setIsAnimating(false) }
this.animator = entranceAnimation
return entranceAnimation
}
+ fun fadeForSharedTransition() {
+ animator?.cancel()
+ val fadeAnimator = ValueAnimator.ofFloat(1f, 0f)
+ fadeAnimator.addUpdateListener {
+ for (view in fadeUI) {
+ view.alpha = it.animatedValue as Float
+ }
+ }
+ animator = fadeAnimator
+ fadeAnimator.start()
+ }
+
+ fun runLongScreenshotTransition(
+ destRect: Rect,
+ longScreenshot: ScrollCaptureController.LongScreenshot,
+ onTransitionEnd: Runnable
+ ): Animator {
+ val animSet = AnimatorSet()
+
+ val scrimAnim = ValueAnimator.ofFloat(0f, 1f)
+ scrimAnim.addUpdateListener { animation: ValueAnimator ->
+ scrollingScrim.setAlpha(1 - animation.animatedFraction)
+ }
+ scrollTransitionPreview.visibility = View.VISIBLE
+ if (true) {
+ scrollTransitionPreview.setImageBitmap(longScreenshot.toBitmap())
+ val startX: Float = scrollTransitionPreview.x
+ val startY: Float = scrollTransitionPreview.y
+ val locInScreen: IntArray = scrollTransitionPreview.getLocationOnScreen()
+ destRect.offset(startX.toInt() - locInScreen[0], startY.toInt() - locInScreen[1])
+ scrollTransitionPreview.pivotX = 0f
+ scrollTransitionPreview.pivotY = 0f
+ scrollTransitionPreview.setAlpha(1f)
+ val currentScale: Float = scrollTransitionPreview.width / longScreenshot.width.toFloat()
+ val matrix = Matrix()
+ matrix.setScale(currentScale, currentScale)
+ matrix.postTranslate(
+ longScreenshot.left * currentScale,
+ longScreenshot.top * currentScale
+ )
+ scrollTransitionPreview.setImageMatrix(matrix)
+ val destinationScale: Float = destRect.width() / scrollTransitionPreview.width.toFloat()
+ val previewAnim = ValueAnimator.ofFloat(0f, 1f)
+ previewAnim.addUpdateListener { animation: ValueAnimator ->
+ val t = animation.animatedFraction
+ val currScale = MathUtils.lerp(1f, destinationScale, t)
+ scrollTransitionPreview.scaleX = currScale
+ scrollTransitionPreview.scaleY = currScale
+ scrollTransitionPreview.x = MathUtils.lerp(startX, destRect.left.toFloat(), t)
+ scrollTransitionPreview.y = MathUtils.lerp(startY, destRect.top.toFloat(), t)
+ }
+ val previewFadeAnim = ValueAnimator.ofFloat(1f, 0f)
+ previewFadeAnim.addUpdateListener { animation: ValueAnimator ->
+ scrollTransitionPreview.setAlpha(1 - animation.animatedFraction)
+ }
+ previewAnim.doOnEnd { onTransitionEnd.run() }
+ animSet.play(previewAnim).with(scrimAnim).before(previewFadeAnim)
+ } else {
+ // if we switched orientations between the original screenshot and the long screenshot
+ // capture, just fade out the scrim instead of running the preview animation
+ scrimAnim.doOnEnd { onTransitionEnd.run() }
+ animSet.play(scrimAnim)
+ }
+ animator = animSet
+ return animSet
+ }
+
+ fun fadeForLongScreenshotTransition() {
+ scrollingScrim.imageTintBlendMode = BlendMode.SRC_ATOP
+ val anim = ValueAnimator.ofFloat(0f, .3f)
+ anim.addUpdateListener {
+ scrollingScrim.setImageTintList(
+ ColorStateList.valueOf(Color.argb(it.animatedValue as Float, 0f, 0f, 0f))
+ )
+ }
+ for (view in fadeUI) {
+ view.alpha = 0f
+ }
+ screenshotPreview.alpha = 0f
+ anim.setDuration(200)
+ anim.start()
+ }
+
+ fun restoreUI() {
+ animator?.cancel()
+ for (view in fadeUI) {
+ view.alpha = 1f
+ }
+ screenshotPreview.alpha = 1f
+ }
+
fun getSwipeReturnAnimation(): Animator {
animator?.cancel()
val animator = ValueAnimator.ofFloat(view.translationX, 0f)
@@ -114,6 +227,7 @@
}
fun getSwipeDismissAnimation(requestedVelocity: Float?): Animator {
+ animator?.cancel()
val velocity = getAdjustedVelocity(requestedVelocity)
val screenWidth = view.resources.displayMetrics.widthPixels
// translation at which point the visible UI is fully off the screen (in the direction
@@ -131,6 +245,8 @@
view.alpha = 1f - it.animatedFraction
}
animator.duration = ((abs(distance / velocity))).toLong()
+ animator.doOnStart { viewModel.setIsAnimating(true) }
+ animator.doOnEnd { viewModel.setIsAnimating(false) }
this.animator = animator
return animator
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
index c7bc50cb..442b387 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
@@ -16,7 +16,11 @@
package com.android.systemui.screenshot.ui.binder
+import android.content.res.Configuration
import android.graphics.Bitmap
+import android.graphics.Matrix
+import android.graphics.Rect
+import android.util.LayoutDirection
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -29,6 +33,7 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.screenshot.ScreenshotEvent
+import com.android.systemui.screenshot.ui.ScreenshotAnimationController
import com.android.systemui.screenshot.ui.ScreenshotShelfView
import com.android.systemui.screenshot.ui.SwipeGestureListener
import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel
@@ -45,9 +50,9 @@
fun bind(
view: ScreenshotShelfView,
viewModel: ScreenshotViewModel,
+ animationController: ScreenshotAnimationController,
layoutInflater: LayoutInflater,
onDismissalRequested: (event: ScreenshotEvent, velocity: Float?) -> Unit,
- onDismissalCancelled: () -> Unit,
onUserInteraction: () -> Unit
) {
val swipeGestureListener =
@@ -56,7 +61,7 @@
onDismiss = {
onDismissalRequested(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, it)
},
- onCancel = onDismissalCancelled
+ onCancel = { animationController.getSwipeReturnAnimation().start() }
)
view.onTouchInterceptListener = { swipeGestureListener.onMotionEvent(it) }
view.userInteractionCallback = onUserInteraction
@@ -66,11 +71,14 @@
val previewBorder = view.requireViewById<View>(R.id.screenshot_preview_border)
previewView.clipToOutline = true
previewViewBlur.clipToOutline = true
+ val actionsContainer: LinearLayout = view.requireViewById(R.id.screenshot_actions)
val dismissButton = view.requireViewById<View>(R.id.screenshot_dismiss_button)
dismissButton.visibility = if (viewModel.showDismissButton) View.VISIBLE else View.GONE
dismissButton.setOnClickListener {
onDismissalRequested(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL, null)
}
+ val scrollingScrim: ImageView = view.requireViewById(R.id.screenshot_scrolling_scrim)
+ val scrollablePreview: ImageView = view.requireViewById(R.id.screenshot_scrollable_preview)
val badgeView = view.requireViewById<ImageView>(R.id.screenshot_badge)
// use immediate dispatcher to ensure screenshot bitmap is set before animation
@@ -91,6 +99,29 @@
}
}
launch {
+ viewModel.scrollingScrim.collect { bitmap ->
+ if (bitmap != null) {
+ scrollingScrim.setImageBitmap(bitmap)
+ scrollingScrim.visibility = View.VISIBLE
+ } else {
+ scrollingScrim.visibility = View.GONE
+ }
+ }
+ }
+ launch {
+ viewModel.scrollableRect.collect { rect ->
+ if (rect != null) {
+ setScrollablePreview(
+ scrollablePreview,
+ viewModel.preview.value,
+ rect
+ )
+ } else {
+ scrollablePreview.visibility = View.GONE
+ }
+ }
+ }
+ launch {
viewModel.badge.collect { badge ->
badgeView.setImageDrawable(badge)
badgeView.visibility = if (badge != null) View.VISIBLE else View.GONE
@@ -102,6 +133,14 @@
}
}
launch {
+ viewModel.isAnimating.collect { isAnimating ->
+ previewView.isClickable = !isAnimating
+ for (child in actionsContainer.children) {
+ child.isClickable = !isAnimating
+ }
+ }
+ }
+ launch {
viewModel.actions.collect { actions ->
updateActions(
actions,
@@ -191,4 +230,35 @@
screenshotPreview.layoutParams = params
screenshotPreview.requestLayout()
}
+
+ private fun setScrollablePreview(
+ scrollablePreview: ImageView,
+ bitmap: Bitmap?,
+ scrollableRect: Rect
+ ) {
+ if (bitmap == null) {
+ return
+ }
+ val fixedSize = scrollablePreview.resources.getDimensionPixelSize(R.dimen.overlay_x_scale)
+ val inPortrait =
+ scrollablePreview.resources.configuration.orientation ==
+ Configuration.ORIENTATION_PORTRAIT
+ val scale: Float = fixedSize / ((if (inPortrait) bitmap.width else bitmap.height).toFloat())
+ val params = scrollablePreview.layoutParams
+
+ params.width = (scale * scrollableRect.width()).toInt()
+ params.height = (scale * scrollableRect.height()).toInt()
+ val matrix = Matrix()
+ matrix.setScale(scale, scale)
+ matrix.postTranslate(-scrollableRect.left * scale, -scrollableRect.top * scale)
+
+ scrollablePreview.translationX =
+ (scale *
+ if (scrollablePreview.layoutDirection == LayoutDirection.LTR) scrollableRect.left
+ else scrollableRect.right - (scrollablePreview.parent as View).width)
+ scrollablePreview.translationY = scale * scrollableRect.top
+ scrollablePreview.setImageMatrix(matrix)
+ scrollablePreview.setImageBitmap(bitmap)
+ scrollablePreview.setVisibility(View.VISIBLE)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
index 81bc281..3f99bc4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.screenshot.ui.viewmodel
import android.graphics.Bitmap
+import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.util.Log
import android.view.accessibility.AccessibilityManager
@@ -26,6 +27,8 @@
class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager) {
private val _preview = MutableStateFlow<Bitmap?>(null)
val preview: StateFlow<Bitmap?> = _preview
+ private val _scrollingScrim = MutableStateFlow<Bitmap?>(null)
+ val scrollingScrim: StateFlow<Bitmap?> = _scrollingScrim
private val _badge = MutableStateFlow<Drawable?>(null)
val badge: StateFlow<Drawable?> = _badge
private val _previewAction = MutableStateFlow<(() -> Unit)?>(null)
@@ -35,6 +38,10 @@
private val _animationState = MutableStateFlow(AnimationState.NOT_STARTED)
val animationState: StateFlow<AnimationState> = _animationState
+ private val _isAnimating = MutableStateFlow(false)
+ val isAnimating: StateFlow<Boolean> = _isAnimating
+ private val _scrollableRect = MutableStateFlow<Rect?>(null)
+ val scrollableRect: StateFlow<Rect?> = _scrollableRect
val showDismissButton: Boolean
get() = accessibilityManager.isEnabled
@@ -42,6 +49,10 @@
_preview.value = bitmap
}
+ fun setScrollingScrimBitmap(bitmap: Bitmap?) {
+ _scrollingScrim.value = bitmap
+ }
+
fun setScreenshotBadge(badge: Drawable?) {
_badge.value = badge
}
@@ -114,12 +125,23 @@
_animationState.value = state
}
+ fun setIsAnimating(isAnimating: Boolean) {
+ _isAnimating.value = isAnimating
+ }
+
+ fun setScrollableRect(rect: Rect?) {
+ _scrollableRect.value = rect
+ }
+
fun reset() {
_preview.value = null
+ _scrollingScrim.value = null
_badge.value = null
_previewAction.value = null
_actions.value = listOf()
_animationState.value = AnimationState.NOT_STARTED
+ _isAnimating.value = false
+ _scrollableRect.value = null
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
index 288ff09..84156eeb 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
@@ -51,6 +51,16 @@
return super.onTouchEvent(event);
}
+ @Override
+ public boolean onHoverEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
+ setHovered(true);
+ } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
+ setHovered(false);
+ }
+ return true;
+ }
+
public void setAccessibilityLabel(String label) {
mAccessibilityLabel = label;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index ee7b4be..22aa492 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -123,15 +123,9 @@
private var anyBouncerShowing = false
/**
- * True if the shade is fully expanded and the user is not interacting with it anymore, meaning
- * the hub should not receive any touch input.
+ * True if the shade is fully expanded, meaning the hub should not receive any touch input.
*
- * We need to not pause the touch handling lifecycle as soon as the shade opens because if the
- * user swipes down, then back up without lifting their finger, the lifecycle will be paused
- * then resumed, and resuming force-stops all active touch sessions. This means the shade will
- * not receive the end of the gesture and will be stuck open.
- *
- * Based on [ShadeInteractor.isAnyFullyExpanded] and [ShadeInteractor.isUserInteracting].
+ * Tracks [ShadeInteractor.isAnyFullyExpanded].
*/
private var shadeShowing = false
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 7051d5f..6bb30c7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -141,6 +141,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver;
import com.android.systemui.keyguard.shared.ComposeLockscreen;
+import com.android.systemui.keyguard.shared.model.Edge;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
@@ -170,6 +171,7 @@
import com.android.systemui.power.shared.model.WakefulnessModel;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.shade.data.repository.FlingInfo;
import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
@@ -1130,8 +1132,12 @@
controller.setup(mNotificationContainerParent));
// Dreaming->Lockscreen
- collectFlow(mView, mKeyguardTransitionInteractor.transition(DREAMING, LOCKSCREEN),
- mDreamingToLockscreenTransition, mMainDispatcher);
+ collectFlow(
+ mView,
+ mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(DREAMING, LOCKSCREEN)),
+ mDreamingToLockscreenTransition,
+ mMainDispatcher);
collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(),
setDreamLockscreenTransitionAlpha(mNotificationStackScrollLayoutController),
mMainDispatcher);
@@ -1141,7 +1147,8 @@
// Gone -> Dreaming hosted in lockscreen
collectFlow(mView, mKeyguardTransitionInteractor
- .transition(GONE, DREAMING_LOCKSCREEN_HOSTED),
+ .transition(Edge.Companion.create(Scenes.Gone, DREAMING_LOCKSCREEN_HOSTED),
+ Edge.Companion.create(GONE, DREAMING_LOCKSCREEN_HOSTED)),
mGoneToDreamingLockscreenHostedTransition, mMainDispatcher);
collectFlow(mView, mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha(),
setTransitionAlpha(mNotificationStackScrollLayoutController),
@@ -1149,16 +1156,17 @@
// Lockscreen -> Dreaming hosted in lockscreen
collectFlow(mView, mKeyguardTransitionInteractor
- .transition(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED),
+ .transition(Edge.Companion.create(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED)),
mLockscreenToDreamingLockscreenHostedTransition, mMainDispatcher);
// Dreaming hosted in lockscreen -> Lockscreen
collectFlow(mView, mKeyguardTransitionInteractor
- .transition(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN),
+ .transition(Edge.Companion.create(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN)),
mDreamingLockscreenHostedToLockscreenTransition, mMainDispatcher);
// Occluded->Lockscreen
- collectFlow(mView, mKeyguardTransitionInteractor.transition(OCCLUDED, LOCKSCREEN),
+ collectFlow(mView, mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(OCCLUDED, LOCKSCREEN)),
mOccludedToLockscreenTransition, mMainDispatcher);
if (!MigrateClocksToBlueprint.isEnabled()) {
collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(),
@@ -1169,7 +1177,8 @@
}
// Lockscreen->Dreaming
- collectFlow(mView, mKeyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING),
+ collectFlow(mView, mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(LOCKSCREEN, DREAMING)),
mLockscreenToDreamingTransition, mMainDispatcher);
if (!MigrateClocksToBlueprint.isEnabled()) {
collectFlow(mView, mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha(),
@@ -1181,7 +1190,9 @@
setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
// Gone->Dreaming
- collectFlow(mView, mKeyguardTransitionInteractor.transition(GONE, DREAMING),
+ collectFlow(mView, mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(Scenes.Gone, DREAMING),
+ Edge.Companion.create(GONE, DREAMING)),
mGoneToDreamingTransition, mMainDispatcher);
if (!MigrateClocksToBlueprint.isEnabled()) {
collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(),
@@ -1192,7 +1203,8 @@
setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
// Lockscreen->Occluded
- collectFlow(mView, mKeyguardTransitionInteractor.transition(LOCKSCREEN, OCCLUDED),
+ collectFlow(mView, mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(LOCKSCREEN, OCCLUDED)),
mLockscreenToOccludedTransition, mMainDispatcher);
if (!MigrateClocksToBlueprint.isEnabled()) {
collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(),
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index b50a3cd..6efa633 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -49,6 +49,7 @@
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.model.Edge;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.res.R;
@@ -137,11 +138,6 @@
private final PanelExpansionInteractor mPanelExpansionInteractor;
private final ShadeExpansionStateManager mShadeExpansionStateManager;
- /**
- * If {@code true}, an external touch sent in {@link #handleExternalTouch(MotionEvent)} has been
- * intercepted and all future touch events for the gesture should be processed by this view.
- */
- private boolean mExternalTouchIntercepted = false;
private boolean mIsTrackingBarGesture = false;
private boolean mIsOcclusionTransitionRunning = false;
private DisableSubpixelTextTransitionListener mDisableSubpixelTextTransitionListener;
@@ -225,7 +221,8 @@
mDisableSubpixelTextTransitionListener = new DisableSubpixelTextTransitionListener(mView);
bouncerViewBinder.bind(mView.findViewById(R.id.keyguard_bouncer_container));
- collectFlow(mView, keyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING),
+ collectFlow(mView, keyguardTransitionInteractor.transition(
+ Edge.Companion.create(LOCKSCREEN, DREAMING)),
mLockscreenToDreamingTransition);
collectFlow(
mView,
@@ -258,28 +255,11 @@
}
/**
- * Handle a touch event while dreaming or on the hub by forwarding the event to the content
- * view.
- * <p>
- * Since important logic for handling touches lives in the dispatch/intercept phases, we
- * simulate going through all of these stages before sending onTouchEvent if intercepted.
- *
+ * Handle a touch event while dreaming by forwarding the event to the content view.
* @param event The event to forward.
*/
- public void handleExternalTouch(MotionEvent event) {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mExternalTouchIntercepted = false;
- }
-
- if (!mView.dispatchTouchEvent(event)) {
- return;
- }
- if (!mExternalTouchIntercepted) {
- mExternalTouchIntercepted = mView.onInterceptTouchEvent(event);
- }
- if (mExternalTouchIntercepted) {
- mView.onTouchEvent(event);
- }
+ public void handleDreamTouch(MotionEvent event) {
+ mView.dispatchTouchEvent(event);
}
/** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index 6df8ac4..8b56dbd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -207,6 +207,11 @@
/** Indicates QS is at its max height */
private boolean mFullyExpanded;
+ /**
+ * Indicates QS is at its maximum height, AND takes up the whole screen (i.e. not in split
+ * shade).
+ */
+ private boolean mFullScreen;
private boolean mExpandedWhenExpandingStarted;
private boolean mAnimatingHiddenFromCollapsed;
private boolean mVisible;
@@ -967,27 +972,35 @@
}
private void setQsFullScreen(boolean qsFullScreen) {
+ if (mFullScreen == qsFullScreen) {
+ return; // no change
+ }
+ mFullScreen = qsFullScreen;
+
mShadeRepository.setLegacyQsFullscreen(qsFullScreen);
mNotificationStackScrollLayoutController.setQsFullScreen(qsFullScreen);
- if (!SceneContainerFlag.isEnabled()) {
- mNotificationStackScrollLayoutController.setScrollingEnabled(
- mBarState != KEYGUARD && (!qsFullScreen || mExpansionFromOverscroll));
- }
}
void updateQsState() {
+ boolean qsExpanded = getExpanded();
+
if (!FooterViewRefactor.isEnabled()) {
// Update full screen state; note that this will be true if the QS panel is only
// partially expanded, and that is fixed with the footer view refactor.
- setQsFullScreen(/* qsFullScreen = */ getExpanded() && !mSplitShadeEnabled);
+ setQsFullScreen(/* qsFullScreen = */ qsExpanded && !mSplitShadeEnabled);
+ }
+
+ if (!SceneContainerFlag.isEnabled()) {
+ mNotificationStackScrollLayoutController.setScrollingEnabled(
+ mBarState != KEYGUARD && (!mFullScreen || mExpansionFromOverscroll));
}
if (mQsStateUpdateListener != null) {
- mQsStateUpdateListener.onQsStateUpdated(getExpanded(), mStackScrollerOverscrolling);
+ mQsStateUpdateListener.onQsStateUpdated(qsExpanded, mStackScrollerOverscrolling);
}
if (mQs == null) return;
- mQs.setExpanded(getExpanded());
+ mQs.setExpanded(qsExpanded);
}
/** update expanded state of QS */
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 4f1056c..edd2961 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
@@ -101,6 +101,7 @@
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationCompactMessagingTemplateViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -845,6 +846,16 @@
}
/**
+ *
+ * @return true when compact version of Heads Up is on the screen.
+ */
+ public boolean isCompactConversationHeadsUpOnScreen() {
+ final NotificationViewWrapper viewWrapper =
+ getVisibleNotificationViewWrapper();
+
+ return viewWrapper instanceof NotificationCompactMessagingTemplateViewWrapper;
+ }
+ /**
* @see NotificationChildrenContainer#setUntruncatedChildCount(int)
*/
public void setUntruncatedChildCount(int childCount) {
@@ -2790,7 +2801,10 @@
}
}
- protected void expandNotification() {
+ /**
+ * Triggers expand click listener to expand the notification.
+ */
+ public void expandNotification() {
mExpandClickListener.onClick(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt
index ce87d2f..3a5f3b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt
@@ -24,7 +24,7 @@
/**
* Compact Heads up Notifications template that doesn't set feedback icon and audibly alert icons
*/
-class NotificationCompactHeadsUpTemplateViewWrapper(
+open class NotificationCompactHeadsUpTemplateViewWrapper(
ctx: Context,
view: View,
row: ExpandableNotificationRow
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactMessagingTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactMessagingTemplateViewWrapper.kt
new file mode 100644
index 0000000..bb40b56
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactMessagingTemplateViewWrapper.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import com.android.internal.R
+import com.android.internal.widget.CachingIconView
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+
+/** Wraps a notification containing a messaging or conversation template */
+class NotificationCompactMessagingTemplateViewWrapper
+constructor(ctx: Context, view: View, row: ExpandableNotificationRow) :
+ NotificationCompactHeadsUpTemplateViewWrapper(ctx, view, row) {
+
+ private val compactMessagingView: ViewGroup = requireNotNull(view as? ViewGroup)
+
+ private var conversationIconView: CachingIconView? = null
+ private var expandBtn: View? = null
+ override fun onContentUpdated(row: ExpandableNotificationRow?) {
+ resolveViews()
+ super.onContentUpdated(row)
+ }
+
+ private fun resolveViews() {
+ conversationIconView = compactMessagingView.requireViewById(R.id.conversation_icon)
+ expandBtn = compactMessagingView.requireViewById(R.id.expand_button)
+ }
+
+ override fun updateTransformedTypes() {
+ super.updateTransformedTypes()
+
+ addViewsTransformingToSimilar(
+ conversationIconView,
+ expandBtn,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 4244542..22b95ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -74,6 +74,8 @@
return new NotificationCallTemplateViewWrapper(ctx, v, row);
} else if ("compactHUN".equals((v.getTag()))) {
return new NotificationCompactHeadsUpTemplateViewWrapper(ctx, v, row);
+ } else if ("compactMessagingHUN".equals((v.getTag()))) {
+ return new NotificationCompactMessagingTemplateViewWrapper(ctx, v, row);
}
if (row.getEntry().getSbn().getNotification().isStyle(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 0ba7b3c..3393321 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -26,6 +26,7 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
@@ -64,6 +65,7 @@
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
@@ -295,8 +297,7 @@
return combine(
isOnLockscreenWithoutShade,
keyguardTransitionInteractor.isInTransition(
- from = LOCKSCREEN,
- to = AOD,
+ edge = Edge.create(from = LOCKSCREEN, to = AOD)
),
::Pair
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 7d97428..8fb552f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -283,12 +283,11 @@
void awakenDreams();
/**
- * Handle a touch event while dreaming or on the glanceable hub when the touch was initiated
- * within a prescribed swipeable area. This method is provided for cases where swiping in
- * certain areas should be handled by CentralSurfaces instead (e.g. swiping hub open, opening
- * the notification shade over dream or hub).
+ * Handle a touch event while dreaming when the touch was initiated within a prescribed
+ * swipeable area. This method is provided for cases where swiping in certain areas of a dream
+ * should be handled by CentralSurfaces instead (e.g. swiping communal hub open).
*/
- void handleExternalShadeWindowTouch(MotionEvent event);
+ void handleDreamTouch(MotionEvent event);
boolean isBouncerShowing();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
index d5e66ff..8af7ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -79,7 +79,7 @@
override fun updateScrimController() {}
override fun shouldIgnoreTouch() = false
override fun isDeviceInteractive() = false
- override fun handleExternalShadeWindowTouch(event: MotionEvent?) {}
+ override fun handleDreamTouch(event: MotionEvent?) {}
override fun awakenDreams() {}
override fun isBouncerShowing() = false
override fun isBouncerShowingScrimmed() = false
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 aa55f37..5b0b46e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -2932,8 +2932,8 @@
};
@Override
- public void handleExternalShadeWindowTouch(MotionEvent event) {
- getNotificationShadeWindowViewController().handleExternalTouch(event);
+ public void handleDreamTouch(MotionEvent event) {
+ getNotificationShadeWindowViewController().handleDreamTouch(event);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index f219b9d..2b26e3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -54,7 +54,6 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.ShadeViewStateProvider;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.disableflags.DisableStateTracker;
@@ -133,9 +132,6 @@
private View mSystemIconsContainer;
private final StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory;
- // TODO(b/273443374): remove
- private NotificationMediaManager mNotificationMediaManager;
-
private final ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@Override
@@ -302,7 +298,6 @@
@Main Executor mainExecutor,
@Background Executor backgroundExecutor,
KeyguardLogger logger,
- NotificationMediaManager notificationMediaManager,
StatusOverlayHoverListenerFactory statusOverlayHoverListenerFactory
) {
super(view);
@@ -357,7 +352,6 @@
/* mask2= */ DISABLE2_SYSTEM_ICONS,
this::updateViewState
);
- mNotificationMediaManager = notificationMediaManager;
mStatusOverlayHoverListenerFactory = statusOverlayHoverListenerFactory;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 74182fc..fe001b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -64,6 +64,7 @@
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.model.Edge;
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.ScrimAlpha;
import com.android.systemui.keyguard.shared.model.TransitionState;
@@ -71,6 +72,7 @@
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
@@ -454,23 +456,32 @@
};
// PRIMARY_BOUNCER->GONE
- collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(PRIMARY_BOUNCER, GONE),
+ collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(PRIMARY_BOUNCER, GONE)),
mBouncerToGoneTransition, mMainDispatcher);
collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha(),
mScrimAlphaConsumer, mMainDispatcher);
// ALTERNATE_BOUNCER->GONE
- collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(ALTERNATE_BOUNCER, GONE),
+ collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(ALTERNATE_BOUNCER, Scenes.Gone),
+ Edge.Companion.create(ALTERNATE_BOUNCER, GONE)),
mBouncerToGoneTransition, mMainDispatcher);
collectFlow(behindScrim, mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha(),
mScrimAlphaConsumer, mMainDispatcher);
// LOCKSCREEN<->GLANCEABLE_HUB
+ collectFlow(
+ behindScrim,
+ mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(LOCKSCREEN, Scenes.Communal),
+ Edge.Companion.create(LOCKSCREEN, GLANCEABLE_HUB)),
+ mGlanceableHubConsumer,
+ mMainDispatcher);
collectFlow(behindScrim,
- mKeyguardTransitionInteractor.transition(LOCKSCREEN, GLANCEABLE_HUB),
- mGlanceableHubConsumer, mMainDispatcher);
- collectFlow(behindScrim,
- mKeyguardTransitionInteractor.transition(GLANCEABLE_HUB, LOCKSCREEN),
+ mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(Scenes.Communal, LOCKSCREEN),
+ Edge.Companion.create(GLANCEABLE_HUB, LOCKSCREEN)),
mGlanceableHubConsumer, mMainDispatcher);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index b715646..fa88be5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -1549,7 +1549,8 @@
}
if (KeyguardWmStateRefactor.isEnabled()) {
- mKeyguardTransitionInteractor.startDismissKeyguardTransition();
+ mKeyguardTransitionInteractor.startDismissKeyguardTransition(
+ "SBKVM#keyguardAuthenticated");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index a6284e3..4505a1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -196,7 +196,22 @@
// The group isn't expanded, let's make sure it's visible!
mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
}
- row.setUserExpanded(true);
+
+ if (android.app.Flags.compactHeadsUpNotificationReply()
+ && row.isCompactConversationHeadsUpOnScreen()) {
+ // Notification can be system expanded true and it is set user expanded in
+ // activateRemoteInput. notifyHeightChanged also doesn't work as visibleType doesn't
+ // change. To expand huning notification properly, we need set userExpanded false.
+ if (!row.isPinned() && row.isExpanded()) {
+ row.setUserExpanded(false);
+ }
+ // expand notification emits expanded information to HUN listener.
+ row.expandNotification();
+ } else {
+ // Note: Since Normal HUN has remote input view in it, we don't expect to hit
+ // onMakeExpandedVisibleForRemoteInput from activateRemoteInput for Normal HUN.
+ row.setUserExpanded(true);
+ }
row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
index cc87e8a..0a6e95e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
@@ -19,6 +19,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
@@ -81,12 +82,12 @@
) : CollapsedStatusBarViewModel {
override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> =
keyguardTransitionInteractor
- .isInTransition(LOCKSCREEN, OCCLUDED)
+ .isInTransition(Edge.create(from = LOCKSCREEN, to = OCCLUDED))
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), initialValue = false)
override val transitionFromLockscreenToDreamStartedEvent: Flow<Unit> =
keyguardTransitionInteractor
- .transition(LOCKSCREEN, DREAMING)
+ .transition(Edge.create(from = LOCKSCREEN, to = DREAMING))
.filter { it.transitionState == TransitionState.STARTED }
.map {}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
index 155102c9..3696108 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
@@ -27,6 +27,8 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactoryImpl
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaControllerInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaControllerInteractorImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -41,6 +43,11 @@
impl: LocalMediaRepositoryFactoryImpl
): LocalMediaRepositoryFactory
+ @Binds
+ fun bindMediaControllerInteractor(
+ impl: MediaControllerInteractorImpl
+ ): MediaControllerInteractor
+
companion object {
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt
new file mode 100644
index 0000000..4812765
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import android.media.MediaMetadata
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.media.session.PlaybackState
+import android.os.Bundle
+import android.os.Handler
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel
+import javax.inject.Inject
+import kotlinx.coroutines.channels.ProducerScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+
+interface MediaControllerInteractor {
+
+ /** [MediaController.Callback] flow representation. */
+ fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel>
+}
+
+@SysUISingleton
+class MediaControllerInteractorImpl
+@Inject
+constructor(
+ @Background private val backgroundHandler: Handler,
+) : MediaControllerInteractor {
+
+ override fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel> {
+ return conflatedCallbackFlow {
+ val callback = MediaControllerCallbackProducer(this)
+ mediaController.registerCallback(callback, backgroundHandler)
+ awaitClose { mediaController.unregisterCallback(callback) }
+ }
+ }
+}
+
+private class MediaControllerCallbackProducer(
+ private val producingScope: ProducerScope<MediaControllerChangeModel>
+) : MediaController.Callback() {
+
+ override fun onSessionDestroyed() {
+ send(MediaControllerChangeModel.SessionDestroyed)
+ }
+
+ override fun onSessionEvent(event: String, extras: Bundle?) {
+ send(MediaControllerChangeModel.SessionEvent(event, extras))
+ }
+
+ override fun onPlaybackStateChanged(state: PlaybackState?) {
+ send(MediaControllerChangeModel.PlaybackStateChanged(state))
+ }
+
+ override fun onMetadataChanged(metadata: MediaMetadata?) {
+ send(MediaControllerChangeModel.MetadataChanged(metadata))
+ }
+
+ override fun onQueueChanged(queue: MutableList<MediaSession.QueueItem>?) {
+ send(MediaControllerChangeModel.QueueChanged(queue))
+ }
+
+ override fun onQueueTitleChanged(title: CharSequence?) {
+ send(MediaControllerChangeModel.QueueTitleChanged(title))
+ }
+
+ override fun onExtrasChanged(extras: Bundle?) {
+ send(MediaControllerChangeModel.ExtrasChanged(extras))
+ }
+
+ override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
+ send(MediaControllerChangeModel.AudioInfoChanged(info))
+ }
+
+ private fun send(change: MediaControllerChangeModel) {
+ producingScope.launch { producingScope.send(change) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
index dc73344..599bd73 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
@@ -18,11 +18,9 @@
import android.media.session.MediaController
import android.media.session.PlaybackState
-import android.os.Handler
-import com.android.settingslib.volume.data.repository.MediaControllerChange
import com.android.settingslib.volume.data.repository.MediaControllerRepository
-import com.android.settingslib.volume.data.repository.stateChanges
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import javax.inject.Inject
@@ -45,38 +43,39 @@
@Inject
constructor(
@Background private val backgroundCoroutineContext: CoroutineContext,
- @Background private val backgroundHandler: Handler,
+ private val mediaControllerInteractor: MediaControllerInteractor,
private val mediaControllerRepository: MediaControllerRepository,
) {
/** [PlaybackState] changes for the [MediaDeviceSession]. */
fun playbackState(session: MediaDeviceSession): Flow<PlaybackState?> {
return stateChanges(session) {
- emit(MediaControllerChange.PlaybackStateChanged(it.playbackState))
+ emit(MediaControllerChangeModel.PlaybackStateChanged(it.playbackState))
}
- .filterIsInstance(MediaControllerChange.PlaybackStateChanged::class)
+ .filterIsInstance(MediaControllerChangeModel.PlaybackStateChanged::class)
.map { it.state }
}
/** [MediaController.PlaybackInfo] changes for the [MediaDeviceSession]. */
fun playbackInfo(session: MediaDeviceSession): Flow<MediaController.PlaybackInfo?> {
return stateChanges(session) {
- emit(MediaControllerChange.AudioInfoChanged(it.playbackInfo))
+ emit(MediaControllerChangeModel.AudioInfoChanged(it.playbackInfo))
}
- .filterIsInstance(MediaControllerChange.AudioInfoChanged::class)
+ .filterIsInstance(MediaControllerChangeModel.AudioInfoChanged::class)
.map { it.info }
}
private fun stateChanges(
session: MediaDeviceSession,
- onStart: suspend FlowCollector<MediaControllerChange>.(controller: MediaController) -> Unit,
- ): Flow<MediaControllerChange?> =
+ onStart:
+ suspend FlowCollector<MediaControllerChangeModel>.(controller: MediaController) -> Unit,
+ ): Flow<MediaControllerChangeModel?> =
mediaControllerRepository.activeSessions
.flatMapLatest { controllers ->
val controller: MediaController =
findControllerForSession(controllers, session)
?: return@flatMapLatest flowOf(null)
- controller.stateChanges(backgroundHandler).onStart { onStart(controller) }
+ mediaControllerInteractor.stateChanges(controller).onStart { onStart(controller) }
}
.flowOn(backgroundCoroutineContext)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
index b00829e..9fbd79a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -19,12 +19,10 @@
import android.content.pm.PackageManager
import android.media.VolumeProvider
import android.media.session.MediaController
-import android.os.Handler
import android.util.Log
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.repository.LocalMediaRepository
import com.android.settingslib.volume.data.repository.MediaControllerRepository
-import com.android.settingslib.volume.data.repository.stateChanges
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSessions
@@ -61,7 +59,7 @@
@VolumePanelScope private val coroutineScope: CoroutineScope,
@Background private val backgroundCoroutineContext: CoroutineContext,
mediaControllerRepository: MediaControllerRepository,
- @Background private val backgroundHandler: Handler,
+ private val mediaControllerInteractor: MediaControllerInteractor,
) {
private val activeMediaControllers: Flow<MediaControllers> =
@@ -194,7 +192,10 @@
return flowOf(null)
}
- return stateChanges(backgroundHandler).map { this }.onStart { emit(this@stateChanges) }
+ return mediaControllerInteractor
+ .stateChanges(this)
+ .map { this }
+ .onStart { emit(this@stateChanges) }
}
private data class MediaControllers(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaControllerChangeModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaControllerChangeModel.kt
new file mode 100644
index 0000000..ef5a44a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaControllerChangeModel.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.model
+
+import android.media.MediaMetadata
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.media.session.PlaybackState
+import android.os.Bundle
+
+/** Models particular change event received by [MediaController.Callback]. */
+sealed interface MediaControllerChangeModel {
+
+ data object SessionDestroyed : MediaControllerChangeModel
+
+ data class SessionEvent(val event: String, val extras: Bundle?) : MediaControllerChangeModel
+
+ data class PlaybackStateChanged(val state: PlaybackState?) : MediaControllerChangeModel
+
+ data class MetadataChanged(val metadata: MediaMetadata?) : MediaControllerChangeModel
+
+ data class QueueChanged(val queue: MutableList<MediaSession.QueueItem>?) :
+ MediaControllerChangeModel
+
+ data class QueueTitleChanged(val title: CharSequence?) : MediaControllerChangeModel
+
+ data class ExtrasChanged(val extras: Bundle?) : MediaControllerChangeModel
+
+ data class AudioInfoChanged(val info: MediaController.PlaybackInfo?) :
+ MediaControllerChangeModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 1568e8c0..2e29bbd 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -20,6 +20,7 @@
import static android.app.WallpaperManager.FLAG_SYSTEM;
import static android.app.WallpaperManager.SetWallpaperFlags;
+import static com.android.systemui.Flags.fixImageWallpaperCrashSurfaceAlreadyReleased;
import static com.android.window.flags.Flags.offloadColorExtraction;
import android.annotation.Nullable;
@@ -128,8 +129,17 @@
* and if the count is 0, unload the bitmap
*/
private int mBitmapUsages = 0;
+
+ /**
+ * Main lock for long operations (loading the bitmap or processing colors).
+ */
private final Object mLock = new Object();
+ /**
+ * Lock for SurfaceHolder operations. Should only be acquired after the main lock.
+ */
+ private final Object mSurfaceLock = new Object();
+
CanvasEngine() {
super();
setFixedSizeAllowed(true);
@@ -223,6 +233,12 @@
if (DEBUG) {
Log.i(TAG, "onSurfaceDestroyed");
}
+ if (fixImageWallpaperCrashSurfaceAlreadyReleased()) {
+ synchronized (mSurfaceLock) {
+ mSurfaceHolder = null;
+ }
+ return;
+ }
mLongExecutor.execute(this::onSurfaceDestroyedSynchronized);
}
@@ -259,7 +275,7 @@
}
private void drawFrameInternal() {
- if (mSurfaceHolder == null) {
+ if (mSurfaceHolder == null && !fixImageWallpaperCrashSurfaceAlreadyReleased()) {
Log.i(TAG, "attempt to draw a frame without a valid surface");
return;
}
@@ -268,6 +284,19 @@
if (!isBitmapLoaded()) {
loadWallpaperAndDrawFrameInternal();
} else {
+ if (fixImageWallpaperCrashSurfaceAlreadyReleased()) {
+ synchronized (mSurfaceLock) {
+ if (mSurfaceHolder == null) {
+ Log.i(TAG, "Surface released before the image could be drawn");
+ return;
+ }
+ mBitmapUsages++;
+ drawFrameOnCanvas(mBitmap);
+ reportEngineShown(false);
+ unloadBitmapIfNotUsedInternal();
+ return;
+ }
+ }
mBitmapUsages++;
drawFrameOnCanvas(mBitmap);
reportEngineShown(false);
@@ -328,9 +357,14 @@
mBitmap.recycle();
}
mBitmap = null;
-
- final Surface surface = getSurfaceHolder().getSurface();
- surface.hwuiDestroy();
+ if (fixImageWallpaperCrashSurfaceAlreadyReleased()) {
+ synchronized (mSurfaceLock) {
+ if (mSurfaceHolder != null) mSurfaceHolder.getSurface().hwuiDestroy();
+ }
+ } else {
+ final Surface surface = getSurfaceHolder().getSurface();
+ surface.hwuiDestroy();
+ }
mWallpaperManager.forgetLoadedWallpaper();
Trace.endSection();
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 6f550ba..5702a8c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -21,14 +21,18 @@
import android.view.ViewTreeObserver
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.log.core.LogLevel
@@ -68,8 +72,9 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import com.android.systemui.Flags as AConfigFlags
+import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -319,26 +324,16 @@
fun listenForDozeAmountTransition_updatesClockDozeAmount() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(
- keyguardTransitionInteractor.transition(
- KeyguardState.LOCKSCREEN,
- KeyguardState.AOD
- )
- )
+ whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, AOD)))
.thenReturn(transitionStep)
- whenever(
- keyguardTransitionInteractor.transition(
- KeyguardState.AOD,
- KeyguardState.LOCKSCREEN
- )
- )
+ whenever(keyguardTransitionInteractor.transition(Edge.create(AOD, LOCKSCREEN)))
.thenReturn(transitionStep)
val job = underTest.listenForDozeAmountTransition(this)
transitionStep.value =
TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
+ from = LOCKSCREEN,
+ to = AOD,
value = 0.4f,
transitionState = TransitionState.RUNNING,
)
@@ -353,14 +348,14 @@
fun listenForTransitionToAodFromGone_updatesClockDozeAmountToOne() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD))
+ whenever(keyguardTransitionInteractor.transitionStepsToState(AOD))
.thenReturn(transitionStep)
val job = underTest.listenForAnyStateToAodTransition(this)
transitionStep.value =
TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
+ from = GONE,
+ to = AOD,
transitionState = TransitionState.STARTED,
)
yield()
@@ -374,16 +369,16 @@
fun listenForTransitionToLSFromOccluded_updatesClockDozeAmountToZero() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.LOCKSCREEN))
- .thenReturn(transitionStep)
+ whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN))
+ .thenReturn(transitionStep)
val job = underTest.listenForAnyStateToLockscreenTransition(this)
transitionStep.value =
- TransitionStep(
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.LOCKSCREEN,
- transitionState = TransitionState.STARTED,
- )
+ TransitionStep(
+ from = OCCLUDED,
+ to = LOCKSCREEN,
+ transitionState = TransitionState.STARTED,
+ )
yield()
verify(animations, times(2)).doze(0f)
@@ -395,37 +390,37 @@
fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD))
+ whenever(keyguardTransitionInteractor.transitionStepsToState(AOD))
.thenReturn(transitionStep)
val job = underTest.listenForAnyStateToAodTransition(this)
transitionStep.value =
TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
+ from = LOCKSCREEN,
+ to = AOD,
transitionState = TransitionState.STARTED,
)
yield()
verify(animations, never()).doze(1f)
- job.cancel()
- }
+ job.cancel()
+ }
@Test
fun listenForAnyStateToLockscreenTransition_neverUpdatesClockDozeAmount() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.LOCKSCREEN))
- .thenReturn(transitionStep)
+ whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN))
+ .thenReturn(transitionStep)
val job = underTest.listenForAnyStateToLockscreenTransition(this)
transitionStep.value =
- TransitionStep(
- from = KeyguardState.AOD,
- to = KeyguardState.LOCKSCREEN,
- transitionState = TransitionState.STARTED,
- )
+ TransitionStep(
+ from = AOD,
+ to = LOCKSCREEN,
+ transitionState = TransitionState.STARTED,
+ )
yield()
verify(animations, never()).doze(0f)
@@ -437,16 +432,16 @@
fun listenForAnyStateToDozingTransition_UpdatesClockDozeAmountToOne() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.DOZING))
- .thenReturn(transitionStep)
+ whenever(keyguardTransitionInteractor.transitionStepsToState(DOZING))
+ .thenReturn(transitionStep)
val job = underTest.listenForAnyStateToDozingTransition(this)
transitionStep.value =
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.DOZING,
- transitionState = TransitionState.STARTED,
- )
+ TransitionStep(
+ from = LOCKSCREEN,
+ to = DOZING,
+ transitionState = TransitionState.STARTED,
+ )
yield()
verify(animations, times(2)).doze(1f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
index 8653308..44a8904 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
@@ -26,6 +26,8 @@
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.model.sysUiState
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -47,7 +49,7 @@
private val testScope = kosmos.testScope
private val testHelper = kosmos.shortcutHelperTestHelper
-
+ private val sysUiState = kosmos.sysUiState
private val viewModel = kosmos.shortcutHelperViewModel
@Test
@@ -90,12 +92,12 @@
}
@Test
- fun shouldShow_falseAfterViewDestroyed() =
+ fun shouldShow_falseAfterViewClosed() =
testScope.runTest {
val shouldShow by collectLastValue(viewModel.shouldShow)
testHelper.toggle(deviceId = 567)
- viewModel.onUserLeave()
+ viewModel.onViewClosed()
assertThat(shouldShow).isFalse()
}
@@ -108,7 +110,7 @@
testHelper.hideForSystem()
testHelper.toggle(deviceId = 987)
testHelper.showFromActivity()
- viewModel.onUserLeave()
+ viewModel.onViewClosed()
testHelper.hideFromActivity()
testHelper.hideForSystem()
testHelper.toggle(deviceId = 456)
@@ -127,4 +129,27 @@
val shouldShowNew by collectLastValue(viewModel.shouldShow)
assertThat(shouldShowNew).isEqualTo(shouldShow)
}
+
+ @Test
+ fun sysUiStateFlag_disabledByDefault() =
+ testScope.runTest {
+ assertThat(sysUiState.isFlagEnabled(SYSUI_STATE_SHORTCUT_HELPER_SHOWING)).isFalse()
+ }
+
+ @Test
+ fun sysUiStateFlag_trueAfterViewOpened() =
+ testScope.runTest {
+ viewModel.onViewOpened()
+
+ assertThat(sysUiState.isFlagEnabled(SYSUI_STATE_SHORTCUT_HELPER_SHOWING)).isTrue()
+ }
+
+ @Test
+ fun sysUiStateFlag_falseAfterViewClosed() =
+ testScope.runTest {
+ viewModel.onViewOpened()
+ viewModel.onViewClosed()
+
+ assertThat(sysUiState.isFlagEnabled(SYSUI_STATE_SHORTCUT_HELPER_SHOWING)).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index b50d248..7afed9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -19,6 +19,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.utils.GlobalWindowManager
@@ -74,7 +75,7 @@
globalWindowManager,
testScope.backgroundScope,
kosmos.testDispatcher,
- featureFlags
+ featureFlags,
)
resourceTrimmer.start()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 9b2db3e..1f13298 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -21,6 +21,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
@@ -41,10 +42,9 @@
private lateinit var executor: FakeExecutor
@Mock private lateinit var activityTaskManagerService: IActivityTaskManager
-
@Mock private lateinit var keyguardStateController: KeyguardStateController
-
@Mock private lateinit var keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier
+ @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Before
fun setUp() {
@@ -57,6 +57,7 @@
activityTaskManagerService = activityTaskManagerService,
keyguardStateController = keyguardStateController,
keyguardSurfaceBehindAnimator = keyguardSurfaceBehindAnimator,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
index 1396b20..391831a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
@@ -18,15 +18,19 @@
package com.android.systemui.keyguard.ui.viewmodel
import androidx.test.filters.SmallTest
+import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -34,6 +38,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import org.mockito.kotlin.whenever
@ExperimentalCoroutinesApi
@RunWith(JUnit4::class)
@@ -49,13 +54,35 @@
fun alternateBouncerTransition_alternateBouncerWindowRequiredTrue() =
testScope.runTest {
mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ val canShowAlternateBouncer by collectLastValue(underTest.canShowAlternateBouncer)
val alternateBouncerWindowRequired by
collectLastValue(underTest.alternateBouncerWindowRequired)
+ givenCanShowAlternateBouncer()
fingerprintPropertyRepository.supportsUdfps()
transitionRepository.sendTransitionSteps(
listOf(
+ stepToLockscreen(0f, TransitionState.STARTED),
+ stepToLockscreen(.4f),
+ stepToLockscreen(1f, TransitionState.FINISHED),
+ ),
+ testScope,
+ )
+ assertThat(canShowAlternateBouncer).isTrue()
+ transitionRepository.sendTransitionSteps(
+ listOf(
+ stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED),
+ stepFromLockscreenToAlternateBouncer(.4f),
+ stepFromLockscreenToAlternateBouncer(.6f),
+ ),
+ testScope,
+ )
+ assertThat(canShowAlternateBouncer).isTrue()
+ assertThat(alternateBouncerWindowRequired).isTrue()
+
+ transitionRepository.sendTransitionSteps(
+ listOf(
stepFromAlternateBouncer(0f, TransitionState.STARTED),
- stepFromAlternateBouncer(.4f),
+ stepFromAlternateBouncer(.2f),
stepFromAlternateBouncer(.6f),
),
testScope,
@@ -77,13 +104,21 @@
mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val alternateBouncerWindowRequired by
collectLastValue(underTest.alternateBouncerWindowRequired)
+ givenCanShowAlternateBouncer()
fingerprintPropertyRepository.supportsUdfps()
transitionRepository.sendTransitionSteps(
listOf(
- stepFromAlternateBouncer(0f, TransitionState.STARTED),
- stepFromAlternateBouncer(.4f),
- stepFromAlternateBouncer(.6f),
- stepFromAlternateBouncer(1f),
+ stepToLockscreen(0f, TransitionState.STARTED),
+ stepToLockscreen(.4f),
+ stepToLockscreen(1f, TransitionState.FINISHED),
+ ),
+ testScope,
+ )
+ transitionRepository.sendTransitionSteps(
+ listOf(
+ stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED),
+ stepFromLockscreenToAlternateBouncer(.4f),
+ stepFromLockscreenToAlternateBouncer(.6f),
),
testScope,
)
@@ -96,13 +131,23 @@
mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val alternateBouncerWindowRequired by
collectLastValue(underTest.alternateBouncerWindowRequired)
+ givenCanShowAlternateBouncer()
fingerprintPropertyRepository.supportsUdfps()
transitionRepository.sendTransitionSteps(
listOf(
+ stepFromLockscreenToDozing(0f, TransitionState.STARTED),
+ stepFromLockscreenToDozing(.4f),
+ stepFromLockscreenToDozing(.6f),
+ stepFromLockscreenToDozing(1f, TransitionState.FINISHED),
+ ),
+ testScope,
+ )
+ assertThat(alternateBouncerWindowRequired).isFalse()
+ transitionRepository.sendTransitionSteps(
+ listOf(
stepFromDozingToLockscreen(0f, TransitionState.STARTED),
stepFromDozingToLockscreen(.4f),
stepFromDozingToLockscreen(.6f),
- stepFromDozingToLockscreen(1f),
),
testScope,
)
@@ -115,19 +160,39 @@
mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val alternateBouncerWindowRequired by
collectLastValue(underTest.alternateBouncerWindowRequired)
+ givenCanShowAlternateBouncer()
fingerprintPropertyRepository.supportsRearFps()
transitionRepository.sendTransitionSteps(
listOf(
- stepFromAlternateBouncer(0f, TransitionState.STARTED),
- stepFromAlternateBouncer(.4f),
- stepFromAlternateBouncer(.6f),
- stepFromAlternateBouncer(1f),
+ stepToLockscreen(0f, TransitionState.STARTED),
+ stepToLockscreen(.4f),
+ stepToLockscreen(1f, TransitionState.FINISHED),
+ ),
+ testScope,
+ )
+ transitionRepository.sendTransitionSteps(
+ listOf(
+ stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED),
+ stepFromLockscreenToAlternateBouncer(.4f),
+ stepFromLockscreenToAlternateBouncer(.6f),
),
testScope,
)
assertThat(alternateBouncerWindowRequired).isFalse()
}
+ private fun stepToLockscreen(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return step(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ value = value,
+ transitionState = state,
+ )
+ }
+
private fun stepFromAlternateBouncer(
value: Float,
state: TransitionState = TransitionState.RUNNING
@@ -140,6 +205,18 @@
)
}
+ private fun stepFromLockscreenToAlternateBouncer(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return step(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ value = value,
+ transitionState = state,
+ )
+ }
+
private fun stepFromDozingToLockscreen(
value: Float,
state: TransitionState = TransitionState.RUNNING
@@ -152,6 +229,18 @@
)
}
+ private fun stepFromLockscreenToDozing(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return step(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DOZING,
+ value = value,
+ transitionState = state,
+ )
+ }
+
private fun step(
from: KeyguardState,
to: KeyguardState,
@@ -166,4 +255,16 @@
ownerName = "AlternateBouncerViewModelTest"
)
}
+
+ /**
+ * Given the alternate bouncer parameters are set so that the alternate bouncer can show, aside
+ * from the fingerprint modality.
+ */
+ private fun givenCanShowAlternateBouncer() {
+ kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(false)
+ kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+ whenever(kosmos.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
+ whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
index 265ade3..5986f4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
@@ -33,9 +33,6 @@
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
@@ -50,6 +47,9 @@
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
private const val KEY = "TEST_KEY"
private const val KEY_ALT = "TEST_KEY_2"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
index 99bf2db..3372f06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
@@ -66,9 +66,6 @@
import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.After
@@ -90,6 +87,9 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoSession
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.eq
import org.mockito.quality.Strictness
private const val KEY = "KEY"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index 35eefd9..caaa42f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -40,9 +40,6 @@
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
@@ -60,6 +57,9 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
private const val KEY = "TEST_KEY"
private const val KEY_ALT = "TEST_KEY_2"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 5791826..3bf4173 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -71,10 +71,6 @@
import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.utils.os.FakeHandler
@@ -101,6 +97,10 @@
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoSession
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
private const val KEY = "KEY"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index befe64c..d2701dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -51,7 +51,6 @@
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.After
@@ -72,6 +71,7 @@
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.eq
private const val KEY = "TEST_KEY"
private const val KEY_OLD = "TEST_KEY_OLD"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
index 030bca2..31a2435 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
@@ -27,7 +27,6 @@
import com.android.systemui.media.controls.MediaTestUtils
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import org.junit.After
import org.junit.Before
@@ -38,12 +37,13 @@
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.any
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
private const val PACKAGE = "PKG"
private const val KEY = "TEST_KEY"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
index cdbf9d7..6ca0bef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
@@ -31,10 +31,6 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -52,6 +48,10 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
private const val KEY = "KEY"
private const val PACKAGE = "PKG"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index 3bb8b8f..05c882e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -54,15 +54,13 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PageIndicator
import com.android.systemui.res.R
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.GlobalSettings
import com.android.systemui.util.settings.SecureSettings
@@ -92,6 +90,9 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.eq
private val DATA = MediaTestUtils.emptyMediaData
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index 0c9fee9..6d7976e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -98,11 +98,6 @@
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.KotlinArgumentCaptor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.GlobalSettings
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -125,6 +120,9 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
private const val KEY = "TEST_KEY"
private const val PACKAGE = "PKG"
@@ -247,8 +245,7 @@
// Set up package manager mocks
val icon = context.getDrawable(R.drawable.ic_android)
whenever(packageManager.getApplicationIcon(anyString())).thenReturn(icon)
- whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java)))
- .thenReturn(icon)
+ whenever(packageManager.getApplicationIcon(any<ApplicationInfo>())).thenReturn(icon)
whenever(packageManager.getApplicationInfo(eq(PACKAGE), anyInt()))
.thenReturn(applicationInfo)
whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE)
@@ -644,7 +641,7 @@
bgExecutor.runAllReady()
mainExecutor.runAllReady()
- verify(albumView).setImageDrawable(any(Drawable::class.java))
+ verify(albumView).setImageDrawable(any<Drawable>())
}
@Test
@@ -657,7 +654,7 @@
bgExecutor.runAllReady()
mainExecutor.runAllReady()
- verify(albumView).setImageDrawable(any(Drawable::class.java))
+ verify(albumView).setImageDrawable(any<Drawable>())
}
@Test
@@ -675,12 +672,12 @@
player.bindPlayer(state0, PACKAGE)
bgExecutor.runAllReady()
mainExecutor.runAllReady()
- verify(albumView).setImageDrawable(any(Drawable::class.java))
+ verify(albumView).setImageDrawable(any<Drawable>())
// Run Metadata update so that later states don't update
val captor = argumentCaptor<Animator.AnimatorListener>()
verify(mockAnimator, times(2)).addListener(captor.capture())
- captor.value.onAnimationEnd(mockAnimator)
+ captor.lastValue.onAnimationEnd(mockAnimator)
assertThat(titleText.getText()).isEqualTo(TITLE)
assertThat(artistText.getText()).isEqualTo(ARTIST)
@@ -696,13 +693,13 @@
player.bindPlayer(state2, PACKAGE)
bgExecutor.runAllReady()
mainExecutor.runAllReady()
- verify(albumView, times(2)).setImageDrawable(any(Drawable::class.java))
+ verify(albumView, times(2)).setImageDrawable(any<Drawable>())
// Fourth binding to new image runs transition due to color scheme change
player.bindPlayer(state3, PACKAGE)
bgExecutor.runAllReady()
mainExecutor.runAllReady()
- verify(albumView, times(3)).setImageDrawable(any(Drawable::class.java))
+ verify(albumView, times(3)).setImageDrawable(any<Drawable>())
}
@Test
@@ -974,7 +971,7 @@
val captor = argumentCaptor<SeekBarObserver>()
verify(seekBarData).observeForever(captor.capture())
- val seekBarObserver = captor.value!!
+ val seekBarObserver = captor.lastValue
// Then the seekbar is set to animate
assertThat(seekBarObserver.animationEnabled).isTrue()
@@ -1086,27 +1083,19 @@
whenever(mockAvd0.isRunning()).thenReturn(false)
val captor = ArgumentCaptor.forClass(Animatable2.AnimationCallback::class.java)
verify(mockAvd0, times(1)).registerAnimationCallback(captor.capture())
- verify(mockAvd1, never())
- .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
- verify(mockAvd2, never())
- .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd1, never()).registerAnimationCallback(any<Animatable2.AnimationCallback>())
+ verify(mockAvd2, never()).registerAnimationCallback(any<Animatable2.AnimationCallback>())
captor.getValue().onAnimationEnd(mockAvd0)
// Validate correct state was bound
assertThat(actionPlayPause.contentDescription).isEqualTo("loading")
assertThat(actionPlayPause.getBackground()).isNull()
- verify(mockAvd0, times(1))
- .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
- verify(mockAvd1, times(1))
- .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
- verify(mockAvd2, times(1))
- .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
- verify(mockAvd0, times(1))
- .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java))
- verify(mockAvd1, times(1))
- .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java))
- verify(mockAvd2, never())
- .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd0, times(1)).registerAnimationCallback(any<Animatable2.AnimationCallback>())
+ verify(mockAvd1, times(1)).registerAnimationCallback(any<Animatable2.AnimationCallback>())
+ verify(mockAvd2, times(1)).registerAnimationCallback(any<Animatable2.AnimationCallback>())
+ verify(mockAvd0, times(1)).unregisterAnimationCallback(any<Animatable2.AnimationCallback>())
+ verify(mockAvd1, times(1)).unregisterAnimationCallback(any<Animatable2.AnimationCallback>())
+ verify(mockAvd2, never()).unregisterAnimationCallback(any<Animatable2.AnimationCallback>())
}
@Test
@@ -1118,7 +1107,7 @@
// Capture animation handler
val captor = argumentCaptor<Animator.AnimatorListener>()
verify(mockAnimator, times(2)).addListener(captor.capture())
- val handler = captor.value
+ val handler = captor.lastValue
// Validate text views unchanged but animation started
assertThat(titleText.getText()).isEqualTo("")
@@ -1147,7 +1136,7 @@
// Capture animation handler
val captor = argumentCaptor<Animator.AnimatorListener>()
verify(mockAnimator, times(2)).addListener(captor.capture())
- val handler = captor.value
+ val handler = captor.lastValue
// Validate text views unchanged but animation started
assertThat(titleText.getText()).isEqualTo("")
@@ -1179,7 +1168,7 @@
// Capture animation handler
val captor = argumentCaptor<Animator.AnimatorListener>()
verify(mockAnimator, times(2)).addListener(captor.capture())
- val handler = captor.value
+ val handler = captor.lastValue
handler.onAnimationEnd(mockAnimator)
assertThat(artistText.getText()).isEqualTo("ARTIST_0")
@@ -1775,10 +1764,9 @@
player.attachPlayer(viewHolder)
player.bindPlayer(mediaData, KEY)
- val callback: () -> Unit = {}
- val captor = KotlinArgumentCaptor(callback::class.java)
+ val captor = argumentCaptor<() -> Unit>()
verify(seekBarViewModel).logSeek = captor.capture()
- captor.value.invoke()
+ captor.lastValue.invoke()
verify(logger).logSeek(anyInt(), eq(PACKAGE), eq(instanceId))
}
@@ -1801,7 +1789,7 @@
// THEN it sends the PendingIntent without dismissing keyguard first,
// and does not use the Intent directly (see b/271845008)
captor.value.onClick(viewHolder.player)
- verify(pendingIntent).send(any(Bundle::class.java))
+ verify(pendingIntent).send(any<Bundle>())
verify(pendingIntent, never()).getIntent()
verify(activityStarter, never()).postStartActivityDismissingKeyguard(eq(clickIntent), any())
}
@@ -2219,8 +2207,8 @@
mainExecutor.runAllReady()
verify(recCardTitle).setTextColor(any<Int>())
- verify(recAppIconItem, times(3)).setImageDrawable(any(Drawable::class.java))
- verify(coverItem, times(3)).setImageDrawable(any(Drawable::class.java))
+ verify(recAppIconItem, times(3)).setImageDrawable(any<Drawable>())
+ verify(coverItem, times(3)).setImageDrawable(any<Drawable>())
verify(coverItem, times(3)).imageMatrix = any()
}
@@ -2547,7 +2535,7 @@
seamless.callOnClick()
// Then we send the pending intent as is, without modifying the original intent
- verify(pendingIntent).send(any(Bundle::class.java))
+ verify(pendingIntent).send(any<Bundle>())
verify(pendingIntent, never()).getIntent()
}
@@ -2579,13 +2567,16 @@
return Icon.createWithBitmap(bmp)
}
- private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener =
- withArgCaptor {
- verify(seekBarViewModel).setScrubbingChangeListener(capture())
- }
+ private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener {
+ val captor = argumentCaptor<SeekBarViewModel.ScrubbingChangeListener>()
+ verify(seekBarViewModel).setScrubbingChangeListener(captor.capture())
+ return captor.lastValue
+ }
- private fun getEnabledChangeListener(): SeekBarViewModel.EnabledChangeListener = withArgCaptor {
- verify(seekBarViewModel).setEnabledChangeListener(capture())
+ private fun getEnabledChangeListener(): SeekBarViewModel.EnabledChangeListener {
+ val captor = argumentCaptor<SeekBarViewModel.EnabledChangeListener>()
+ verify(seekBarViewModel).setEnabledChangeListener(captor.capture())
+ return captor.lastValue
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
index 5e7d8fb..a10d81f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
@@ -21,7 +21,6 @@
import android.os.Bundle
import android.os.UserHandle
import android.testing.AndroidTestingRunner
-import android.view.View
import android.view.Window
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -49,7 +48,7 @@
private val intentExecutor = mock<ActionIntentExecutor>()
private val window = mock<Window>()
- private val view = mock<View>()
+ private val viewProxy = mock<ScreenshotShelfViewProxy>()
private val onDismiss = mock<(() -> Unit)>()
private val pendingIntent = mock<PendingIntent>()
@@ -70,16 +69,16 @@
}
@Test
- fun sendPendingIntent_dismisses() = runTest {
+ fun sendPendingIntent_requestsDismissal() = runTest {
actionExecutor = createActionExecutor()
actionExecutor.sendPendingIntent(pendingIntent)
verify(pendingIntent).send(any(Bundle::class.java))
- verify(onDismiss).invoke()
+ verify(viewProxy).requestDismissal(null)
}
private fun createActionExecutor(): ActionExecutor {
- return ActionExecutor(intentExecutor, testScope, window, view, onDismiss)
+ return ActionExecutor(intentExecutor, testScope, window, viewProxy, onDismiss)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 766113f..8e32907 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -547,6 +547,8 @@
}).when(mView).setOnTouchListener(any(NotificationPanelViewController.TouchHandler.class));
// Dreaming->Lockscreen
+ when(mKeyguardTransitionInteractor.transition(any()))
+ .thenReturn(emptyFlow());
when(mKeyguardTransitionInteractor.transition(any(), any()))
.thenReturn(emptyFlow());
when(mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 45d0102..4a867a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -46,6 +46,7 @@
import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -173,7 +174,7 @@
.thenReturn(keyguardBouncerComponent)
whenever(keyguardBouncerComponent.securityContainerController)
.thenReturn(keyguardSecurityContainerController)
- whenever(keyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING))
+ whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, DREAMING)))
.thenReturn(emptyFlow<TransitionStep>())
featureFlagsClassic = FakeFeatureFlagsClassic()
@@ -518,46 +519,6 @@
}
@Test
- fun handleExternalTouch_intercepted_sendsOnTouch() {
- // Accept dispatch and also intercept.
- whenever(view.dispatchTouchEvent(any())).thenReturn(true)
- whenever(view.onInterceptTouchEvent(any())).thenReturn(true)
-
- underTest.handleExternalTouch(DOWN_EVENT)
- underTest.handleExternalTouch(MOVE_EVENT)
-
- // Once intercepted, both events are sent to the view.
- verify(view).onTouchEvent(DOWN_EVENT)
- verify(view).onTouchEvent(MOVE_EVENT)
- }
-
- @Test
- fun handleExternalTouch_notDispatched_interceptNotCalled() {
- // Don't accept dispatch
- whenever(view.dispatchTouchEvent(any())).thenReturn(false)
-
- underTest.handleExternalTouch(DOWN_EVENT)
-
- // Interception is not offered.
- verify(view, never()).onInterceptTouchEvent(any())
- }
-
- @Test
- fun handleExternalTouch_notIntercepted_onTouchNotSent() {
- // Accept dispatch, but don't dispatch
- whenever(view.dispatchTouchEvent(any())).thenReturn(true)
- whenever(view.onInterceptTouchEvent(any())).thenReturn(false)
-
- underTest.handleExternalTouch(DOWN_EVENT)
- underTest.handleExternalTouch(MOVE_EVENT)
-
- // Interception offered for both events, but onTouchEvent is never called.
- verify(view).onInterceptTouchEvent(DOWN_EVENT)
- verify(view).onInterceptTouchEvent(MOVE_EVENT)
- verify(view, never()).onTouchEvent(any())
- }
-
- @Test
fun testGetKeyguardMessageArea() =
testScope.runTest {
underTest.keyguardMessageArea
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index f380b6c..e83a46b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -36,6 +36,7 @@
import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.res.R
@@ -151,7 +152,7 @@
whenever(statusBarStateController.isDozing).thenReturn(false)
mDependency.injectTestDependency(ShadeController::class.java, shadeController)
whenever(dockManager.isDocked).thenReturn(false)
- whenever(keyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING))
+ whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, DREAMING)))
.thenReturn(emptyFlow())
val featureFlags = FakeFeatureFlags()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
index 2c453a7..dfd7a71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
@@ -21,6 +21,7 @@
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -36,6 +37,8 @@
runCurrent()
assertThat(mQsController.isExpansionEnabled).isFalse()
+
+ coroutineContext.cancelChildren()
}
@Test
@@ -45,6 +48,8 @@
runCurrent()
assertThat(mQsController.isExpansionEnabled).isTrue()
+
+ coroutineContext.cancelChildren()
}
@Test
@@ -58,6 +63,8 @@
runCurrent()
assertThat(mQsController.isExpansionEnabled).isFalse()
+
+ coroutineContext.cancelChildren()
}
@Test
@@ -71,6 +78,8 @@
runCurrent()
assertThat(mQsController.isExpansionEnabled).isFalse()
+
+ coroutineContext.cancelChildren()
}
@Test
@@ -81,5 +90,7 @@
runCurrent()
assertThat(mQsController.isExpansionEnabled).isTrue()
+
+ coroutineContext.cancelChildren()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index dfee737..5b2526e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -71,7 +71,6 @@
import com.android.systemui.shade.ShadeViewStateProvider;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.data.repository.FakeKeyguardStatusBarRepository;
import com.android.systemui.statusbar.domain.interactor.KeyguardStatusBarInteractor;
@@ -144,8 +143,6 @@
@Mock private SecureSettings mSecureSettings;
@Mock private CommandQueue mCommandQueue;
@Mock private KeyguardLogger mLogger;
-
- @Mock private NotificationMediaManager mNotificationMediaManager;
@Mock private StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory;
private TestShadeViewStateProvider mShadeViewStateProvider;
@@ -225,7 +222,6 @@
mFakeExecutor,
mBackgroundExecutor,
mLogger,
- mNotificationMediaManager,
mStatusOverlayHoverListenerFactory
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
index 31848a6..e1dcb14 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
@@ -1,8 +1,8 @@
package com.android.systemui.util
import android.graphics.Rect
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.wm.shell.common.FloatingContentCoordinator
@@ -14,7 +14,7 @@
import org.junit.runner.RunWith
@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class FloatingContentCoordinatorTest : SysuiTestCase() {
@@ -198,12 +198,11 @@
}
/**
- * Helper class that uses [floatingCoordinator.findAreaForContentVertically] to move a
- * Rect when needed.
+ * Helper class that uses [floatingCoordinator.findAreaForContentVertically] to move a Rect when
+ * needed.
*/
- inner class FloatingRect(
- private val underlyingRect: Rect
- ) : FloatingContentCoordinator.FloatingContent {
+ inner class FloatingRect(private val underlyingRect: Rect) :
+ FloatingContentCoordinator.FloatingContent {
override fun moveToBounds(bounds: Rect) {
underlyingRect.set(bounds)
}
@@ -216,4 +215,4 @@
return underlyingRect
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
index 436f5b8..457f2bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
@@ -19,12 +19,13 @@
import android.content.BroadcastReceiver
import android.content.IntentFilter
import android.os.UserHandle
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.lifecycle.Observer
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
+import java.util.concurrent.Executor
import org.junit.After
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -38,10 +39,9 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class RingerModeLiveDataTest : SysuiTestCase() {
@@ -52,16 +52,11 @@
private val INTENT = "INTENT"
}
- @Mock
- private lateinit var broadcastDispatcher: BroadcastDispatcher
- @Mock
- private lateinit var valueSupplier: () -> Int
- @Mock
- private lateinit var observer: Observer<Int>
- @Captor
- private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver>
- @Captor
- private lateinit var intentFilterCaptor: ArgumentCaptor<IntentFilter>
+ @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock private lateinit var valueSupplier: () -> Int
+ @Mock private lateinit var observer: Observer<Int>
+ @Captor private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver>
+ @Captor private lateinit var intentFilterCaptor: ArgumentCaptor<IntentFilter>
// Run everything immediately
private val executor = Executor { it.run() }
@@ -88,14 +83,14 @@
fun testOnActive_broadcastRegistered() {
liveData.observeForever(observer)
verify(broadcastDispatcher)
- .registerReceiver(any(), any(), eq(executor), eq(UserHandle.ALL), anyInt(), any())
+ .registerReceiver(any(), any(), eq(executor), eq(UserHandle.ALL), anyInt(), any())
}
@Test
fun testOnActive_intentFilterHasIntent() {
liveData.observeForever(observer)
- verify(broadcastDispatcher).registerReceiver(any(), capture(intentFilterCaptor), any(),
- any(), anyInt(), any())
+ verify(broadcastDispatcher)
+ .registerReceiver(any(), capture(intentFilterCaptor), any(), any(), anyInt(), any())
assertTrue(intentFilterCaptor.value.hasAction(INTENT))
}
@@ -111,4 +106,4 @@
liveData.removeObserver(observer)
verify(broadcastDispatcher).unregisterReceiver(any())
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
index b13cb72..6271904 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
@@ -19,10 +19,10 @@
import android.app.WallpaperInfo
import android.app.WallpaperManager
import android.os.IBinder
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.View
import android.view.ViewRootImpl
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.eq
@@ -32,36 +32,30 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.doThrow
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
+import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
@SmallTest
class WallpaperControllerTest : SysuiTestCase() {
- @Mock
- private lateinit var wallpaperManager: WallpaperManager
- @Mock
- private lateinit var root: View
- @Mock
- private lateinit var viewRootImpl: ViewRootImpl
- @Mock
- private lateinit var windowToken: IBinder
+ @Mock private lateinit var wallpaperManager: WallpaperManager
+ @Mock private lateinit var root: View
+ @Mock private lateinit var viewRootImpl: ViewRootImpl
+ @Mock private lateinit var windowToken: IBinder
private val wallpaperRepository = FakeWallpaperRepository()
- @JvmField
- @Rule
- val mockitoRule = MockitoJUnit.rule()
+ @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
private lateinit var wallaperController: WallpaperController
@@ -92,9 +86,7 @@
@Test
fun setUnfoldTransitionZoom_defaultUnfoldTransitionIsDisabled_doesNotUpdateWallpaperZoom() {
- wallpaperRepository.wallpaperInfo.value = createWallpaperInfo(
- useDefaultTransition = false
- )
+ wallpaperRepository.wallpaperInfo.value = createWallpaperInfo(useDefaultTransition = false)
wallaperController.setUnfoldTransitionZoom(0.5f)
@@ -130,7 +122,8 @@
@Test
fun setNotificationZoom_exceptionWhenUpdatingZoom_doesNotFail() {
- doThrow(IllegalArgumentException("test exception")).`when`(wallpaperManager)
+ doThrow(IllegalArgumentException("test exception"))
+ .`when`(wallpaperManager)
.setWallpaperZoomOut(any(), anyFloat())
wallaperController.setNotificationShadeZoom(0.5f)
@@ -140,8 +133,7 @@
private fun createWallpaperInfo(useDefaultTransition: Boolean = true): WallpaperInfo {
val info = mock(WallpaperInfo::class.java)
- whenever(info.shouldUseDefaultUnfoldTransition())
- .thenReturn(useDefaultTransition)
+ whenever(info.shouldUseDefaultUnfoldTransition()).thenReturn(useDefaultTransition)
return info
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
index 92afb03..b26598c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
@@ -16,13 +16,16 @@
package com.android.systemui.util.animation
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
-import org.junit.Test
import java.lang.IllegalArgumentException
+import org.junit.runner.RunWith
+import org.junit.Test
@SmallTest
+@RunWith(AndroidJUnit4::class)
class AnimationUtilTest : SysuiTestCase() {
@Test
fun getMsForFrames_5frames_returns83() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
index 9dfa14d..7dfac0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
@@ -22,8 +22,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import android.testing.AndroidTestingRunner;
-
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -39,7 +38,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class FakeExecutorTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
index 78fc680..48fb745 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
@@ -24,8 +24,7 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
-
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -39,7 +38,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class MessageRouterImplTest extends SysuiTestCase {
private static final int MESSAGE_A = 0;
private static final int MESSAGE_B = 1;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt
index 15032dc..7ec420f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt
@@ -15,7 +15,7 @@
*/
package com.android.systemui.util.concurrency
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.time.FakeSystemClock
@@ -26,7 +26,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class MockExecutorHandlerTest : SysuiTestCase() {
/** Test FakeExecutor that receives non-delayed items to execute. */
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java
index 00f37ae..13fff29d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java
@@ -18,8 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
-import android.testing.AndroidTestingRunner;
-
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -30,7 +29,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class RepeatableExecutorTest extends SysuiTestCase {
private static final int DELAY = 100;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
index b367a60..37015e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
@@ -23,8 +23,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
-
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.CoreStartable;
@@ -44,7 +43,7 @@
import java.util.Set;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ConditionalCoreStartableTest extends SysuiTestCase {
public static class FakeConditionalCoreStartable extends ConditionalCoreStartable {
interface Callback {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
index ac357ea..b8f5815 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
@@ -4,7 +4,7 @@
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ShapeDrawable
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -12,7 +12,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class DrawableSizeTest : SysuiTestCase() {
@@ -32,14 +32,11 @@
@Test
fun testDownscaleToSize_drawableSmallerThanRequirement_unchanged() {
- val drawable = BitmapDrawable(resources,
- Bitmap.createBitmap(
- resources.displayMetrics,
- 150,
- 150,
- Bitmap.Config.ARGB_8888
- )
- )
+ val drawable =
+ BitmapDrawable(
+ resources,
+ Bitmap.createBitmap(resources.displayMetrics, 150, 150, Bitmap.Config.ARGB_8888)
+ )
val result = DrawableSize.downscaleToSize(resources, drawable, 300, 300)
assertThat(result).isSameInstanceAs(drawable)
}
@@ -48,14 +45,11 @@
fun testDownscaleToSize_drawableLargerThanRequirementWithDensity_resized() {
// This bitmap would actually fail to resize if the method doesn't check for
// bitmap dimensions inside drawable.
- val drawable = BitmapDrawable(resources,
- Bitmap.createBitmap(
- resources.displayMetrics,
- 150,
- 75,
- Bitmap.Config.ARGB_8888
- )
- )
+ val drawable =
+ BitmapDrawable(
+ resources,
+ Bitmap.createBitmap(resources.displayMetrics, 150, 75, Bitmap.Config.ARGB_8888)
+ )
val result = DrawableSize.downscaleToSize(resources, drawable, 75, 75)
assertThat(result).isNotSameInstanceAs(drawable)
@@ -65,9 +59,9 @@
@Test
fun testDownscaleToSize_drawableAnimated_unchanged() {
- val drawable = resources.getDrawable(android.R.drawable.stat_sys_download,
- resources.newTheme())
+ val drawable =
+ resources.getDrawable(android.R.drawable.stat_sys_download, resources.newTheme())
val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1)
assertThat(result).isSameInstanceAs(drawable)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
index 7d0d57b..e2ce50c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
@@ -16,7 +16,7 @@
package com.android.systemui.util.kotlin
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.time.FakeSystemClock
@@ -47,7 +47,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class PairwiseFlowTest : SysuiTestCase() {
@Test
fun simple() = runBlocking {
@@ -89,7 +89,9 @@
initRun = true
"initial"
}
- ) { prev: String, next: String -> "$prev|$next" }
+ ) { prev: String, next: String ->
+ "$prev|$next"
+ }
)
.emitsExactly("initial|val1", "val1|val2")
assertThat(initRun).isTrue()
@@ -104,7 +106,9 @@
initRun = true
"initial"
}
- ) { prev: String, next: String -> "$prev|$next" }
+ ) { prev: String, next: String ->
+ "$prev|$next"
+ }
)
.emitsNothing()
// Even though the flow will not emit anything, the initial value function should still get
@@ -120,7 +124,9 @@
initRun = true
"initial"
}
- ) { prev: String, next: String -> "$prev|$next" }
+ ) { prev: String, next: String ->
+ "$prev|$next"
+ }
// Since the flow isn't collected, ensure [initialValueFun] isn't run.
assertThat(initRun).isFalse()
@@ -146,7 +152,7 @@
}
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class SetChangesFlowTest : SysuiTestCase() {
@Test
fun simple() = runBlocking {
@@ -198,7 +204,7 @@
}
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class SampleFlowTest : SysuiTestCase() {
@Test
fun simple() = runBlocking {
@@ -240,7 +246,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ThrottleFlowTest : SysuiTestCase() {
@Test
@@ -248,13 +254,16 @@
// Arrange
val choreographer = createChoreographer(this)
val output = mutableListOf<Int>()
- val collectJob = backgroundScope.launch {
- flow {
- emit(1)
- delay(1000)
- emit(2)
- }.throttle(1000, choreographer.fakeClock).toList(output)
- }
+ val collectJob =
+ backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(1000)
+ emit(2)
+ }
+ .throttle(1000, choreographer.fakeClock)
+ .toList(output)
+ }
// Act
choreographer.advanceAndRun(0)
@@ -283,13 +292,16 @@
// Arrange
val choreographer = createChoreographer(this)
val output = mutableListOf<Int>()
- val collectJob = backgroundScope.launch {
- flow {
- emit(1)
- delay(500)
- emit(2)
- }.throttle(1000, choreographer.fakeClock).toList(output)
- }
+ val collectJob =
+ backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(500)
+ emit(2)
+ }
+ .throttle(1000, choreographer.fakeClock)
+ .toList(output)
+ }
// Act
choreographer.advanceAndRun(0)
@@ -319,15 +331,18 @@
// Arrange
val choreographer = createChoreographer(this)
val output = mutableListOf<Int>()
- val collectJob = backgroundScope.launch {
- flow {
- emit(1)
- delay(500)
- emit(2)
- delay(500)
- emit(3)
- }.throttle(1000, choreographer.fakeClock).toList(output)
- }
+ val collectJob =
+ backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(500)
+ emit(2)
+ delay(500)
+ emit(3)
+ }
+ .throttle(1000, choreographer.fakeClock)
+ .toList(output)
+ }
// Act
choreographer.advanceAndRun(0)
@@ -357,15 +372,18 @@
// Arrange
val choreographer = createChoreographer(this)
val output = mutableListOf<Int>()
- val collectJob = backgroundScope.launch {
- flow {
- emit(1)
- delay(500)
- emit(2)
- delay(250)
- emit(3)
- }.throttle(1000, choreographer.fakeClock).toList(output)
- }
+ val collectJob =
+ backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(500)
+ emit(2)
+ delay(250)
+ emit(3)
+ }
+ .throttle(1000, choreographer.fakeClock)
+ .toList(output)
+ }
// Act
choreographer.advanceAndRun(0)
@@ -391,15 +409,16 @@
collectJob.cancel()
}
- private fun createChoreographer(testScope: TestScope) = object {
- val fakeClock = FakeSystemClock()
+ private fun createChoreographer(testScope: TestScope) =
+ object {
+ val fakeClock = FakeSystemClock()
- fun advanceAndRun(millis: Long) {
- fakeClock.advanceTime(millis)
- testScope.advanceTimeBy(millis)
- testScope.runCurrent()
+ fun advanceAndRun(millis: Long) {
+ fakeClock.advanceTime(millis)
+ testScope.advanceTimeBy(millis)
+ testScope.runCurrent()
+ }
}
- }
}
private fun <T> assertThatFlow(flow: Flow<T>) =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
index 4ca1fd3..c31b287 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.util.kotlin
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import java.util.concurrent.atomic.AtomicLong
@@ -31,43 +31,42 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class IpcSerializerTest : SysuiTestCase() {
private val serializer = IpcSerializer()
@Ignore("b/253046405")
@Test
- fun serializeManyIncomingIpcs(): Unit = runBlocking(Dispatchers.Main.immediate) {
- val processor = launch(start = CoroutineStart.LAZY) { serializer.process() }
- withContext(Dispatchers.IO) {
- val lastEvaluatedTime = AtomicLong(System.currentTimeMillis())
- // First, launch many serialization requests in parallel
- repeat(100_000) {
- launch(Dispatchers.Unconfined) {
- val enqueuedTime = System.currentTimeMillis()
- serializer.runSerialized {
- val last = lastEvaluatedTime.getAndSet(enqueuedTime)
- assertTrue(
- "expected $last less than or equal to $enqueuedTime ",
- last <= enqueuedTime,
- )
+ fun serializeManyIncomingIpcs(): Unit =
+ runBlocking(Dispatchers.Main.immediate) {
+ val processor = launch(start = CoroutineStart.LAZY) { serializer.process() }
+ withContext(Dispatchers.IO) {
+ val lastEvaluatedTime = AtomicLong(System.currentTimeMillis())
+ // First, launch many serialization requests in parallel
+ repeat(100_000) {
+ launch(Dispatchers.Unconfined) {
+ val enqueuedTime = System.currentTimeMillis()
+ serializer.runSerialized {
+ val last = lastEvaluatedTime.getAndSet(enqueuedTime)
+ assertTrue(
+ "expected $last less than or equal to $enqueuedTime ",
+ last <= enqueuedTime,
+ )
+ }
}
}
+ // Then, process them all in the order they came in.
+ processor.start()
}
- // Then, process them all in the order they came in.
- processor.start()
+ // All done, stop processing
+ processor.cancel()
}
- // All done, stop processing
- processor.cancel()
- }
@Test(timeout = 5000)
fun serializeOnOneThread_doesNotDeadlock() = runBlocking {
val job = launch { serializer.process() }
- repeat(100) {
- serializer.runSerializedBlocking { }
- }
+ repeat(100) { serializer.runSerializedBlocking {} }
job.cancel()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt
index 2013bb0..8bfff9c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt
@@ -27,13 +27,13 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
import org.mockito.Mock
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
internal class PackageManagerExtComponentEnabledTest(private val testCase: TestCase) :
SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
index 6848b83..b2f7c1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
@@ -16,7 +16,7 @@
package com.android.systemui.util.kotlin
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -28,7 +28,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class RaceSuspendTest : SysuiTestCase() {
@Test
fun raceSimple() = runBlocking {
@@ -46,10 +46,11 @@
@Test
fun raceImmediate() = runBlocking {
assertThat(
- race<Int>(
- { 1 },
- { 2 },
+ race<Int>(
+ { 1 },
+ { 2 },
+ )
)
- ).isEqualTo(1)
+ .isEqualTo(1)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
index 84129be..300c298 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
@@ -26,9 +26,9 @@
import android.content.res.Resources;
import android.hardware.Sensor;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -46,7 +46,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class PostureDependentProximitySensorTest extends SysuiTestCase {
@Mock private Resources mResources;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
index 19dbf9a..5dd008a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
@@ -23,9 +23,9 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -40,7 +40,7 @@
import java.util.function.Consumer;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class ProximityCheckTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
index 5e75578..0eab74e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
@@ -24,9 +24,9 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -40,7 +40,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class ProximitySensorImplDualTest extends SysuiTestCase {
private ProximitySensor mProximitySensor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
index 752cd32..f44c842 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
@@ -21,9 +21,9 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -40,7 +40,7 @@
* Tests for ProximitySensor that rely on a single hardware sensor.
*/
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class ProximitySensorImplSingleTest extends SysuiTestCase {
private ProximitySensor mProximitySensor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
index 8d26c87..a54afad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
@@ -30,8 +30,8 @@
import android.content.pm.UserInfo;
import android.os.IBinder;
import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -49,7 +49,7 @@
import java.util.Objects;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ObservableServiceConnectionTest extends SysuiTestCase {
static class Foo {
int mValue;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java
index a2fd288..a70b00c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java
@@ -24,8 +24,8 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -38,7 +38,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class PackageObserverTest extends SysuiTestCase {
@Mock
Context mContext;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
index 55c49ee..ef10fdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
@@ -19,8 +19,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
-
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -37,7 +36,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class PersistentConnectionManagerTest extends SysuiTestCase {
private static final int MAX_RETRIES = 5;
private static final int RETRY_DELAY_MS = 1000;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
index f65caee2..99f6303 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
@@ -28,8 +28,8 @@
import android.database.ContentObserver;
import android.os.UserHandle;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -44,7 +44,7 @@
import java.util.Map;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class FakeSettingsTest extends SysuiTestCase {
@Mock
ContentObserver mContentObserver;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
index 913759f..88b2630 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.util.settings.repository
import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -33,11 +34,10 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class UserAwareSecureSettingsRepositoryTest : SysuiTestCase() {
private val dispatcher = StandardTestDispatcher()
@@ -48,11 +48,12 @@
@Before
fun setup() {
- repository = UserAwareSecureSettingsRepositoryImpl(
- secureSettings,
- userRepository,
- dispatcher,
- )
+ repository =
+ UserAwareSecureSettingsRepositoryImpl(
+ secureSettings,
+ userRepository,
+ dispatcher,
+ )
userRepository.setUserInfos(USER_INFOS)
setSettingValueForUser(enabled = true, userInfo = SETTING_ENABLED_USER)
setSettingValueForUser(enabled = false, userInfo = SETTING_DISABLED_USER)
@@ -105,4 +106,4 @@
val SETTING_DISABLED_USER = UserInfo(/* id= */ 1, "user2", /* flags= */ 0)
val USER_INFOS = listOf(SETTING_ENABLED_USER, SETTING_DISABLED_USER)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
index 94100fe..6637d5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.util.ui
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -30,7 +30,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class AnimatedValueTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
index e3cd9b2..3dcb828 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
@@ -19,17 +19,20 @@
import android.graphics.Rect
import android.view.View
import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.spy
import org.mockito.Mockito.`when`
@SmallTest
+@RunWith(AndroidJUnit4::class)
class ViewUtilTest : SysuiTestCase() {
private val viewUtil = ViewUtil()
private lateinit var view: View
@@ -45,11 +48,13 @@
location[1] = VIEW_TOP
`when`(view.locationOnScreen).thenReturn(location)
doAnswer { invocation ->
- val pos = invocation.arguments[0] as IntArray
- pos[0] = VIEW_LEFT
- pos[1] = VIEW_TOP
- null
- }.`when`(view).getLocationInWindow(any())
+ val pos = invocation.arguments[0] as IntArray
+ pos[0] = VIEW_LEFT
+ pos[1] = VIEW_TOP
+ null
+ }
+ .`when`(view)
+ .getLocationInWindow(any())
}
@Test
@@ -59,9 +64,8 @@
@Test
fun touchIsWithinView_onTopLeftCorner_returnsTrue() {
- assertThat(viewUtil.touchIsWithinView(
- view, VIEW_LEFT.toFloat(), VIEW_TOP.toFloat())
- ).isTrue()
+ assertThat(viewUtil.touchIsWithinView(view, VIEW_LEFT.toFloat(), VIEW_TOP.toFloat()))
+ .isTrue()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
index ed07ad2..207c35d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
@@ -35,15 +35,18 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
import java.util.List;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+
@SmallTest
-@RunWith(Parameterized.class)
+@RunWith(ParameterizedAndroidJunit4.class)
public class WakeLockTest extends SysuiTestCase {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
public static List<FlagsParameterization> getFlags() {
return FlagsParameterization.allCombinationsOf(
Flags.FLAG_DELAYED_WAKELOCK_RELEASE_ON_BACKGROUND_THREAD);
@@ -114,4 +117,4 @@
// shouldn't throw an exception on production builds
mWakeLock.release(WHY);
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 38f2a56..3401cc4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -27,6 +27,9 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.model.sysUiState
+import com.android.systemui.settings.displayTracker
val Kosmos.shortcutHelperRepository by
Kosmos.Fixture { ShortcutHelperRepository(fakeCommandQueue, broadcastDispatcher) }
@@ -42,7 +45,9 @@
}
val Kosmos.shortcutHelperInteractor by
- Kosmos.Fixture { ShortcutHelperInteractor(shortcutHelperRepository) }
+ Kosmos.Fixture {
+ ShortcutHelperInteractor(displayTracker, testScope, sysUiState, shortcutHelperRepository)
+ }
val Kosmos.shortcutHelperViewModel by
Kosmos.Fixture { ShortcutHelperViewModel(testDispatcher, shortcutHelperInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
index 6cc1e8e..c90642d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
@@ -20,6 +20,7 @@
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by
Kosmos.Fixture {
@@ -32,5 +33,6 @@
fromAodTransitionInteractor = { fromAodTransitionInteractor },
fromAlternateBouncerTransitionInteractor = { fromAlternateBouncerTransitionInteractor },
fromDozingTransitionInteractor = { fromDozingTransitionInteractor },
+ sceneInteractor = { sceneInteractor }
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
index 460913f..b8fcec6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
val Kosmos.aodToLockscreenTransitionViewModel by Fixture {
AodToLockscreenTransitionViewModel(
- deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
shadeInteractor = shadeInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
index 59bbff1..1b58582 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
@@ -18,8 +18,6 @@
import android.content.packageManager
import android.content.pm.ApplicationInfo
-import android.os.Handler
-import android.os.looper
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.mediaOutputDialogManager
@@ -32,6 +30,7 @@
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.mediaControllerInteractor
val Kosmos.localMediaRepository by Kosmos.Fixture { FakeLocalMediaRepository() }
val Kosmos.localMediaRepositoryFactory by
@@ -53,7 +52,7 @@
testScope.backgroundScope,
testScope.testScheduler,
mediaControllerRepository,
- Handler(looper),
+ mediaControllerInteractor,
)
}
@@ -61,7 +60,7 @@
Kosmos.Fixture {
MediaDeviceSessionInteractor(
testScope.testScheduler,
- Handler(looper),
+ mediaControllerInteractor,
mediaControllerRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/FakeMediaControllerInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/FakeMediaControllerInteractor.kt
new file mode 100644
index 0000000..f03ec01
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/FakeMediaControllerInteractor.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import android.media.session.MediaController
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+
+class FakeMediaControllerInteractor : MediaControllerInteractor {
+
+ private val stateChanges = MutableSharedFlow<MediaControllerChangeModel>(replay = 1)
+
+ override fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel> =
+ stateChanges
+
+ fun updateState(change: MediaControllerChangeModel) {
+ stateChanges.tryEmit(change)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt
new file mode 100644
index 0000000..652b3ea
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import android.os.Handler
+import android.os.looper
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.mediaControllerInteractor: MediaControllerInteractor by
+ Kosmos.Fixture { MediaControllerInteractorImpl(Handler(looper)) }
diff --git a/proto/src/am_capabilities.proto b/proto/src/am_capabilities.proto
index fc9f7a45..c2b3ac2 100644
--- a/proto/src/am_capabilities.proto
+++ b/proto/src/am_capabilities.proto
@@ -15,8 +15,16 @@
string name = 1;
}
+message VMInfo {
+ // The value of the "java.vm.name" system property
+ string name = 1;
+ // The value of the "java.vm.version" system property
+ string version = 2;
+}
+
message Capabilities {
repeated Capability values = 1;
repeated VMCapability vm_capabilities = 2;
repeated FrameworkCapability framework_capabilities = 3;
+ VMInfo vm_info = 4;
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java
new file mode 100644
index 0000000..4992c4b
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java
@@ -0,0 +1,753 @@
+/*
+ * Copyright (C) 2024 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.platform.test.ravenwood;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.IntentSender.SendIntentException;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.view.Display;
+import android.view.DisplayAdjustments;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A subclass of Context with all the abstract methods replaced with concrete methods.
+ *
+ * <p>In order to make sure it implements all the abstract methods, we intentionally keep it
+ * non-abstract.
+ */
+public class RavenwoodBaseContext extends Context {
+ RavenwoodBaseContext() {
+ // Only usable by ravenwood.
+ }
+
+ private static RuntimeException notSupported() {
+ return new RuntimeException("This Context API is not yet supported under"
+ + " the Ravenwood deviceless testing environment. Contact g/ravenwood");
+ }
+
+ @Override
+ public AssetManager getAssets() {
+ throw notSupported();
+ }
+
+ @Override
+ public Resources getResources() {
+ throw notSupported();
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ throw notSupported();
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ throw notSupported();
+ }
+
+ @Override
+ public Looper getMainLooper() {
+ throw notSupported();
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ throw notSupported();
+ }
+
+ @Override
+ public void setTheme(int resid) {
+ throw notSupported();
+ }
+
+ @Override
+ public Theme getTheme() {
+ throw notSupported();
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ throw notSupported();
+ }
+
+ @Override
+ public String getPackageName() {
+ throw notSupported();
+ }
+
+ @Override
+ public String getBasePackageName() {
+ throw notSupported();
+ }
+
+ @Override
+ public ApplicationInfo getApplicationInfo() {
+ throw notSupported();
+ }
+
+ @Override
+ public String getPackageResourcePath() {
+ throw notSupported();
+ }
+
+ @Override
+ public String getPackageCodePath() {
+ throw notSupported();
+ }
+
+ @Override
+ public SharedPreferences getSharedPreferences(String name, int mode) {
+ throw notSupported();
+ }
+
+ @Override
+ public SharedPreferences getSharedPreferences(File file, int mode) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean moveSharedPreferencesFrom(Context sourceContext, String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean deleteSharedPreferences(String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public void reloadSharedPreferences() {
+ throw notSupported();
+ }
+
+ @Override
+ public FileInputStream openFileInput(String name) throws FileNotFoundException {
+ throw notSupported();
+ }
+
+ @Override
+ public FileOutputStream openFileOutput(String name, int mode) throws FileNotFoundException {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean deleteFile(String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public File getFileStreamPath(String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public File getSharedPreferencesPath(String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public File getDataDir() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getFilesDir() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getNoBackupFilesDir() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getExternalFilesDir(String type) {
+ throw notSupported();
+ }
+
+ @Override
+ public File[] getExternalFilesDirs(String type) {
+ throw notSupported();
+ }
+
+ @Override
+ public File getObbDir() {
+ throw notSupported();
+ }
+
+ @Override
+ public File[] getObbDirs() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getCacheDir() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getCodeCacheDir() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getExternalCacheDir() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getPreloadsFileCache() {
+ throw notSupported();
+ }
+
+ @Override
+ public File[] getExternalCacheDirs() {
+ throw notSupported();
+ }
+
+ @Override
+ public File[] getExternalMediaDirs() {
+ throw notSupported();
+ }
+
+ @Override
+ public String[] fileList() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getDir(String name, int mode) {
+ throw notSupported();
+ }
+
+ @Override
+ public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) {
+ throw notSupported();
+ }
+
+ @Override
+ public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory,
+ DatabaseErrorHandler errorHandler) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean moveDatabaseFrom(Context sourceContext, String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean deleteDatabase(String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public File getDatabasePath(String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public String[] databaseList() {
+ throw notSupported();
+ }
+
+ @Override
+ public Drawable getWallpaper() {
+ throw notSupported();
+ }
+
+ @Override
+ public Drawable peekWallpaper() {
+ throw notSupported();
+ }
+
+ @Override
+ public int getWallpaperDesiredMinimumWidth() {
+ throw notSupported();
+ }
+
+ @Override
+ public int getWallpaperDesiredMinimumHeight() {
+ throw notSupported();
+ }
+
+ @Override
+ public void setWallpaper(Bitmap bitmap) throws IOException {
+ throw notSupported();
+ }
+
+ @Override
+ public void setWallpaper(InputStream data) throws IOException {
+ throw notSupported();
+ }
+
+ @Override
+ public void clearWallpaper() throws IOException {
+ throw notSupported();
+ }
+
+ @Override
+ public void startActivity(Intent intent) {
+ throw notSupported();
+ }
+
+ @Override
+ public void startActivity(Intent intent, Bundle options) {
+ throw notSupported();
+ }
+
+ @Override
+ public void startActivities(Intent[] intents) {
+ throw notSupported();
+ }
+
+ @Override
+ public void startActivities(Intent[] intents, Bundle options) {
+ throw notSupported();
+ }
+
+ @Override
+ public void startIntentSender(IntentSender intent, Intent fillInIntent, int flagsMask,
+ int flagsValues, int extraFlags) throws SendIntentException {
+ throw notSupported();
+ }
+
+ @Override
+ public void startIntentSender(IntentSender intent, Intent fillInIntent, int flagsMask,
+ int flagsValues, int extraFlags, Bundle options) throws SendIntentException {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcast(Intent intent) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcast(Intent intent, String receiverPermission) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
+ String[] receiverPermissions) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendOrderedBroadcast(Intent intent, String receiverPermission) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendOrderedBroadcast(Intent intent, String receiverPermission,
+ BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+ String initialData, Bundle initialExtras) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendOrderedBroadcast(Intent intent, String receiverPermission,
+ int appOp, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+ String initialData, Bundle initialExtras) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission,
+ Bundle options) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission,
+ int appOp) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
+ int initialCode, String initialData, Bundle initialExtras) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
+ Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission, int appOp, Bundle options,
+ BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+ String initialData, Bundle initialExtras) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendStickyBroadcast(Intent intent) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver,
+ Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+ throw notSupported();
+
+ }
+
+ @Override
+ public void removeStickyBroadcast(Intent intent) {
+ throw notSupported();
+
+ }
+
+ @Override
+ public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
+ throw notSupported();
+
+ }
+
+ @Override
+ public void sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user,
+ BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+ String initialData, Bundle initialExtras) {
+ throw notSupported();
+ }
+
+ @Override
+ public void removeStickyBroadcastAsUser(Intent intent, UserHandle user) {
+ throw notSupported();
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ throw notSupported();
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
+ throw notSupported();
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler) {
+ throw notSupported();
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler, int flags) {
+ throw notSupported();
+ }
+
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ throw notSupported();
+ }
+
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
+ throw notSupported();
+ }
+
+ @Override
+ public void unregisterReceiver(BroadcastReceiver receiver) {
+ throw notSupported();
+ }
+
+ @Override
+ public ComponentName startService(Intent service) {
+ throw notSupported();
+ }
+
+ @Override
+ public ComponentName startForegroundService(Intent service) {
+ throw notSupported();
+ }
+
+ @Override
+ public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean stopService(Intent service) {
+ throw notSupported();
+ }
+
+ @Override
+ public ComponentName startServiceAsUser(Intent service, UserHandle user) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean stopServiceAsUser(Intent service, UserHandle user) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean bindService(Intent service, ServiceConnection conn, int flags) {
+ throw notSupported();
+ }
+
+ @Override
+ public void unbindService(ServiceConnection conn) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean startInstrumentation(ComponentName className, String profileFile,
+ Bundle arguments) {
+ throw notSupported();
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkPermission(String permission, int pid, int uid) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkCallingPermission(String permission) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkCallingOrSelfPermission(String permission) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkSelfPermission(String permission) {
+ throw notSupported();
+ }
+
+ @Override
+ public void enforcePermission(String permission, int pid, int uid, String message) {
+ throw notSupported();
+ }
+
+ @Override
+ public void enforceCallingPermission(String permission, String message) {
+ throw notSupported();
+ }
+
+ @Override
+ public void enforceCallingOrSelfPermission(String permission, String message) {
+ throw notSupported();
+ }
+
+ @Override
+ public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
+ throw notSupported();
+ }
+
+ @Override
+ public void revokeUriPermission(Uri uri, int modeFlags) {
+ throw notSupported();
+ }
+
+ @Override
+ public void revokeUriPermission(String toPackage, Uri uri, int modeFlags) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkCallingUriPermission(Uri uri, int modeFlags) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkUriPermission(Uri uri, String readPermission, String writePermission,
+ int pid, int uid, int modeFlags) {
+ throw notSupported();
+ }
+
+ @Override
+ public void enforceUriPermission(Uri uri, int pid, int uid, int modeFlags, String message) {
+ throw notSupported();
+ }
+
+ @Override
+ public void enforceCallingUriPermission(Uri uri, int modeFlags, String message) {
+ throw notSupported();
+ }
+
+ @Override
+ public void enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message) {
+ throw notSupported();
+ }
+
+ @Override
+ public void enforceUriPermission(Uri uri, String readPermission, String writePermission,
+ int pid, int uid, int modeFlags, String message) {
+ throw notSupported();
+ }
+
+ @Override
+ public Context createPackageContext(String packageName, int flags)
+ throws NameNotFoundException {
+ throw notSupported();
+ }
+
+ @Override
+ public Context createApplicationContext(ApplicationInfo application, int flags)
+ throws NameNotFoundException {
+ throw notSupported();
+ }
+
+ @Override
+ public Context createContextForSplit(String splitName) throws NameNotFoundException {
+ throw notSupported();
+ }
+
+ @Override
+ public Context createConfigurationContext(Configuration overrideConfiguration) {
+ throw notSupported();
+ }
+
+ @Override
+ public Context createDisplayContext(Display display) {
+ throw notSupported();
+ }
+
+ @Override
+ public Context createDeviceProtectedStorageContext() {
+ throw notSupported();
+ }
+
+ @Override
+ public Context createCredentialProtectedStorageContext() {
+ throw notSupported();
+ }
+
+ @Override
+ public DisplayAdjustments getDisplayAdjustments(int displayId) {
+ throw notSupported();
+ }
+
+ @Override
+ public int getDisplayId() {
+ throw notSupported();
+ }
+
+ @Override
+ public void updateDisplay(int displayId) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean isDeviceProtectedStorage() {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean isCredentialProtectedStorage() {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean canLoadUnsafeResources() {
+ throw notSupported();
+ }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
index 109ef76..1dd5e1d 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
@@ -28,7 +28,6 @@
import android.os.UserHandle;
import android.ravenwood.example.BlueManager;
import android.ravenwood.example.RedManager;
-import android.test.mock.MockContext;
import android.util.ArrayMap;
import android.util.Singleton;
@@ -36,7 +35,7 @@
import java.util.concurrent.Executor;
import java.util.function.Supplier;
-public class RavenwoodContext extends MockContext {
+public class RavenwoodContext extends RavenwoodBaseContext {
private final String mPackageName;
private final HandlerThread mMainThread;
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index 371c3ac..9d29a05 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -1,59 +1,59 @@
# Ravenwood "policy" file for framework-minus-apex.
# Keep all AIDL interfaces
-class :aidl stubclass
+class :aidl keepclass
# Keep all feature flag implementations
-class :feature_flags stubclass
+class :feature_flags keepclass
# Keep all sysprops generated code implementations
-class :sysprops stubclass
+class :sysprops keepclass
# Exported to Mainline modules; cannot use annotations
-class com.android.internal.util.FastXmlSerializer stubclass
-class com.android.internal.util.FileRotator stubclass
-class com.android.internal.util.HexDump stubclass
-class com.android.internal.util.IndentingPrintWriter stubclass
-class com.android.internal.util.LocalLog stubclass
-class com.android.internal.util.MessageUtils stubclass
-class com.android.internal.util.TokenBucket stubclass
-class android.os.HandlerExecutor stubclass
-class android.util.BackupUtils stubclass
-class android.util.IndentingPrintWriter stubclass
-class android.util.LocalLog stubclass
-class android.util.Pair stubclass
-class android.util.Rational stubclass
+class com.android.internal.util.FastXmlSerializer keepclass
+class com.android.internal.util.FileRotator keepclass
+class com.android.internal.util.HexDump keepclass
+class com.android.internal.util.IndentingPrintWriter keepclass
+class com.android.internal.util.LocalLog keepclass
+class com.android.internal.util.MessageUtils keepclass
+class com.android.internal.util.TokenBucket keepclass
+class android.os.HandlerExecutor keepclass
+class android.util.BackupUtils keepclass
+class android.util.IndentingPrintWriter keepclass
+class android.util.LocalLog keepclass
+class android.util.Pair keepclass
+class android.util.Rational keepclass
# From modules-utils; cannot use annotations
-class com.android.internal.util.Preconditions stubclass
-class com.android.internal.logging.InstanceId stubclass
-class com.android.internal.logging.InstanceIdSequence stubclass
-class com.android.internal.logging.UiEvent stubclass
-class com.android.internal.logging.UiEventLogger stubclass
+class com.android.internal.util.Preconditions keepclass
+class com.android.internal.logging.InstanceId keepclass
+class com.android.internal.logging.InstanceIdSequence keepclass
+class com.android.internal.logging.UiEvent keepclass
+class com.android.internal.logging.UiEventLogger keepclass
# From modules-utils; cannot use annotations
-class com.android.modules.utils.BinaryXmlPullParser stubclass
-class com.android.modules.utils.BinaryXmlSerializer stubclass
-class com.android.modules.utils.FastDataInput stubclass
-class com.android.modules.utils.FastDataOutput stubclass
-class com.android.modules.utils.ModifiedUtf8 stubclass
-class com.android.modules.utils.TypedXmlPullParser stubclass
-class com.android.modules.utils.TypedXmlSerializer stubclass
+class com.android.modules.utils.BinaryXmlPullParser keepclass
+class com.android.modules.utils.BinaryXmlSerializer keepclass
+class com.android.modules.utils.FastDataInput keepclass
+class com.android.modules.utils.FastDataOutput keepclass
+class com.android.modules.utils.ModifiedUtf8 keepclass
+class com.android.modules.utils.TypedXmlPullParser keepclass
+class com.android.modules.utils.TypedXmlSerializer keepclass
# Uri
-class android.net.Uri stubclass
-class android.net.UriCodec stubclass
+class android.net.Uri keepclass
+class android.net.UriCodec keepclass
# Telephony
-class android.telephony.PinResult stubclass
+class android.telephony.PinResult keepclass
# Just enough to support mocking, no further functionality
-class android.content.BroadcastReceiver stub
- method <init> ()V stub
-class android.content.Context stub
- method <init> ()V stub
- method getSystemService (Ljava/lang/Class;)Ljava/lang/Object; stub
-class android.content.pm.PackageManager stub
- method <init> ()V stub
-class android.text.ClipboardManager stub
- method <init> ()V stub
+class android.content.BroadcastReceiver keep
+ method <init> ()V keep
+class android.content.Context keep
+ method <init> ()V keep
+ method getSystemService (Ljava/lang/Class;)Ljava/lang/Object; keep
+class android.content.pm.PackageManager keep
+ method <init> ()V keep
+class android.text.ClipboardManager keep
+ method <init> ()V keep
diff --git a/ravenwood/texts/ravenwood-services-policies.txt b/ravenwood/texts/ravenwood-services-policies.txt
index d8d563e..5cdb4f7 100644
--- a/ravenwood/texts/ravenwood-services-policies.txt
+++ b/ravenwood/texts/ravenwood-services-policies.txt
@@ -1,7 +1,7 @@
# Ravenwood "policy" file for services.core.
# Keep all AIDL interfaces
-class :aidl stubclass
+class :aidl keepclass
# Keep all feature flag implementations
-class :feature_flags stubclass
+class :feature_flags keepclass
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
index 468b9ab..219b788 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -36,6 +36,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.IInlineSuggestionsResponseCallback;
+import com.android.internal.inputmethod.InlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
import com.android.server.autofill.ui.InlineFillUi;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -376,8 +377,8 @@
/**
* Internal implementation of {@link IInlineSuggestionsRequestCallback}.
*/
- private static final class InlineSuggestionsRequestCallbackImpl extends
- IInlineSuggestionsRequestCallback.Stub {
+ private static final class InlineSuggestionsRequestCallbackImpl
+ implements InlineSuggestionsRequestCallback {
private final WeakReference<AutofillInlineSuggestionsRequestSession> mSession;
@@ -388,7 +389,7 @@
@BinderThread
@Override
- public void onInlineSuggestionsUnsupported() throws RemoteException {
+ public void onInlineSuggestionsUnsupported() {
if (sDebug) Slog.d(TAG, "onInlineSuggestionsUnsupported() called.");
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
@@ -412,7 +413,7 @@
}
@Override
- public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
+ public void onInputMethodStartInput(AutofillId imeFieldId) {
if (sVerbose) Slog.v(TAG, "onInputMethodStartInput() received on " + imeFieldId);
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
@@ -423,7 +424,7 @@
}
@Override
- public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
+ public void onInputMethodShowInputRequested(boolean requestResult) {
if (sVerbose) {
Slog.v(TAG, "onInputMethodShowInputRequested() received: " + requestResult);
}
@@ -454,7 +455,7 @@
}
@Override
- public void onInputMethodFinishInput() throws RemoteException {
+ public void onInputMethodFinishInput() {
if (sVerbose) Slog.v(TAG, "onInputMethodFinishInput() received");
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
@@ -466,7 +467,7 @@
@BinderThread
@Override
- public void onInlineSuggestionsSessionInvalidated() throws RemoteException {
+ public void onInlineSuggestionsSessionInvalidated() {
if (sDebug) Slog.d(TAG, "onInlineSuggestionsSessionInvalidated() called.");
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index d04dbc2..d9e6186 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -870,6 +870,7 @@
}
synchronized (this) {
pinnedApp.mFiles.add(pf);
+ mPinnedFiles.put(pf.fileName, pf);
}
apkPinSizeLimit -= pf.bytesPinned;
@@ -1341,18 +1342,6 @@
public List<PinnedFileStat> getPinnerStats() {
ArrayList<PinnedFileStat> stats = new ArrayList<>();
- Collection<PinnedApp> pinnedApps;
- synchronized(this) {
- pinnedApps = mPinnedApps.values();
- }
- for (PinnedApp pinnedApp : pinnedApps) {
- for (PinnedFile pf : pinnedApp.mFiles) {
- PinnedFileStat stat =
- new PinnedFileStat(pf.fileName, pf.bytesPinned, pf.groupName);
- stats.add(stat);
- }
- }
-
Collection<PinnedFile> pinnedFiles;
synchronized(this) {
pinnedFiles = mPinnedFiles.values();
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index a182a10..bbd4323 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -130,6 +130,7 @@
import com.android.server.am.nano.Capability;
import com.android.server.am.nano.FrameworkCapability;
import com.android.server.am.nano.VMCapability;
+import com.android.server.am.nano.VMInfo;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.Slogf;
@@ -460,6 +461,8 @@
return -1;
}
}
+ String vmName = System.getProperty("java.vm.name", "?");
+ String vmVersion = System.getProperty("java.vm.version", "?");
if (outputAsProtobuf) {
Capabilities capabilities = new Capabilities();
@@ -486,6 +489,11 @@
capabilities.frameworkCapabilities[i] = cap;
}
+ VMInfo vmInfo = new VMInfo();
+ vmInfo.name = vmName;
+ vmInfo.version = vmVersion;
+ capabilities.vmInfo = vmInfo;
+
try {
getRawOutputStream().write(Capabilities.toByteArray(capabilities));
} catch (IOException e) {
@@ -505,6 +513,8 @@
for (String capability : Debug.getFeatureList()) {
pw.println("framework:" + capability);
}
+ pw.println("vm_name:" + vmName);
+ pw.println("vm_version:" + vmVersion);
}
return 0;
}
diff --git a/services/core/java/com/android/server/display/config/RefreshRateData.java b/services/core/java/com/android/server/display/config/RefreshRateData.java
index b186fd5..d7ed904 100644
--- a/services/core/java/com/android/server/display/config/RefreshRateData.java
+++ b/services/core/java/com/android/server/display/config/RefreshRateData.java
@@ -20,6 +20,10 @@
import android.content.res.Resources;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Collections;
+import java.util.List;
/**
* RefreshRates config for display
@@ -58,12 +62,17 @@
*/
public final int defaultRefreshRateInHbmSunlight;
+ public final List<SupportedModeData> lowPowerSupportedModes;
+
+ @VisibleForTesting
public RefreshRateData(int defaultRefreshRate, int defaultPeakRefreshRate,
- int defaultRefreshRateInHbmHdr, int defaultRefreshRateInHbmSunlight) {
+ int defaultRefreshRateInHbmHdr, int defaultRefreshRateInHbmSunlight,
+ List<SupportedModeData> lowPowerSupportedModes) {
this.defaultRefreshRate = defaultRefreshRate;
this.defaultPeakRefreshRate = defaultPeakRefreshRate;
this.defaultRefreshRateInHbmHdr = defaultRefreshRateInHbmHdr;
this.defaultRefreshRateInHbmSunlight = defaultRefreshRateInHbmSunlight;
+ this.lowPowerSupportedModes = Collections.unmodifiableList(lowPowerSupportedModes);
}
@@ -71,9 +80,10 @@
public String toString() {
return "RefreshRateData {"
+ "defaultRefreshRate: " + defaultRefreshRate
- + "defaultPeakRefreshRate: " + defaultPeakRefreshRate
- + "defaultRefreshRateInHbmHdr: " + defaultRefreshRateInHbmHdr
- + "defaultRefreshRateInHbmSunlight: " + defaultRefreshRateInHbmSunlight
+ + ", defaultPeakRefreshRate: " + defaultPeakRefreshRate
+ + ", defaultRefreshRateInHbmHdr: " + defaultRefreshRateInHbmHdr
+ + ", defaultRefreshRateInHbmSunlight: " + defaultRefreshRateInHbmSunlight
+ + ", lowPowerSupportedModes=" + lowPowerSupportedModes
+ "} ";
}
@@ -90,8 +100,13 @@
int defaultRefreshRateInHbmSunlight = loadDefaultRefreshRateInHbmSunlight(
refreshRateConfigs, resources);
+ NonNegativeFloatToFloatMap modes =
+ refreshRateConfigs == null ? null : refreshRateConfigs.getLowPowerSupportedModes();
+ List<SupportedModeData> lowPowerSupportedModes = SupportedModeData.load(modes);
+
return new RefreshRateData(defaultRefreshRate, defaultPeakRefreshRate,
- defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight);
+ defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight,
+ lowPowerSupportedModes);
}
private static int loadDefaultRefreshRate(
diff --git a/services/core/java/com/android/server/display/config/SensorData.java b/services/core/java/com/android/server/display/config/SensorData.java
index 6ad13c3..8bfc4a3 100644
--- a/services/core/java/com/android/server/display/config/SensorData.java
+++ b/services/core/java/com/android/server/display/config/SensorData.java
@@ -24,7 +24,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.feature.DisplayManagerFlags;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -42,7 +41,7 @@
public final String name;
public final float minRefreshRate;
public final float maxRefreshRate;
- public final List<SupportedMode> supportedModes;
+ public final List<SupportedModeData> supportedModes;
@VisibleForTesting
public SensorData() {
@@ -61,7 +60,7 @@
@VisibleForTesting
public SensorData(String type, String name, float minRefreshRate, float maxRefreshRate,
- List<SupportedMode> supportedModes) {
+ List<SupportedModeData> supportedModes) {
this.type = type;
this.name = name;
this.minRefreshRate = minRefreshRate;
@@ -214,26 +213,11 @@
minRefreshRate = rr.getMinimum().floatValue();
maxRefreshRate = rr.getMaximum().floatValue();
}
- ArrayList<SupportedMode> supportedModes = new ArrayList<>();
- NonNegativeFloatToFloatMap configSupportedModes = sensorDetails.getSupportedModes();
- if (configSupportedModes != null) {
- for (NonNegativeFloatToFloatPoint supportedMode : configSupportedModes.getPoint()) {
- supportedModes.add(new SupportedMode(supportedMode.getFirst().floatValue(),
- supportedMode.getSecond().floatValue()));
- }
- }
+ List<SupportedModeData> supportedModes = SupportedModeData.load(
+ sensorDetails.getSupportedModes());
return new SensorData(sensorDetails.getType(), sensorDetails.getName(), minRefreshRate,
maxRefreshRate, supportedModes);
}
- public static class SupportedMode {
- public final float refreshRate;
- public final float vsyncRate;
-
- public SupportedMode(float refreshRate, float vsyncRate) {
- this.refreshRate = refreshRate;
- this.vsyncRate = vsyncRate;
- }
- }
}
diff --git a/services/core/java/com/android/server/display/config/SupportedModeData.java b/services/core/java/com/android/server/display/config/SupportedModeData.java
new file mode 100644
index 0000000..3c82884
--- /dev/null
+++ b/services/core/java/com/android/server/display/config/SupportedModeData.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.config;
+
+import android.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Supported display mode data. Display mode is uniquely identified by refreshRate-vsync pair
+ */
+public class SupportedModeData {
+ public final float refreshRate;
+ public final float vsyncRate;
+
+ public SupportedModeData(float refreshRate, float vsyncRate) {
+ this.refreshRate = refreshRate;
+ this.vsyncRate = vsyncRate;
+ }
+
+ @Override
+ public String toString() {
+ return "SupportedModeData{"
+ + "refreshRate= " + refreshRate
+ + ", vsyncRate= " + vsyncRate
+ + '}';
+ }
+
+ static List<SupportedModeData> load(@Nullable NonNegativeFloatToFloatMap configMap) {
+ ArrayList<SupportedModeData> supportedModes = new ArrayList<>();
+ if (configMap != null) {
+ for (NonNegativeFloatToFloatPoint supportedMode : configMap.getPoint()) {
+ supportedModes.add(new SupportedModeData(supportedMode.getFirst().floatValue(),
+ supportedMode.getSecond().floatValue()));
+ }
+ }
+ return supportedModes;
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 91bd80e..846ee23 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -78,6 +78,7 @@
import com.android.server.display.DisplayDeviceConfig;
import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint;
import com.android.server.display.config.RefreshRateData;
+import com.android.server.display.config.SupportedModeData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.utils.AmbientFilter;
@@ -451,15 +452,6 @@
return config != null && config.isVrrSupportEnabled();
}
- private boolean isVrrSupportedByAnyDisplayLocked() {
- for (int i = 0; i < mDisplayDeviceConfigByDisplay.size(); i++) {
- if (mDisplayDeviceConfigByDisplay.valueAt(i).isVrrSupportEnabled()) {
- return true;
- }
- }
- return false;
- }
-
/**
* Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate ranges.
*/
@@ -939,18 +931,44 @@
private final Uri mMatchContentFrameRateSetting =
Settings.Secure.getUriFor(Settings.Secure.MATCH_CONTENT_FRAME_RATE);
- private final boolean mVsynLowPowerVoteEnabled;
+ private final boolean mVsyncLowPowerVoteEnabled;
private final boolean mPeakRefreshRatePhysicalLimitEnabled;
private final Context mContext;
+ private final Handler mHandler;
private float mDefaultPeakRefreshRate;
private float mDefaultRefreshRate;
+ private boolean mIsLowPower = false;
+
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ synchronized (mLock) {
+ updateLowPowerModeAllowedModesLocked();
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_LOW_POWER_MODE_MODES,
+ null);
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ synchronized (mLock) {
+ updateLowPowerModeAllowedModesLocked();
+ }
+ }
+ };
SettingsObserver(@NonNull Context context, @NonNull Handler handler,
DisplayManagerFlags flags) {
super(handler);
mContext = context;
- mVsynLowPowerVoteEnabled = flags.isVsyncLowPowerVoteEnabled();
+ mHandler = handler;
+ mVsyncLowPowerVoteEnabled = flags.isVsyncLowPowerVoteEnabled();
mPeakRefreshRatePhysicalLimitEnabled = flags.isPeakRefreshRatePhysicalLimitEnabled();
// We don't want to load from the DeviceConfig while constructing since this leads to
// a spike in the latency of DisplayManagerService startup. This happens because
@@ -983,6 +1001,7 @@
UserHandle.USER_SYSTEM);
cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/,
this);
+ mInjector.registerDisplayListener(mDisplayListener, mHandler);
float deviceConfigDefaultPeakRefresh =
mConfigParameterProvider.getPeakRefreshRateDefault();
@@ -995,6 +1014,7 @@
updateLowPowerModeSettingLocked();
updateModeSwitchingTypeSettingLocked();
}
+
}
public void setDefaultRefreshRate(float refreshRate) {
@@ -1061,23 +1081,36 @@
}
private void updateLowPowerModeSettingLocked() {
- boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
+ mIsLowPower = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
final Vote vote;
- if (inLowPowerMode && mVsynLowPowerVoteEnabled && isVrrSupportedByAnyDisplayLocked()) {
- vote = Vote.forSupportedRefreshRates(List.of(
- new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f,
- /* vsyncRate= */ 240f),
- new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f,
- /* vsyncRate= */ 60f)
- ));
- } else if (inLowPowerMode) {
+ if (mIsLowPower) {
vote = Vote.forRenderFrameRates(0f, 60f);
} else {
vote = null;
}
- mVotesStorage.updateGlobalVote(Vote.PRIORITY_LOW_POWER_MODE, vote);
- mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
+ mVotesStorage.updateGlobalVote(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, vote);
+ mBrightnessObserver.onLowPowerModeEnabledLocked(mIsLowPower);
+ updateLowPowerModeAllowedModesLocked();
+ }
+
+ private void updateLowPowerModeAllowedModesLocked() {
+ if (!mVsyncLowPowerVoteEnabled) {
+ return;
+ }
+ if (mIsLowPower) {
+ for (int i = 0; i < mDisplayDeviceConfigByDisplay.size(); i++) {
+ DisplayDeviceConfig config = mDisplayDeviceConfigByDisplay.valueAt(i);
+ List<SupportedModeData> supportedModes = config
+ .getRefreshRateData().lowPowerSupportedModes;
+ mVotesStorage.updateVote(
+ mDisplayDeviceConfigByDisplay.keyAt(i),
+ Vote.PRIORITY_LOW_POWER_MODE_MODES,
+ Vote.forSupportedRefreshRates(supportedModes));
+ }
+ } else {
+ mVotesStorage.removeAllVotesForPriority(Vote.PRIORITY_LOW_POWER_MODE_MODES);
+ }
}
/**
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index ddb334e..8167c1f 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -18,6 +18,9 @@
import android.annotation.NonNull;
+import com.android.server.display.config.SupportedModeData;
+
+import java.util.ArrayList;
import java.util.List;
interface Vote {
@@ -102,9 +105,15 @@
// For internal application to limit display modes to specific ids
int PRIORITY_SYSTEM_REQUESTED_MODES = 14;
- // LOW_POWER_MODE force the render frame rate to [0, 60HZ] if
+ // PRIORITY_LOW_POWER_MODE_MODES limits display modes to specific refreshRate-vsync pairs if
// Settings.Global.LOW_POWER_MODE is on.
- int PRIORITY_LOW_POWER_MODE = 15;
+ // Lower priority that PRIORITY_LOW_POWER_MODE_RENDER_RATE and if discarded (due to other
+ // higher priority votes), render rate limit can still apply
+ int PRIORITY_LOW_POWER_MODE_MODES = 14;
+
+ // PRIORITY_LOW_POWER_MODE_RENDER_RATE force the render frame rate to [0, 60HZ] if
+ // Settings.Global.LOW_POWER_MODE is on.
+ int PRIORITY_LOW_POWER_MODE_RENDER_RATE = 15;
// PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
// higher priority voters' result is a range, it will fix the rate to a single choice.
@@ -177,22 +186,26 @@
return new BaseModeRefreshRateVote(baseModeRefreshRate);
}
- static Vote forSupportedRefreshRates(
- List<SupportedRefreshRatesVote.RefreshRates> refreshRates) {
- return new SupportedRefreshRatesVote(refreshRates);
+ static Vote forSupportedRefreshRates(List<SupportedModeData> supportedModes) {
+ if (supportedModes.isEmpty()) {
+ return null;
+ }
+ List<SupportedRefreshRatesVote.RefreshRates> rates = new ArrayList<>();
+ for (SupportedModeData data : supportedModes) {
+ rates.add(new SupportedRefreshRatesVote.RefreshRates(data.refreshRate, data.vsyncRate));
+ }
+ return new SupportedRefreshRatesVote(rates);
}
static Vote forSupportedModes(List<Integer> modeIds) {
return new SupportedModesVote(modeIds);
}
-
-
static Vote forSupportedRefreshRatesAndDisableSwitching(
List<SupportedRefreshRatesVote.RefreshRates> supportedRefreshRates) {
return new CombinedVote(
List.of(forDisableRefreshRateSwitching(),
- forSupportedRefreshRates(supportedRefreshRates)));
+ new SupportedRefreshRatesVote(supportedRefreshRates)));
}
static String priorityToString(int priority) {
@@ -213,8 +226,10 @@
return "PRIORITY_HIGH_BRIGHTNESS_MODE";
case PRIORITY_PROXIMITY:
return "PRIORITY_PROXIMITY";
- case PRIORITY_LOW_POWER_MODE:
- return "PRIORITY_LOW_POWER_MODE";
+ case PRIORITY_LOW_POWER_MODE_MODES:
+ return "PRIORITY_LOW_POWER_MODE_MODES";
+ case PRIORITY_LOW_POWER_MODE_RENDER_RATE:
+ return "PRIORITY_LOW_POWER_MODE_RENDER_RATE";
case PRIORITY_SKIN_TEMPERATURE:
return "PRIORITY_SKIN_TEMPERATURE";
case PRIORITY_UDFPS:
@@ -227,6 +242,8 @@
return "PRIORITY_LIMIT_MODE";
case PRIORITY_SYNCHRONIZED_REFRESH_RATE:
return "PRIORITY_SYNCHRONIZED_REFRESH_RATE";
+ case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
+ return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
case PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE:
return "PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE";
case PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index d21fc85..5db17bb 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -29,7 +29,6 @@
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.sysprop.HdmiProperties;
import android.util.Slog;
@@ -278,8 +277,7 @@
void dismissUiOnActiveSourceStatusRecovered() {
assertRunOnServiceThread();
Intent intent = new Intent(HdmiControlManager.ACTION_ON_ACTIVE_SOURCE_RECOVERED_DISMISS_UI);
- mService.getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
- HdmiControlService.PERMISSION);
+ mService.sendBroadcastAsUser(intent);
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index d2d0279..936e8b6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -40,6 +40,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -4392,8 +4393,7 @@
assertRunOnServiceThread();
Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
- getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
- HdmiControlService.PERMISSION);
+ sendBroadcastAsUser(intent);
}
@ServiceThreadOnly
@@ -4402,8 +4402,17 @@
Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_EXTRA_PARAM1, extra);
- getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
- HdmiControlService.PERMISSION);
+ sendBroadcastAsUser(intent);
+ }
+
+ // This method is used such that we can override it inside unit tests to avoid a
+ // SecurityException.
+ @ServiceThreadOnly
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ assertRunOnServiceThread();
+ getContext().sendBroadcastAsUser(intent, UserHandle.ALL, HdmiControlService.PERMISSION);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
index 00bc751..ad98b4a 100644
--- a/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
+++ b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
@@ -29,6 +29,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.IInlineSuggestionsResponseCallback;
+import com.android.internal.inputmethod.InlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
/**
@@ -49,12 +50,12 @@
private static final class CreateInlineSuggestionsRequest {
@NonNull final InlineSuggestionsRequestInfo mRequestInfo;
- @NonNull final IInlineSuggestionsRequestCallback mCallback;
+ @NonNull final InlineSuggestionsRequestCallback mCallback;
@NonNull final String mPackageName;
CreateInlineSuggestionsRequest(
@NonNull InlineSuggestionsRequestInfo requestInfo,
- @NonNull IInlineSuggestionsRequestCallback callback,
+ @NonNull InlineSuggestionsRequestCallback callback,
@NonNull String packageName) {
mRequestInfo = requestInfo;
mCallback = callback;
@@ -78,7 +79,7 @@
*/
@GuardedBy("ImfLock.class")
@Nullable
- private IInlineSuggestionsRequestCallback mInlineSuggestionsRequestCallback;
+ private InlineSuggestionsRequestCallback mInlineSuggestionsRequestCallback;
AutofillSuggestionsController(@NonNull InputMethodManagerService service) {
mService = service;
@@ -97,33 +98,30 @@
@GuardedBy("ImfLock.class")
void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
- InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback,
+ InlineSuggestionsRequestInfo requestInfo, InlineSuggestionsRequestCallback callback,
boolean touchExplorationEnabled) {
clearPendingInlineSuggestionsRequest();
mInlineSuggestionsRequestCallback = callback;
final InputMethodInfo imi = mService.queryInputMethodForCurrentUserLocked(
mService.getSelectedMethodIdLocked());
- try {
- if (userId == mService.getCurrentImeUserIdLocked()
- && imi != null && isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) {
- mPendingInlineSuggestionsRequest = new CreateInlineSuggestionsRequest(
- requestInfo, callback, imi.getPackageName());
- if (mService.getCurMethodLocked() != null) {
- // In the normal case when the IME is connected, we can make the request here.
- performOnCreateInlineSuggestionsRequest();
- } else {
- // Otherwise, the next time the IME connection is established,
- // InputMethodBindingController.mMainConnection#onServiceConnected() will call
- // into #performOnCreateInlineSuggestionsRequestLocked() to make the request.
- if (DEBUG) {
- Slog.d(TAG, "IME not connected. Delaying inline suggestions request.");
- }
- }
+
+ if (userId == mService.getCurrentImeUserIdLocked()
+ && imi != null && isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) {
+ mPendingInlineSuggestionsRequest = new CreateInlineSuggestionsRequest(
+ requestInfo, callback, imi.getPackageName());
+ if (mService.getCurMethodLocked() != null) {
+ // In the normal case when the IME is connected, we can make the request here.
+ performOnCreateInlineSuggestionsRequest();
} else {
- callback.onInlineSuggestionsUnsupported();
+ // Otherwise, the next time the IME connection is established,
+ // InputMethodBindingController.mMainConnection#onServiceConnected() will call
+ // into #performOnCreateInlineSuggestionsRequestLocked() to make the request.
+ if (DEBUG) {
+ Slog.d(TAG, "IME not connected. Delaying inline suggestions request.");
+ }
}
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException calling onCreateInlineSuggestionsRequest(): " + e);
+ } else {
+ callback.onInlineSuggestionsUnsupported();
}
}
@@ -166,11 +164,7 @@
@GuardedBy("ImfLock.class")
void invalidateAutofillSession() {
if (mInlineSuggestionsRequestCallback != null) {
- try {
- mInlineSuggestionsRequestCallback.onInlineSuggestionsSessionInvalidated();
- } catch (RemoteException e) {
- Slog.e(TAG, "Cannot invalidate autofill session.", e);
- }
+ mInlineSuggestionsRequestCallback.onInlineSuggestionsSessionInvalidated();
}
}
@@ -180,13 +174,13 @@
*/
private final class InlineSuggestionsRequestCallbackDecorator
extends IInlineSuggestionsRequestCallback.Stub {
- @NonNull private final IInlineSuggestionsRequestCallback mCallback;
+ @NonNull private final InlineSuggestionsRequestCallback mCallback;
@NonNull private final String mImePackageName;
private final int mImeDisplayId;
@NonNull private final IBinder mImeToken;
InlineSuggestionsRequestCallbackDecorator(
- @NonNull IInlineSuggestionsRequestCallback callback, @NonNull String imePackageName,
+ @NonNull InlineSuggestionsRequestCallback callback, @NonNull String imePackageName,
int displayId, @NonNull IBinder imeToken) {
mCallback = callback;
mImePackageName = imePackageName;
@@ -195,7 +189,7 @@
}
@Override
- public void onInlineSuggestionsUnsupported() throws RemoteException {
+ public void onInlineSuggestionsUnsupported() {
mCallback.onInlineSuggestionsUnsupported();
}
@@ -220,32 +214,32 @@
}
@Override
- public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
+ public void onInputMethodStartInput(AutofillId imeFieldId) {
mCallback.onInputMethodStartInput(imeFieldId);
}
@Override
- public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
+ public void onInputMethodShowInputRequested(boolean requestResult) {
mCallback.onInputMethodShowInputRequested(requestResult);
}
@Override
- public void onInputMethodStartInputView() throws RemoteException {
+ public void onInputMethodStartInputView() {
mCallback.onInputMethodStartInputView();
}
@Override
- public void onInputMethodFinishInputView() throws RemoteException {
+ public void onInputMethodFinishInputView() {
mCallback.onInputMethodFinishInputView();
}
@Override
- public void onInputMethodFinishInput() throws RemoteException {
+ public void onInputMethodFinishInput() {
mCallback.onInputMethodFinishInput();
}
@Override
- public void onInlineSuggestionsSessionInvalidated() throws RemoteException {
+ public void onInlineSuggestionsSessionInvalidated() {
mCallback.onInlineSuggestionsSessionInvalidated();
}
}
diff --git a/services/core/java/com/android/server/inputmethod/ImeBindingState.java b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
index 4c20c3b..f78ea84 100644
--- a/services/core/java/com/android/server/inputmethod/ImeBindingState.java
+++ b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
@@ -21,7 +21,9 @@
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.os.IBinder;
+import android.os.UserHandle;
import android.util.Printer;
import android.util.proto.ProtoOutputStream;
import android.view.WindowManager;
@@ -36,6 +38,9 @@
*/
final class ImeBindingState {
+ @UserIdInt
+ final int mUserId;
+
/**
* The last window token that we confirmed to be focused. This is always updated upon
* reports from the input method client. If the window state is already changed before the
@@ -90,6 +95,7 @@
static ImeBindingState newEmptyState() {
return new ImeBindingState(
+ /*userId=*/ UserHandle.USER_NULL,
/*focusedWindow=*/ null,
/*focusedWindowSoftInputMode=*/ SOFT_INPUT_STATE_UNSPECIFIED,
/*focusedWindowClient=*/ null,
@@ -97,10 +103,12 @@
);
}
- ImeBindingState(@Nullable IBinder focusedWindow,
+ ImeBindingState(@UserIdInt int userId,
+ @Nullable IBinder focusedWindow,
@SoftInputModeFlags int focusedWindowSoftInputMode,
@Nullable ClientState focusedWindowClient,
@Nullable EditorInfo focusedWindowEditorInfo) {
+ mUserId = userId;
mFocusedWindow = focusedWindow;
mFocusedWindowSoftInputMode = focusedWindowSoftInputMode;
mFocusedWindowClient = focusedWindowClient;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index e8543f2..dace67f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -25,7 +25,7 @@
import android.view.inputmethod.InputMethodInfo;
import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
-import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
+import com.android.internal.inputmethod.InlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.server.LocalServices;
@@ -86,11 +86,11 @@
*
* @param userId the user ID to be queried
* @param requestInfo information needed to create an {@link InlineSuggestionsRequest}.
- * @param cb {@link IInlineSuggestionsRequestCallback} used to pass back the request
+ * @param cb {@link InlineSuggestionsRequestCallback} used to pass back the request
* object
*/
public abstract void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
- InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb);
+ InlineSuggestionsRequestInfo requestInfo, InlineSuggestionsRequestCallback cb);
/**
* Force switch to the enabled input method by {@code imeId} for current user. If the input
@@ -263,7 +263,7 @@
@Override
public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
InlineSuggestionsRequestInfo requestInfo,
- IInlineSuggestionsRequestCallback cb) {
+ InlineSuggestionsRequestCallback cb) {
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 691145c..954f9bc 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -147,7 +147,6 @@
import com.android.internal.inputmethod.IBooleanListener;
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
import com.android.internal.inputmethod.IImeTracker;
-import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethod;
import com.android.internal.inputmethod.IInputMethodClient;
@@ -157,6 +156,7 @@
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteInputConnection;
import com.android.internal.inputmethod.ImeTracing;
+import com.android.internal.inputmethod.InlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.InputMethodDebug;
@@ -1024,8 +1024,16 @@
}
private void onFinishPackageChangesInternal() {
+ final int userId = getChangingUserId();
+
+ // Instantiating InputMethodInfo requires disk I/O.
+ // Do them before acquiring the lock to minimize the chances of ANR (b/340221861).
+ final var newMethodMapWithoutAdditionalSubtypes =
+ queryInputMethodServicesInternal(mContext, userId,
+ AdditionalSubtypeMap.EMPTY_MAP, DirectBootAwareness.AUTO)
+ .getMethodMap();
+
synchronized (ImfLock.class) {
- final int userId = getChangingUserId();
final boolean isCurrentUser = (userId == mCurrentUserId);
final AdditionalSubtypeMap additionalSubtypeMap =
AdditionalSubtypeMapRepository.get(userId);
@@ -1077,9 +1085,10 @@
&& !(additionalSubtypeChanged || shouldRebuildInputMethodListLocked())) {
return;
}
-
- final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
- userId, newAdditionalSubtypeMap, DirectBootAwareness.AUTO);
+ final var newMethodMap = newMethodMapWithoutAdditionalSubtypes
+ .applyAdditionalSubtypes(newAdditionalSubtypeMap);
+ final InputMethodSettings newSettings =
+ InputMethodSettings.create(newMethodMap, userId);
InputMethodSettingsRepository.put(userId, newSettings);
if (!isCurrentUser) {
return;
@@ -3712,7 +3721,8 @@
null, null, null, null, -1, false);
}
- mImeBindingState = new ImeBindingState(windowToken, softInputMode, cs, editorInfo);
+ mImeBindingState = new ImeBindingState(userData.mUserId, windowToken, softInputMode, cs,
+ editorInfo);
mFocusedWindowPerceptible.put(windowToken, true);
// We want to start input before showing the IME, but after closing
@@ -5476,7 +5486,7 @@
@Override
public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
- InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb) {
+ InlineSuggestionsRequestInfo requestInfo, InlineSuggestionsRequestCallback cb) {
// Get the device global touch exploration state before lock to avoid deadlock.
final boolean touchExplorationEnabled = AccessibilityManagerInternal.get()
.isTouchExplorationEnabled(userId);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMap.java b/services/core/java/com/android/server/inputmethod/InputMethodMap.java
index a8e5e2e..221309e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMap.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMap.java
@@ -75,4 +75,28 @@
int size() {
return mMap.size();
}
+
+ @AnyThread
+ @NonNull
+ public InputMethodMap applyAdditionalSubtypes(
+ @NonNull AdditionalSubtypeMap additionalSubtypeMap) {
+ if (additionalSubtypeMap.isEmpty()) {
+ return this;
+ }
+ final int size = size();
+ final ArrayMap<String, InputMethodInfo> newMethodMap = new ArrayMap<>(size);
+ boolean updated = false;
+ for (int i = 0; i < size; ++i) {
+ final var imi = valueAt(i);
+ final var imeId = imi.getId();
+ final var newAdditionalSubtypes = additionalSubtypeMap.get(imeId);
+ if (newAdditionalSubtypes == null || newAdditionalSubtypes.isEmpty()) {
+ newMethodMap.put(imi.getId(), imi);
+ } else {
+ newMethodMap.put(imi.getId(), new InputMethodInfo(imi, newAdditionalSubtypes));
+ updated = true;
+ }
+ }
+ return updated ? InputMethodMap.of(newMethodMap) : this;
+ }
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index a0dbfa0..ec94e2b 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -412,10 +412,15 @@
/* package */
synchronized void addTransaction(
ContextHubServiceTransaction transaction) throws IllegalStateException {
+ if (transaction == null) {
+ return;
+ }
+
if (mTransactionQueue.size() == MAX_PENDING_REQUESTS) {
throw new IllegalStateException("Transaction queue is full (capacity = "
+ MAX_PENDING_REQUESTS + ")");
}
+
mTransactionQueue.add(transaction);
mTransactionRecordDeque.add(new TransactionRecord(transaction.toString()));
@@ -517,7 +522,10 @@
* the caller has obtained a lock on this ContextHubTransactionManager object.
*/
private void removeTransactionAndStartNext() {
- mTimeoutFuture.cancel(false /* mayInterruptIfRunning */);
+ if (mTimeoutFuture != null) {
+ mTimeoutFuture.cancel(/* mayInterruptIfRunning= */ false);
+ mTimeoutFuture = null;
+ }
ContextHubServiceTransaction transaction = mTransactionQueue.remove();
transaction.setComplete();
diff --git a/services/core/java/com/android/server/net/NetworkManagementService.java b/services/core/java/com/android/server/net/NetworkManagementService.java
index d25f529..5ea3e70 100644
--- a/services/core/java/com/android/server/net/NetworkManagementService.java
+++ b/services/core/java/com/android/server/net/NetworkManagementService.java
@@ -20,6 +20,9 @@
import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
@@ -31,6 +34,9 @@
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_BACKGROUND;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_ALLOW;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_USER;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
@@ -143,6 +149,8 @@
private final Object mQuotaLock = new Object();
private final Object mRulesLock = new Object();
+ private final boolean mUseMeteredFirewallChains;
+
/** Set of interfaces with active quotas. */
@GuardedBy("mQuotaLock")
private HashMap<String, Long> mActiveQuotas = Maps.newHashMap();
@@ -150,9 +158,11 @@
@GuardedBy("mQuotaLock")
private HashMap<String, Long> mActiveAlerts = Maps.newHashMap();
/** Set of UIDs denied on metered networks. */
+ // TODO: b/336693007 - Remove once NPMS has completely migrated to metered firewall chains.
@GuardedBy("mRulesLock")
private SparseBooleanArray mUidRejectOnMetered = new SparseBooleanArray();
/** Set of UIDs allowed on metered networks. */
+ // TODO: b/336693007 - Remove once NPMS has completely migrated to metered firewall chains.
@GuardedBy("mRulesLock")
private SparseBooleanArray mUidAllowOnMetered = new SparseBooleanArray();
/** Set of UIDs with cleartext penalties. */
@@ -196,10 +206,32 @@
@GuardedBy("mRulesLock")
private final SparseIntArray mUidFirewallBackgroundRules = new SparseIntArray();
+ /**
+ * Contains the per-UID firewall rules that are used to allowlist the app from metered-network
+ * restrictions when data saver is enabled.
+ */
+ @GuardedBy("mRulesLock")
+ private final SparseIntArray mUidMeteredFirewallAllowRules = new SparseIntArray();
+
+ /**
+ * Contains the per-UID firewall rules that are used to deny app access to metered networks
+ * due to user action.
+ */
+ @GuardedBy("mRulesLock")
+ private final SparseIntArray mUidMeteredFirewallDenyUserRules = new SparseIntArray();
+
+ /**
+ * Contains the per-UID firewall rules that are used to deny app access to metered networks
+ * due to admin action.
+ */
+ @GuardedBy("mRulesLock")
+ private final SparseIntArray mUidMeteredFirewallDenyAdminRules = new SparseIntArray();
+
/** Set of states for the child firewall chains. True if the chain is active. */
@GuardedBy("mRulesLock")
final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
+ // TODO: b/336693007 - Remove once NPMS has completely migrated to metered firewall chains.
@GuardedBy("mQuotaLock")
private volatile boolean mDataSaverMode;
@@ -217,6 +249,15 @@
mContext = context;
mDeps = deps;
+ mUseMeteredFirewallChains = Flags.useMeteredFirewallChains();
+
+ if (mUseMeteredFirewallChains) {
+ // These firewalls are always on and currently ConnectivityService does not allow
+ // changing their enabled state.
+ mFirewallChainStates.put(FIREWALL_CHAIN_METERED_DENY_USER, true);
+ mFirewallChainStates.put(FIREWALL_CHAIN_METERED_DENY_ADMIN, true);
+ }
+
mDaemonHandler = new Handler(FgThread.get().getLooper());
mNetdUnsolicitedEventListener = new NetdUnsolicitedEventListener();
@@ -410,33 +451,39 @@
}
}
- SparseBooleanArray uidRejectOnQuota = null;
- SparseBooleanArray uidAcceptOnQuota = null;
- synchronized (mRulesLock) {
- size = mUidRejectOnMetered.size();
- if (size > 0) {
- if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered denylist rules");
- uidRejectOnQuota = mUidRejectOnMetered;
- mUidRejectOnMetered = new SparseBooleanArray();
- }
+ if (!mUseMeteredFirewallChains) {
+ SparseBooleanArray uidRejectOnQuota = null;
+ SparseBooleanArray uidAcceptOnQuota = null;
+ synchronized (mRulesLock) {
+ size = mUidRejectOnMetered.size();
+ if (size > 0) {
+ if (DBG) {
+ Slog.d(TAG, "Pushing " + size + " UIDs to metered denylist rules");
+ }
+ uidRejectOnQuota = mUidRejectOnMetered;
+ mUidRejectOnMetered = new SparseBooleanArray();
+ }
- size = mUidAllowOnMetered.size();
- if (size > 0) {
- if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered allowlist rules");
- uidAcceptOnQuota = mUidAllowOnMetered;
- mUidAllowOnMetered = new SparseBooleanArray();
+ size = mUidAllowOnMetered.size();
+ if (size > 0) {
+ if (DBG) {
+ Slog.d(TAG, "Pushing " + size + " UIDs to metered allowlist rules");
+ }
+ uidAcceptOnQuota = mUidAllowOnMetered;
+ mUidAllowOnMetered = new SparseBooleanArray();
+ }
}
- }
- if (uidRejectOnQuota != null) {
- for (int i = 0; i < uidRejectOnQuota.size(); i++) {
- setUidOnMeteredNetworkDenylist(uidRejectOnQuota.keyAt(i),
- uidRejectOnQuota.valueAt(i));
+ if (uidRejectOnQuota != null) {
+ for (int i = 0; i < uidRejectOnQuota.size(); i++) {
+ setUidOnMeteredNetworkDenylist(uidRejectOnQuota.keyAt(i),
+ uidRejectOnQuota.valueAt(i));
+ }
}
- }
- if (uidAcceptOnQuota != null) {
- for (int i = 0; i < uidAcceptOnQuota.size(); i++) {
- setUidOnMeteredNetworkAllowlist(uidAcceptOnQuota.keyAt(i),
- uidAcceptOnQuota.valueAt(i));
+ if (uidAcceptOnQuota != null) {
+ for (int i = 0; i < uidAcceptOnQuota.size(); i++) {
+ setUidOnMeteredNetworkAllowlist(uidAcceptOnQuota.keyAt(i),
+ uidAcceptOnQuota.valueAt(i));
+ }
}
}
@@ -459,8 +506,16 @@
syncFirewallChainLocked(FIREWALL_CHAIN_RESTRICTED, "restricted ");
syncFirewallChainLocked(FIREWALL_CHAIN_LOW_POWER_STANDBY, "low power standby ");
syncFirewallChainLocked(FIREWALL_CHAIN_BACKGROUND, FIREWALL_CHAIN_NAME_BACKGROUND);
+ if (mUseMeteredFirewallChains) {
+ syncFirewallChainLocked(FIREWALL_CHAIN_METERED_ALLOW,
+ FIREWALL_CHAIN_NAME_METERED_ALLOW);
+ syncFirewallChainLocked(FIREWALL_CHAIN_METERED_DENY_USER,
+ FIREWALL_CHAIN_NAME_METERED_DENY_USER);
+ syncFirewallChainLocked(FIREWALL_CHAIN_METERED_DENY_ADMIN,
+ FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN);
+ }
- final int[] chains = {
+ final int[] chainsToEnable = {
FIREWALL_CHAIN_STANDBY,
FIREWALL_CHAIN_DOZABLE,
FIREWALL_CHAIN_POWERSAVE,
@@ -469,14 +524,13 @@
FIREWALL_CHAIN_BACKGROUND,
};
- for (int chain : chains) {
+ for (int chain : chainsToEnable) {
if (getFirewallChainState(chain)) {
setFirewallChainEnabled(chain, true);
}
}
}
-
try {
getBatteryStats().noteNetworkStatsEnabled();
} catch (RemoteException e) {
@@ -1077,6 +1131,14 @@
mContext.getSystemService(ConnectivityManager.class)
.setDataSaverEnabled(enable);
mDataSaverMode = enable;
+ if (mUseMeteredFirewallChains) {
+ // Copy mDataSaverMode state to FIREWALL_CHAIN_METERED_ALLOW
+ // until ConnectivityService allows manipulation of the data saver mode via
+ // FIREWALL_CHAIN_METERED_ALLOW.
+ synchronized (mRulesLock) {
+ mFirewallChainStates.put(FIREWALL_CHAIN_METERED_ALLOW, enable);
+ }
+ }
return true;
} else {
final boolean changed = mNetdService.bandwidthEnableDataSaver(enable);
@@ -1191,9 +1253,9 @@
setFirewallChainState(chain, enable);
}
- final String chainName = getFirewallChainName(chain);
- if (chain == FIREWALL_CHAIN_NONE) {
- throw new IllegalArgumentException("Bad child chain: " + chainName);
+ if (!isValidFirewallChainForSetEnabled(chain)) {
+ throw new IllegalArgumentException("Invalid chain for setFirewallChainEnabled: "
+ + NetworkPolicyLogger.getFirewallChainName(chain));
}
final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
@@ -1205,38 +1267,29 @@
}
}
- private String getFirewallChainName(int chain) {
- switch (chain) {
- case FIREWALL_CHAIN_STANDBY:
- return FIREWALL_CHAIN_NAME_STANDBY;
- case FIREWALL_CHAIN_DOZABLE:
- return FIREWALL_CHAIN_NAME_DOZABLE;
- case FIREWALL_CHAIN_POWERSAVE:
- return FIREWALL_CHAIN_NAME_POWERSAVE;
- case FIREWALL_CHAIN_RESTRICTED:
- return FIREWALL_CHAIN_NAME_RESTRICTED;
- case FIREWALL_CHAIN_LOW_POWER_STANDBY:
- return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
- case FIREWALL_CHAIN_BACKGROUND:
- return FIREWALL_CHAIN_NAME_BACKGROUND;
- default:
- throw new IllegalArgumentException("Bad child chain: " + chain);
- }
+ private boolean isValidFirewallChainForSetEnabled(int chain) {
+ return switch (chain) {
+ case FIREWALL_CHAIN_STANDBY, FIREWALL_CHAIN_DOZABLE, FIREWALL_CHAIN_POWERSAVE,
+ FIREWALL_CHAIN_RESTRICTED, FIREWALL_CHAIN_LOW_POWER_STANDBY,
+ FIREWALL_CHAIN_BACKGROUND -> true;
+ // METERED_* firewall chains are not yet supported by
+ // ConnectivityService#setFirewallChainEnabled.
+ default -> false;
+ };
}
private int getFirewallType(int chain) {
switch (chain) {
case FIREWALL_CHAIN_STANDBY:
+ case FIREWALL_CHAIN_METERED_DENY_ADMIN:
+ case FIREWALL_CHAIN_METERED_DENY_USER:
return FIREWALL_DENYLIST;
case FIREWALL_CHAIN_DOZABLE:
- return FIREWALL_ALLOWLIST;
case FIREWALL_CHAIN_POWERSAVE:
- return FIREWALL_ALLOWLIST;
case FIREWALL_CHAIN_RESTRICTED:
- return FIREWALL_ALLOWLIST;
case FIREWALL_CHAIN_LOW_POWER_STANDBY:
- return FIREWALL_ALLOWLIST;
case FIREWALL_CHAIN_BACKGROUND:
+ case FIREWALL_CHAIN_METERED_ALLOW:
return FIREWALL_ALLOWLIST;
default:
return isFirewallEnabled() ? FIREWALL_ALLOWLIST : FIREWALL_DENYLIST;
@@ -1360,6 +1413,12 @@
return mUidFirewallLowPowerStandbyRules;
case FIREWALL_CHAIN_BACKGROUND:
return mUidFirewallBackgroundRules;
+ case FIREWALL_CHAIN_METERED_ALLOW:
+ return mUidMeteredFirewallAllowRules;
+ case FIREWALL_CHAIN_METERED_DENY_USER:
+ return mUidMeteredFirewallDenyUserRules;
+ case FIREWALL_CHAIN_METERED_DENY_ADMIN:
+ return mUidMeteredFirewallDenyAdminRules;
case FIREWALL_CHAIN_NONE:
return mUidFirewallRules;
default:
@@ -1378,6 +1437,10 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ pw.println("Flags:");
+ pw.println(Flags.FLAG_USE_METERED_FIREWALL_CHAINS + ": " + mUseMeteredFirewallChains);
+ pw.println();
+
synchronized (mQuotaLock) {
pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
@@ -1416,6 +1479,27 @@
pw.print("UID firewall background chain enabled: ");
pw.println(getFirewallChainState(FIREWALL_CHAIN_BACKGROUND));
dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_BACKGROUND, mUidFirewallBackgroundRules);
+
+ pw.print("UID firewall metered allow chain enabled (Data saver mode): ");
+ // getFirewallChainState should maintain a duplicated state from mDataSaverMode when
+ // mUseMeteredFirewallChains is enabled.
+ pw.println(getFirewallChainState(FIREWALL_CHAIN_METERED_ALLOW));
+ dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_METERED_ALLOW,
+ mUidMeteredFirewallAllowRules);
+
+ pw.print("UID firewall metered deny_user chain enabled (always-on): ");
+ // This always-on state should be reflected by getFirewallChainState when
+ // mUseMeteredFirewallChains is enabled.
+ pw.println(getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_USER));
+ dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_METERED_DENY_USER,
+ mUidMeteredFirewallDenyUserRules);
+
+ pw.print("UID firewall metered deny_admin chain enabled (always-on): ");
+ // This always-on state should be reflected by getFirewallChainState when
+ // mUseMeteredFirewallChains is enabled.
+ pw.println(getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_ADMIN));
+ dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN,
+ mUidMeteredFirewallDenyAdminRules);
}
pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
@@ -1520,14 +1604,40 @@
if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because it is in background");
return true;
}
- if (mUidRejectOnMetered.get(uid)) {
- if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of no metered data"
- + " in the background");
- return true;
- }
- if (mDataSaverMode && !mUidAllowOnMetered.get(uid)) {
- if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of data saver mode");
- return true;
+ if (mUseMeteredFirewallChains) {
+ if (getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_USER)
+ && mUidMeteredFirewallDenyUserRules.get(uid) == FIREWALL_RULE_DENY) {
+ if (DBG) {
+ Slog.d(TAG, "Uid " + uid + " restricted because of user-restricted metered"
+ + " data in the background");
+ }
+ return true;
+ }
+ if (getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_ADMIN)
+ && mUidMeteredFirewallDenyAdminRules.get(uid) == FIREWALL_RULE_DENY) {
+ if (DBG) {
+ Slog.d(TAG, "Uid " + uid + " restricted because of admin-restricted metered"
+ + " data in the background");
+ }
+ return true;
+ }
+ if (getFirewallChainState(FIREWALL_CHAIN_METERED_ALLOW)
+ && mUidMeteredFirewallAllowRules.get(uid) != FIREWALL_RULE_ALLOW) {
+ if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of data saver mode");
+ return true;
+ }
+ } else {
+ if (mUidRejectOnMetered.get(uid)) {
+ if (DBG) {
+ Slog.d(TAG, "Uid " + uid
+ + " restricted because of no metered data in the background");
+ }
+ return true;
+ }
+ if (mDataSaverMode && !mUidAllowOnMetered.get(uid)) {
+ if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of data saver mode");
+ return true;
+ }
}
return false;
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 8e2d778..681aa8a 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -19,6 +19,9 @@
import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
@@ -28,6 +31,9 @@
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_BACKGROUND;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_ALLOW;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_USER;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
@@ -379,7 +385,7 @@
return "Interfaces of netId=" + netId + " changed to " + newIfaces;
}
- private static String getFirewallChainName(int chain) {
+ static String getFirewallChainName(int chain) {
switch (chain) {
case FIREWALL_CHAIN_DOZABLE:
return FIREWALL_CHAIN_NAME_DOZABLE;
@@ -393,6 +399,12 @@
return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
case FIREWALL_CHAIN_BACKGROUND:
return FIREWALL_CHAIN_NAME_BACKGROUND;
+ case FIREWALL_CHAIN_METERED_ALLOW:
+ return FIREWALL_CHAIN_NAME_METERED_ALLOW;
+ case FIREWALL_CHAIN_METERED_DENY_USER:
+ return FIREWALL_CHAIN_NAME_METERED_DENY_USER;
+ case FIREWALL_CHAIN_METERED_DENY_ADMIN:
+ return FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN;
default:
return String.valueOf(chain);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 22f5332..c60ac3a 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -60,6 +60,9 @@
import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
@@ -514,6 +517,12 @@
*/
private boolean mBackgroundNetworkRestricted;
+ /**
+ * Whether or not metered firewall chains should be used for uid policy controlling access to
+ * metered networks.
+ */
+ private boolean mUseMeteredFirewallChains;
+
// See main javadoc for instructions on how to use these locks.
final Object mUidRulesFirstLock = new Object();
final Object mNetworkPoliciesSecondLock = new Object();
@@ -997,6 +1006,8 @@
mAppStandby = LocalServices.getService(AppStandbyInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mUseMeteredFirewallChains = Flags.useMeteredFirewallChains();
+
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
updatePowerSaveAllowlistUL();
@@ -4030,8 +4041,10 @@
fout.println();
fout.println("Flags:");
- fout.println("Network blocked for TOP_SLEEPING and above: "
+ fout.println(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE + ": "
+ mBackgroundNetworkRestricted);
+ fout.println(Flags.FLAG_USE_METERED_FIREWALL_CHAINS + ": "
+ + mUseMeteredFirewallChains);
fout.println();
fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode);
@@ -5373,23 +5386,44 @@
postUidRulesChangedMsg(uid, uidRules);
}
- // Note that the conditionals below are for avoiding unnecessary calls to netd.
- // TODO: Measure the performance for doing a no-op call to netd so that we can
- // remove the conditionals to simplify the logic below. We can also further reduce
- // some calls to netd if they turn out to be costly.
- final int denylistReasons = BLOCKED_METERED_REASON_ADMIN_DISABLED
- | BLOCKED_METERED_REASON_USER_RESTRICTED;
- if ((oldEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE
- || (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE) {
- setMeteredNetworkDenylist(uid,
- (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE);
- }
- final int allowlistReasons = ALLOWED_METERED_REASON_FOREGROUND
- | ALLOWED_METERED_REASON_USER_EXEMPTED;
- if ((oldAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE
- || (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE) {
- setMeteredNetworkAllowlist(uid,
- (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE);
+ if (mUseMeteredFirewallChains) {
+ if ((newEffectiveBlockedReasons & BLOCKED_METERED_REASON_ADMIN_DISABLED)
+ != BLOCKED_REASON_NONE) {
+ setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_ADMIN, uid, FIREWALL_RULE_DENY);
+ } else {
+ setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_ADMIN, uid, FIREWALL_RULE_DEFAULT);
+ }
+ if ((newEffectiveBlockedReasons & BLOCKED_METERED_REASON_USER_RESTRICTED)
+ != BLOCKED_REASON_NONE) {
+ setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_USER, uid, FIREWALL_RULE_DENY);
+ } else {
+ setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_USER, uid, FIREWALL_RULE_DEFAULT);
+ }
+ if ((newAllowedReasons & (ALLOWED_METERED_REASON_FOREGROUND
+ | ALLOWED_METERED_REASON_USER_EXEMPTED)) != ALLOWED_REASON_NONE) {
+ setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_ALLOW, uid, FIREWALL_RULE_ALLOW);
+ } else {
+ setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_ALLOW, uid, FIREWALL_RULE_DEFAULT);
+ }
+ } else {
+ // Note that the conditionals below are for avoiding unnecessary calls to netd.
+ // TODO: Measure the performance for doing a no-op call to netd so that we can
+ // remove the conditionals to simplify the logic below. We can also further reduce
+ // some calls to netd if they turn out to be costly.
+ final int denylistReasons = BLOCKED_METERED_REASON_ADMIN_DISABLED
+ | BLOCKED_METERED_REASON_USER_RESTRICTED;
+ if ((oldEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE
+ || (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE) {
+ setMeteredNetworkDenylist(uid,
+ (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE);
+ }
+ final int allowlistReasons = ALLOWED_METERED_REASON_FOREGROUND
+ | ALLOWED_METERED_REASON_USER_EXEMPTED;
+ if ((oldAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE
+ || (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE) {
+ setMeteredNetworkAllowlist(uid,
+ (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE);
+ }
}
}
@@ -6149,6 +6183,8 @@
} else if (chain == FIREWALL_CHAIN_BACKGROUND) {
mUidFirewallBackgroundRules.put(uid, rule);
}
+ // Note that we do not need keep a separate cache of uid rules for chains that we do
+ // not call #setUidFirewallRulesUL for.
try {
mNetworkManager.setFirewallUidRule(chain, uid, rule);
@@ -6206,10 +6242,19 @@
FIREWALL_RULE_DEFAULT);
mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, uid,
FIREWALL_RULE_DEFAULT);
- mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false);
- mLogger.meteredAllowlistChanged(uid, false);
- mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false);
- mLogger.meteredDenylistChanged(uid, false);
+ if (mUseMeteredFirewallChains) {
+ mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, uid,
+ FIREWALL_RULE_DEFAULT);
+ mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_USER, uid,
+ FIREWALL_RULE_DEFAULT);
+ mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_METERED_ALLOW, uid,
+ FIREWALL_RULE_DEFAULT);
+ } else {
+ mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false);
+ mLogger.meteredAllowlistChanged(uid, false);
+ mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false);
+ mLogger.meteredDenylistChanged(uid, false);
+ }
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem resetting firewall uid rules for " + uid, e);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig
index d9491de..e986dd8 100644
--- a/services/core/java/com/android/server/net/flags.aconfig
+++ b/services/core/java/com/android/server/net/flags.aconfig
@@ -7,3 +7,13 @@
description: "Block network access for apps in a low importance background state"
bug: "304347838"
}
+
+flag {
+ name: "use_metered_firewall_chains"
+ namespace: "backstage_power"
+ description: "Use metered firewall chains to control access to metered networks"
+ bug: "336693007"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index bf49671..a78b3a2 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -135,7 +135,7 @@
private LogicalLight mAttentionLight;
private final boolean mUseAttentionLight;
- boolean mHasLight = true;
+ boolean mHasLight;
private final SettingsObserver mSettingsObserver;
@@ -149,7 +149,7 @@
private boolean mInCallStateOffHook = false;
private boolean mScreenOn = true;
private boolean mUserPresent = false;
- boolean mNotificationPulseEnabled;
+ private boolean mNotificationPulseEnabled;
private final Uri mInCallNotificationUri;
private final AudioAttributes mInCallNotificationAudioAttributes;
private final float mInCallNotificationVolume;
@@ -305,6 +305,13 @@
}
private void loadUserSettings() {
+ boolean pulseEnabled = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0;
+ if (mNotificationPulseEnabled != pulseEnabled) {
+ mNotificationPulseEnabled = pulseEnabled;
+ updateLightsLocked();
+ }
+
if (Flags.politeNotifications()) {
try {
mCurrentWorkProfileId = getManagedProfileId(ActivityManager.getCurrentUser());
@@ -874,6 +881,9 @@
boolean canShowLightsLocked(final NotificationRecord record, final Signals signals,
boolean aboveThreshold) {
+ if (!mSystemReady) {
+ return false;
+ }
// device lacks light
if (!mHasLight) {
return false;
@@ -1721,8 +1731,6 @@
void setLights(LogicalLight light) {
mNotificationLight = light;
mAttentionLight = light;
- mNotificationPulseEnabled = true;
- mHasLight = true;
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4f87c83..4e9c91c 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7225,7 +7225,15 @@
callingUid, userId, true, false, "cancelNotificationWithTag", pkg);
// ensure opPkg is delegate if does not match pkg
- int uid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+
+ int uid = INVALID_UID;
+
+ try {
+ uid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+ } catch (NameNotFoundException e) {
+ // package either never existed so there's no posted notification or it's being
+ // uninstalled so we'll be cleaning it up soon. log and return immediately below.
+ }
if (uid == INVALID_UID) {
Slog.w(TAG, opPkg + ":" + callingUid + " trying to cancel notification "
@@ -7319,7 +7327,13 @@
// Can throw a SecurityException if the calling uid doesn't have permission to post
// as "pkg"
- final int notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+ int notificationUid = INVALID_UID;
+
+ try {
+ notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+ } catch (NameNotFoundException e) {
+ // not great - throw immediately below
+ }
if (notificationUid == INVALID_UID) {
throw new SecurityException("Caller " + opPkg + ":" + callingUid
@@ -7876,7 +7890,8 @@
}
@VisibleForTesting
- int resolveNotificationUid(String callingPkg, String targetPkg, int callingUid, int userId) {
+ int resolveNotificationUid(String callingPkg, String targetPkg, int callingUid, int userId)
+ throws NameNotFoundException {
if (userId == USER_ALL) {
userId = USER_SYSTEM;
}
@@ -7887,12 +7902,8 @@
return callingUid;
}
- int targetUid = INVALID_UID;
- try {
- targetUid = mPackageManagerClient.getPackageUidAsUser(targetPkg, userId);
- } catch (NameNotFoundException e) {
- /* ignore, handled by caller */
- }
+ int targetUid = mPackageManagerClient.getPackageUidAsUser(targetPkg, userId);
+
// posted from app A on behalf of app B
if (isCallerAndroid(callingPkg, callingUid)
|| mPreferencesHelper.isDelegateAllowed(
diff --git a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java b/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
index 681dd0b..96ab2cc 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
@@ -33,6 +33,7 @@
import android.os.BadParcelableException;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.RemoteException;
@@ -41,7 +42,10 @@
import android.system.Os;
import android.util.Log;
+import com.android.internal.infra.AndroidFuture;
+
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
/**
* Util methods for ensuring the Bundle passed in various methods are read-only and restricted to
@@ -78,16 +82,16 @@
if (canMarshall(obj) || obj instanceof CursorWindow) {
continue;
}
-
- if (obj instanceof ParcelFileDescriptor) {
+ if (obj instanceof Bundle) {
+ sanitizeInferenceParams((Bundle) obj);
+ } else if (obj instanceof ParcelFileDescriptor) {
validatePfdReadOnly((ParcelFileDescriptor) obj);
} else if (obj instanceof SharedMemory) {
((SharedMemory) obj).setProtect(PROT_READ);
} else if (obj instanceof Bitmap) {
- if (((Bitmap) obj).isMutable()) {
- throw new BadParcelableException(
- "Encountered a mutable Bitmap in the Bundle at key : " + key);
- }
+ validateBitmap((Bitmap) obj);
+ } else if (obj instanceof Parcelable[]) {
+ validateParcelableArray((Parcelable[]) obj);
} else {
throw new BadParcelableException(
"Unsupported Parcelable type encountered in the Bundle: "
@@ -125,20 +129,20 @@
continue;
}
- if (obj instanceof ParcelFileDescriptor) {
+ if (obj instanceof Bundle) {
+ sanitizeResponseParams((Bundle) obj);
+ } else if (obj instanceof ParcelFileDescriptor) {
validatePfdReadOnly((ParcelFileDescriptor) obj);
} else if (obj instanceof Bitmap) {
- if (((Bitmap) obj).isMutable()) {
- throw new BadParcelableException(
- "Encountered a mutable Bitmap in the Bundle at key : " + key);
- }
+ validateBitmap((Bitmap) obj);
+ } else if (obj instanceof Parcelable[]) {
+ validateParcelableArray((Parcelable[]) obj);
} else {
throw new BadParcelableException(
"Unsupported Parcelable type encountered in the Bundle: "
+ obj.getClass().getSimpleName());
}
}
- Log.e(TAG, "validateResponseParams : Finished");
}
/**
@@ -183,7 +187,8 @@
public static IStreamingResponseCallback wrapWithValidation(
IStreamingResponseCallback streamingResponseCallback,
- Executor resourceClosingExecutor) {
+ Executor resourceClosingExecutor,
+ AndroidFuture future) {
return new IStreamingResponseCallback.Stub() {
@Override
public void onNewContent(Bundle processedResult) throws RemoteException {
@@ -203,6 +208,7 @@
streamingResponseCallback.onSuccess(resultBundle);
} finally {
resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
+ future.complete(null);
}
}
@@ -210,6 +216,7 @@
public void onFailure(int errorCode, String errorMessage,
PersistableBundle errorParams) throws RemoteException {
streamingResponseCallback.onFailure(errorCode, errorMessage, errorParams);
+ future.completeExceptionally(new TimeoutException());
}
@Override
@@ -237,7 +244,8 @@
}
public static IResponseCallback wrapWithValidation(IResponseCallback responseCallback,
- Executor resourceClosingExecutor) {
+ Executor resourceClosingExecutor,
+ AndroidFuture future) {
return new IResponseCallback.Stub() {
@Override
public void onSuccess(Bundle resultBundle)
@@ -247,6 +255,7 @@
responseCallback.onSuccess(resultBundle);
} finally {
resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
+ future.complete(null);
}
}
@@ -254,6 +263,7 @@
public void onFailure(int errorCode, String errorMessage,
PersistableBundle errorParams) throws RemoteException {
responseCallback.onFailure(errorCode, errorMessage, errorParams);
+ future.completeExceptionally(new TimeoutException());
}
@Override
@@ -280,17 +290,20 @@
}
- public static ITokenInfoCallback wrapWithValidation(ITokenInfoCallback responseCallback) {
+ public static ITokenInfoCallback wrapWithValidation(ITokenInfoCallback responseCallback,
+ AndroidFuture future) {
return new ITokenInfoCallback.Stub() {
@Override
public void onSuccess(TokenInfo tokenInfo) throws RemoteException {
responseCallback.onSuccess(tokenInfo);
+ future.complete(null);
}
@Override
public void onFailure(int errorCode, String errorMessage, PersistableBundle errorParams)
throws RemoteException {
responseCallback.onFailure(errorCode, errorMessage, errorParams);
+ future.completeExceptionally(new TimeoutException());
}
};
}
@@ -310,6 +323,26 @@
}
}
+ private static void validateParcelableArray(Parcelable[] parcelables) {
+ if (parcelables.length > 0
+ && parcelables[0] instanceof ParcelFileDescriptor) {
+ // Safe to cast
+ validatePfdsReadOnly(parcelables);
+ } else if (parcelables.length > 0
+ && parcelables[0] instanceof Bitmap) {
+ validateBitmapsImmutable(parcelables);
+ } else {
+ throw new BadParcelableException(
+ "Could not cast to any known parcelable array");
+ }
+ }
+
+ public static void validatePfdsReadOnly(Parcelable[] pfds) {
+ for (Parcelable pfd : pfds) {
+ validatePfdReadOnly((ParcelFileDescriptor) pfd);
+ }
+ }
+
public static void validatePfdReadOnly(ParcelFileDescriptor pfd) {
if (pfd == null) {
return;
@@ -326,6 +359,19 @@
}
}
+ private static void validateBitmap(Bitmap obj) {
+ if (obj.isMutable()) {
+ throw new BadParcelableException(
+ "Encountered a mutable Bitmap in the Bundle at key : " + obj);
+ }
+ }
+
+ private static void validateBitmapsImmutable(Parcelable[] bitmaps) {
+ for (Parcelable bitmap : bitmaps) {
+ validateBitmap((Bitmap) bitmap);
+ }
+ }
+
public static void tryCloseResource(Bundle bundle) {
if (bundle == null || bundle.isEmpty() || !bundle.hasFileDescriptors()) {
return;
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index 235e3cd..b2e861c 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -33,6 +33,7 @@
import android.app.AppGlobals;
import android.app.ondeviceintelligence.DownloadCallback;
import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.FeatureDetails;
import android.app.ondeviceintelligence.IDownloadCallback;
import android.app.ondeviceintelligence.IFeatureCallback;
import android.app.ondeviceintelligence.IFeatureDetailsCallback;
@@ -64,6 +65,7 @@
import android.os.ShellCallback;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
import android.service.ondeviceintelligence.IOnDeviceSandboxedInferenceService;
import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback;
@@ -82,13 +84,17 @@
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.ondeviceintelligence.callbacks.ListenableDownloadCallback;
import java.io.FileDescriptor;
import java.io.IOException;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* This is the system service for handling calls on the
@@ -135,7 +141,6 @@
@GuardedBy("mLock")
private String[] mTemporaryServiceNames;
-
@GuardedBy("mLock")
private String[] mTemporaryBroadcastKeys;
@GuardedBy("mLock")
@@ -145,6 +150,8 @@
* Handler used to reset the temporary service names.
*/
private Handler mTemporaryHandler;
+ private final @NonNull Handler mMainHandler = new Handler(Looper.getMainLooper());
+
public OnDeviceIntelligenceManagerService(Context context) {
super(context);
@@ -204,8 +211,16 @@
return;
}
ensureRemoteIntelligenceServiceInitialized();
- mRemoteOnDeviceIntelligenceService.run(
- service -> service.getVersion(remoteCallback));
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.getVersion(new RemoteCallback(
+ result -> {
+ remoteCallback.sendResult(result);
+ future.complete(null);
+ }));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
}
@Override
@@ -225,8 +240,25 @@
}
ensureRemoteIntelligenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- mRemoteOnDeviceIntelligenceService.run(
- service -> service.getFeature(callerUid, id, featureCallback));
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.getFeature(callerUid, id, new IFeatureCallback.Stub() {
+ @Override
+ public void onSuccess(Feature result) throws RemoteException {
+ featureCallback.onSuccess(result);
+ future.complete(null);
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) throws RemoteException {
+ featureCallback.onFailure(errorCode, errorMessage, errorParams);
+ future.completeExceptionally(new TimeoutException());
+ }
+ });
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
}
@Override
@@ -246,9 +278,29 @@
}
ensureRemoteIntelligenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- mRemoteOnDeviceIntelligenceService.run(
- service -> service.listFeatures(callerUid,
- listFeaturesCallback));
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.listFeatures(callerUid,
+ new IListFeaturesCallback.Stub() {
+ @Override
+ public void onSuccess(List<Feature> result)
+ throws RemoteException {
+ listFeaturesCallback.onSuccess(result);
+ future.complete(null);
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams)
+ throws RemoteException {
+ listFeaturesCallback.onFailure(errorCode, errorMessage,
+ errorParams);
+ future.completeExceptionally(new TimeoutException());
+ }
+ });
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
}
@Override
@@ -270,9 +322,29 @@
}
ensureRemoteIntelligenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- mRemoteOnDeviceIntelligenceService.run(
- service -> service.getFeatureDetails(callerUid, feature,
- featureDetailsCallback));
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.getFeatureDetails(callerUid, feature,
+ new IFeatureDetailsCallback.Stub() {
+ @Override
+ public void onSuccess(FeatureDetails result)
+ throws RemoteException {
+ future.complete(null);
+ featureDetailsCallback.onSuccess(result);
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams)
+ throws RemoteException {
+ future.completeExceptionally(null);
+ featureDetailsCallback.onFailure(errorCode,
+ errorMessage, errorParams);
+ }
+ });
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
}
@Override
@@ -293,10 +365,20 @@
}
ensureRemoteIntelligenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- mRemoteOnDeviceIntelligenceService.run(
- service -> service.requestFeatureDownload(callerUid, feature,
- wrapCancellationFuture(cancellationSignalFuture),
- downloadCallback));
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ ListenableDownloadCallback listenableDownloadCallback =
+ new ListenableDownloadCallback(
+ downloadCallback,
+ mMainHandler, future, getIdleTimeoutMs());
+ service.requestFeatureDownload(callerUid, feature,
+ wrapCancellationFuture(cancellationSignalFuture),
+ listenableDownloadCallback);
+ return future; // this future has no timeout because, actual download
+ // might take long, but we fail early if there is no progress callbacks.
+ }
+ );
}
@@ -323,11 +405,15 @@
}
ensureRemoteInferenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- result = mRemoteInferenceService.post(
- service -> service.requestTokenInfo(callerUid, feature,
- request,
- wrapCancellationFuture(cancellationSignalFuture),
- wrapWithValidation(tokenInfoCallback)));
+ result = mRemoteInferenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.requestTokenInfo(callerUid, feature,
+ request,
+ wrapCancellationFuture(cancellationSignalFuture),
+ wrapWithValidation(tokenInfoCallback, future));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
resourceClosingExecutor);
} finally {
@@ -362,13 +448,18 @@
}
ensureRemoteInferenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- result = mRemoteInferenceService.post(
- service -> service.processRequest(callerUid, feature,
- request,
- requestType,
- wrapCancellationFuture(cancellationSignalFuture),
- wrapProcessingFuture(processingSignalFuture),
- wrapWithValidation(responseCallback, resourceClosingExecutor)));
+ result = mRemoteInferenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.processRequest(callerUid, feature,
+ request,
+ requestType,
+ wrapCancellationFuture(cancellationSignalFuture),
+ wrapProcessingFuture(processingSignalFuture),
+ wrapWithValidation(responseCallback,
+ resourceClosingExecutor, future));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
resourceClosingExecutor);
} finally {
@@ -402,13 +493,18 @@
}
ensureRemoteInferenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- result = mRemoteInferenceService.post(
- service -> service.processRequestStreaming(callerUid,
- feature,
- request, requestType,
- wrapCancellationFuture(cancellationSignalFuture),
- wrapProcessingFuture(processingSignalFuture),
- streamingCallback));
+ result = mRemoteInferenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.processRequestStreaming(callerUid,
+ feature,
+ request, requestType,
+ wrapCancellationFuture(cancellationSignalFuture),
+ wrapProcessingFuture(processingSignalFuture),
+ wrapWithValidation(streamingCallback,
+ resourceClosingExecutor, future));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
resourceClosingExecutor);
} finally {
@@ -859,4 +955,10 @@
return mTemporaryHandler;
}
+
+ private long getIdleTimeoutMs() {
+ return Settings.Secure.getLongForUser(mContext.getContentResolver(),
+ Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, TimeUnit.HOURS.toMillis(1),
+ mContext.getUserId());
+ }
}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
index 48258d7..ac9747a 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
@@ -22,17 +22,21 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.provider.Settings;
import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
import android.service.ondeviceintelligence.OnDeviceIntelligenceService;
import com.android.internal.infra.ServiceConnector;
+import java.util.concurrent.TimeUnit;
+
/**
* Manages the connection to the remote on-device intelligence service. Also, handles unbinding
* logic set by the service implementation via a Secure Settings flag.
*/
public class RemoteOnDeviceIntelligenceService extends
ServiceConnector.Impl<IOnDeviceIntelligenceService> {
+ private static final long LONG_TIMEOUT = TimeUnit.HOURS.toMillis(4);
private static final String TAG =
RemoteOnDeviceIntelligenceService.class.getSimpleName();
@@ -48,9 +52,15 @@
}
@Override
+ protected long getRequestTimeoutMs() {
+ return LONG_TIMEOUT;
+ }
+
+ @Override
protected long getAutoDisconnectTimeoutMs() {
- // Disable automatic unbinding.
- // TODO: add logic to fetch this flag via SecureSettings.
- return -1;
+ return Settings.Secure.getLongForUser(mContext.getContentResolver(),
+ Settings.Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS,
+ TimeUnit.SECONDS.toMillis(30),
+ mContext.getUserId());
}
}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
index 69ba1d2..18b1383 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
@@ -22,18 +22,24 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.provider.Settings;
import android.service.ondeviceintelligence.IOnDeviceSandboxedInferenceService;
import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
import com.android.internal.infra.ServiceConnector;
+import java.util.concurrent.TimeUnit;
+
/**
- * Manages the connection to the remote on-device sand boxed inference service. Also, handles unbinding
+ * Manages the connection to the remote on-device sand boxed inference service. Also, handles
+ * unbinding
* logic set by the service implementation via a SecureSettings flag.
*/
public class RemoteOnDeviceSandboxedInferenceService extends
ServiceConnector.Impl<IOnDeviceSandboxedInferenceService> {
+ private static final long LONG_TIMEOUT = TimeUnit.HOURS.toMillis(1);
+
/**
* Creates an instance of {@link ServiceConnector}
*
@@ -54,11 +60,17 @@
connect();
}
+ @Override
+ protected long getRequestTimeoutMs() {
+ return LONG_TIMEOUT;
+ }
+
@Override
protected long getAutoDisconnectTimeoutMs() {
- // Disable automatic unbinding.
- // TODO: add logic to fetch this flag via SecureSettings.
- return -1;
+ return Settings.Secure.getLongForUser(mContext.getContentResolver(),
+ Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
+ TimeUnit.SECONDS.toMillis(30),
+ mContext.getUserId());
}
}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java b/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
new file mode 100644
index 0000000..32f0698
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 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.ondeviceintelligence.callbacks;
+
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This class extends the {@link IDownloadCallback} and adds a timeout Runnable to the callback
+ * such that, in the case where the callback methods are not invoked, we do not have to wait for
+ * timeout based on {@link #onDownloadCompleted} which might take minutes or hours to complete in
+ * some cases. Instead, in such cases we rely on the remote service sending progress updates and if
+ * there are *no* progress callbacks in the duration of {@link #idleTimeoutMs}, we can assume the
+ * download will not complete and enabling faster cleanup.
+ */
+public class ListenableDownloadCallback extends IDownloadCallback.Stub implements Runnable {
+ private final IDownloadCallback callback;
+ private final Handler handler;
+ private final AndroidFuture future;
+ private final long idleTimeoutMs;
+
+ /**
+ * Constructor to create a ListenableDownloadCallback.
+ *
+ * @param callback callback to send download updates to caller.
+ * @param handler handler to schedule timeout runnable.
+ * @param future future to complete to signal the callback has reached a terminal state.
+ * @param idleTimeoutMs timeout within which download updates should be received.
+ */
+ public ListenableDownloadCallback(IDownloadCallback callback, Handler handler,
+ AndroidFuture future,
+ long idleTimeoutMs) {
+ this.callback = callback;
+ this.handler = handler;
+ this.future = future;
+ this.idleTimeoutMs = idleTimeoutMs;
+ handler.postDelayed(this,
+ idleTimeoutMs); // init the timeout runnable in case no callback is ever invoked
+ }
+
+ @Override
+ public void onDownloadStarted(long bytesToDownload) throws RemoteException {
+ callback.onDownloadStarted(bytesToDownload);
+ handler.removeCallbacks(this);
+ handler.postDelayed(this, idleTimeoutMs);
+ }
+
+ @Override
+ public void onDownloadProgress(long bytesDownloaded) throws RemoteException {
+ callback.onDownloadProgress(bytesDownloaded);
+ handler.removeCallbacks(this); // remove previously queued timeout tasks.
+ handler.postDelayed(this, idleTimeoutMs); // queue fresh timeout task for next update.
+ }
+
+ @Override
+ public void onDownloadFailed(int failureStatus,
+ String errorMessage, PersistableBundle errorParams) throws RemoteException {
+ callback.onDownloadFailed(failureStatus, errorMessage, errorParams);
+ handler.removeCallbacks(this);
+ future.completeExceptionally(new TimeoutException());
+ }
+
+ @Override
+ public void onDownloadCompleted(
+ android.os.PersistableBundle downloadParams) throws RemoteException {
+ callback.onDownloadCompleted(downloadParams);
+ handler.removeCallbacks(this);
+ future.complete(null);
+ }
+
+ @Override
+ public void run() {
+ future.completeExceptionally(
+ new TimeoutException()); // complete the future as we haven't received updates
+ // for download progress.
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index e3e478d..3b9ad19 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -49,10 +49,13 @@
import static android.view.Display.HdrCapabilities.HDR_TYPE_INVALID;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
-import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
-import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
-import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__GESTURE_SHORTCUT_TYPE__TRIPLE_TAP;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__HARDWARE_SHORTCUT_TYPE__VOLUME_KEY;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__QS_SHORTCUT_TYPE__QUICK_SETTINGS;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_BUTTON;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_FLOATING_MENU;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_GESTURE;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__UNKNOWN_TYPE;
import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__NOT_OPPORTUNISTIC;
import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__OPPORTUNISTIC;
import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__GEO;
@@ -61,7 +64,6 @@
import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__UNKNOWN;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.stats.Flags.addMobileBytesTransferByProcStatePuller;
-import static com.android.server.stats.Flags.statsPullNetworkStatsManagerInitOrderFix;
import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
import static com.android.server.stats.pull.ProcfsMemoryUtil.getProcessCmdlines;
@@ -431,12 +433,6 @@
public static final boolean ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER =
addMobileBytesTransferByProcStatePuller();
- /**
- * Whether or not to enable the mNetworkStatsManager initialization order fix
- */
- private static final boolean ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX =
- statsPullNetworkStatsManagerInitOrderFix();
-
// Puller locks
private final Object mDataBytesTransferLock = new Object();
private final Object mBluetoothBytesTransferLock = new Object();
@@ -799,7 +795,7 @@
case FrameworkStatsLog.KEYSTORE2_CRASH_STATS:
return pullKeystoreAtoms(atomTag, data);
case FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS:
- return pullAccessibilityShortcutStatsLocked(atomTag, data);
+ return pullAccessibilityShortcutStatsLocked(data);
case FrameworkStatsLog.ACCESSIBILITY_FLOATING_MENU_STATS:
return pullAccessibilityFloatingMenuStatsLocked(atomTag, data);
case FrameworkStatsLog.MEDIA_CAPABILITIES:
@@ -840,7 +836,7 @@
registerEventListeners();
});
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
- if (ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX) {
+ if (true) {
initNetworkStatsManager();
}
BackgroundThread.getHandler().post(() -> {
@@ -863,7 +859,7 @@
mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
mStatsSubscriptionsListener = new StatsSubscriptionsListener(mSubscriptionManager);
mStorageManager = (StorageManager) mContext.getSystemService(StorageManager.class);
- if (!ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX) {
+ if (false) {
initNetworkStatsManager();
}
@@ -1047,7 +1043,7 @@
*/
@NonNull
private NetworkStatsManager getNetworkStatsManager() {
- if (ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX) {
+ if (true) {
if (mNetworkStatsManager == null) {
throw new IllegalStateException("NetworkStatsManager is not ready");
}
@@ -4774,7 +4770,10 @@
}
}
- int pullAccessibilityShortcutStatsLocked(int atomTag, List<StatsEvent> pulledData) {
+ /**
+ * Pulls ACCESSIBILITY_SHORTCUT_STATS atom
+ */
+ int pullAccessibilityShortcutStatsLocked(List<StatsEvent> pulledData) {
UserManager userManager = mContext.getSystemService(UserManager.class);
if (userManager == null) {
return StatsManager.PULL_SKIP;
@@ -4782,10 +4781,6 @@
final long token = Binder.clearCallingIdentity();
try {
final ContentResolver resolver = mContext.getContentResolver();
- final int hardware_shortcut_type =
- FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
- final int triple_tap_shortcut =
- FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP;
for (UserInfo userInfo : userManager.getUsers()) {
final int userId = userInfo.getUserHandle().getIdentifier();
@@ -4803,15 +4798,22 @@
final int hardware_shortcut_service_num = countAccessibilityServices(
hardware_shortcut_list);
+ final String qs_shortcut_list = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_QS_TARGETS, userId);
+ final boolean qs_shortcut_enabled = !TextUtils.isEmpty(qs_shortcut_list);
+
// only allow magnification to use it for now
final int triple_tap_service_num = Settings.Secure.getIntForUser(resolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userId);
-
- pulledData.add(
- FrameworkStatsLog.buildStatsEvent(atomTag,
- software_shortcut_type, software_shortcut_service_num,
- hardware_shortcut_type, hardware_shortcut_service_num,
- triple_tap_shortcut, triple_tap_service_num));
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS,
+ software_shortcut_type, software_shortcut_service_num,
+ ACCESSIBILITY_SHORTCUT_STATS__HARDWARE_SHORTCUT_TYPE__VOLUME_KEY,
+ hardware_shortcut_service_num,
+ ACCESSIBILITY_SHORTCUT_STATS__GESTURE_SHORTCUT_TYPE__TRIPLE_TAP,
+ triple_tap_service_num,
+ ACCESSIBILITY_SHORTCUT_STATS__QS_SHORTCUT_TYPE__QUICK_SETTINGS,
+ qs_shortcut_enabled));
}
}
} catch (RuntimeException e) {
@@ -5150,16 +5152,19 @@
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId);
final String hardware_shortcut_list = Settings.Secure.getStringForUser(resolver,
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userId);
+ final String qs_shortcut_list = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_QS_TARGETS, userId);
final boolean hardware_shortcut_dialog_shown = Settings.Secure.getIntForUser(resolver,
Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId) == 1;
final boolean software_shortcut_enabled = !TextUtils.isEmpty(software_shortcut_list);
final boolean hardware_shortcut_enabled =
hardware_shortcut_dialog_shown && !TextUtils.isEmpty(hardware_shortcut_list);
+ final boolean qs_shortcut_enabled = !TextUtils.isEmpty(qs_shortcut_list);
final boolean triple_tap_shortcut_enabled = Settings.Secure.getIntForUser(resolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userId) == 1;
return software_shortcut_enabled || hardware_shortcut_enabled
- || triple_tap_shortcut_enabled;
+ || triple_tap_shortcut_enabled || qs_shortcut_enabled;
}
private boolean isAccessibilityFloatingMenuUser(Context context, @UserIdInt int userId) {
@@ -5176,13 +5181,13 @@
private int convertToAccessibilityShortcutType(int shortcutType) {
switch (shortcutType) {
case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR:
- return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
+ return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_BUTTON;
case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU:
- return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
+ return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_FLOATING_MENU;
case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE:
- return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
+ return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_GESTURE;
default:
- return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
+ return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__UNKNOWN_TYPE;
}
}
diff --git a/services/core/java/com/android/server/stats/stats_flags.aconfig b/services/core/java/com/android/server/stats/stats_flags.aconfig
index c479c6d..6faa273 100644
--- a/services/core/java/com/android/server/stats/stats_flags.aconfig
+++ b/services/core/java/com/android/server/stats/stats_flags.aconfig
@@ -8,11 +8,3 @@
bug: "309512867"
is_fixed_read_only: true
}
-
-flag {
- name: "stats_pull_network_stats_manager_init_order_fix"
- namespace: "statsd"
- description: "Fix the mNetworkStatsManager initialization order"
- bug: "331989853"
- is_fixed_read_only: true
-}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index edf09f1..2b32a30 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3272,8 +3272,12 @@
mOccludesParent = occludesParent;
setMainWindowOpaque(occludesParent);
- if (changed && task != null && !occludesParent) {
- getRootTask().convertActivityToTranslucent(this);
+ if (changed && task != null) {
+ if (!occludesParent) {
+ getRootTask().convertActivityToTranslucent(this);
+ } else {
+ getRootTask().convertActivityFromTranslucent(this);
+ }
}
// Always ensure visibility if this activity doesn't occlude parent, so the
// {@link #returningOptions} of the activity under this one can be applied in
@@ -4266,6 +4270,12 @@
getTaskFragment().cleanUpActivityReferences(this);
clearLastParentBeforePip();
+ // Abort and reset state if the scence transition is playing.
+ final Task rootTask = getRootTask();
+ if (rootTask != null) {
+ rootTask.abortTranslucentActivityWaiting(this);
+ }
+
// Clean up the splash screen if it was still displayed.
cleanUpSplashScreen();
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java
index 3609837..ed07afd 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java
@@ -30,10 +30,12 @@
@Override
void putSnapshot(ActivityRecord ar, TaskSnapshot snapshot) {
final int hasCode = System.identityHashCode(ar);
+ snapshot.addReference(TaskSnapshot.REFERENCE_CACHE);
synchronized (mLock) {
final CacheEntry entry = mRunningCache.get(hasCode);
if (entry != null) {
mAppIdMap.remove(entry.topApp);
+ entry.snapshot.removeReference(TaskSnapshot.REFERENCE_CACHE);
}
mAppIdMap.put(ar, hasCode);
mRunningCache.put(hasCode, new CacheEntry(snapshot, ar));
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 3303367..08aeede 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1768,6 +1768,7 @@
if (!avoidMoveToFront() && (mService.mHomeProcess == null
|| mService.mHomeProcess.mUid != realCallingUid)
&& (prevTopTask != null && prevTopTask.isActivityTypeHomeOrRecents())
+ && !targetTask.isActivityTypeHomeOrRecents()
&& r.mTransitionController.isTransientHide(targetTask)) {
mCanMoveToFrontCode = MOVE_TO_FRONT_AVOID_LEGACY;
}
@@ -2113,7 +2114,6 @@
if (hostTask == null || targetTask != hostTask) {
return EMBEDDING_DISALLOWED_NEW_TASK;
}
-
return taskFragment.isAllowedToEmbedActivity(starting);
}
@@ -2167,7 +2167,7 @@
// We don't need to start a new activity, and the client said not to do anything
// if that is the case, so this is it! And for paranoia, make sure we have
// correctly resumed the top activity.
- if (!mMovedToFront && mDoResume) {
+ if (!mMovedToFront && mDoResume && !avoidMoveToFront()) {
ProtoLog.d(WM_DEBUG_TASKS, "Bring to front target: %s from %s", mTargetRootTask,
targetTaskTop);
mTargetRootTask.moveToFront("intentActivityFound");
@@ -2196,7 +2196,7 @@
if (mMovedToFront) {
// We moved the task to front, use starting window to hide initial drawn delay.
targetTaskTop.showStartingWindow(true /* taskSwitch */);
- } else if (mDoResume) {
+ } else if (mDoResume && !avoidMoveToFront()) {
// Make sure the root task and its belonging display are moved to topmost.
mTargetRootTask.moveToFront("intentActivityFound");
}
@@ -2961,23 +2961,9 @@
sendCanNotEmbedActivityError(mInTaskFragment, embeddingCheckResult);
}
} else {
- TaskFragment candidateTf = mAddingToTaskFragment != null ? mAddingToTaskFragment : null;
+ TaskFragment candidateTf = mAddingToTaskFragment;
if (candidateTf == null) {
- // Puts the activity on the top-most non-isolated navigation TF, unless the
- // activity is launched from the same TF.
- final TaskFragment sourceTaskFragment =
- mSourceRecord != null ? mSourceRecord.getTaskFragment() : null;
- final ActivityRecord top = task.getActivity(r -> {
- if (!r.canBeTopRunning()) {
- return false;
- }
- final TaskFragment taskFragment = r.getTaskFragment();
- return !taskFragment.isIsolatedNav() || (sourceTaskFragment != null
- && sourceTaskFragment == taskFragment);
- });
- if (top != null) {
- candidateTf = top.getTaskFragment();
- }
+ candidateTf = findCandidateTaskFragment(task);
}
if (candidateTf != null && candidateTf.isEmbedded()
&& canEmbedActivity(candidateTf, mStartActivity, task) == EMBEDDING_ALLOWED) {
@@ -2995,6 +2981,50 @@
}
/**
+ * Finds a candidate TaskFragment in {@code task} to launch activity, or returns {@code null}
+ * if there's no such a TaskFragment.
+ */
+ @Nullable
+ private TaskFragment findCandidateTaskFragment(@NonNull Task task) {
+ final TaskFragment sourceTaskFragment =
+ mSourceRecord != null ? mSourceRecord.getTaskFragment() : null;
+ for (int i = task.getChildCount() - 1; i >= 0; --i) {
+ final WindowContainer<?> wc = task.getChildAt(i);
+ final ActivityRecord activity = wc.asActivityRecord();
+ if (activity != null) {
+ if (activity.finishing) {
+ continue;
+ }
+ // Early return if the top child is an Activity.
+ return null;
+ }
+ final TaskFragment taskFragment = wc.asTaskFragment();
+ if (taskFragment == null || taskFragment.isRemovalRequested()) {
+ // Skip if the TaskFragment is going to be finished.
+ continue;
+ }
+ if (taskFragment.getActivity(ActivityRecord::canBeTopRunning) == null) {
+ // Skip if there's no activity in this TF can be top running.
+ continue;
+ }
+ if (taskFragment.isIsolatedNav()) {
+ // Stop here if we reach an isolated navigated TF.
+ return null;
+ }
+ if (sourceTaskFragment != null && sourceTaskFragment == taskFragment) {
+ // Choose the taskFragment launched from even if it's pinned.
+ return taskFragment;
+ }
+ if (taskFragment.isPinned()) {
+ // Skip the pinned TaskFragment.
+ continue;
+ }
+ return taskFragment;
+ }
+ return null;
+ }
+
+ /**
* Notifies the client side that {@link #mStartActivity} cannot be embedded to
* {@code taskFragment}.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 237003a..3aa63af 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -7418,7 +7418,8 @@
FEATURE_LEANBACK);
final boolean isArc = arcFeature != null && arcFeature.version >= 0;
final boolean isTv = tvFeature != null && tvFeature.version >= 0;
- sIsPip2ExperimentEnabled = SystemProperties.getBoolean("wm_shell.pip2", false)
+ sIsPip2ExperimentEnabled = SystemProperties.getBoolean(
+ "persist.wm_shell.pip2", false)
|| (Flags.enablePip2Implementation() && !isArc && !isTv);
}
return sIsPip2ExperimentEnabled;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a5853c0..5079ec1 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5009,7 +5009,7 @@
// This should be called after the insets have been dispatched to clients and we have
// committed finish drawing windows.
- mInsetsStateController.getImeSourceProvider().checkShowImePostLayout();
+ mInsetsStateController.getImeSourceProvider().checkAndStartShowImePostLayout();
mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
if (!inTransition() && !mDisplayRotation.isRotatingSeamlessly()) {
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 092ff3d..e03ff688 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -24,7 +24,6 @@
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.ImeInsetsSourceProviderProto.IME_TARGET_FROM_IME;
import static com.android.server.wm.ImeInsetsSourceProviderProto.INSETS_SOURCE_PROVIDER;
-import static com.android.server.wm.ImeInsetsSourceProviderProto.IS_IME_LAYOUT_DRAWN;
import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS;
import android.annotation.NonNull;
@@ -52,19 +51,26 @@
private static final String TAG = ImeInsetsSourceProvider.class.getSimpleName();
- /** The token tracking the current IME request or {@code null} otherwise. */
+ /** The token tracking the show IME request, non-null only while a show request is pending. */
@Nullable
- private ImeTracker.Token mImeRequesterStatsToken;
+ private ImeTracker.Token mStatsToken;
+ /** The target that requested to show the IME, non-null only while a show request is pending. */
+ @Nullable
private InsetsControlTarget mImeRequester;
- private Runnable mShowImeRunner;
- private boolean mIsImeLayoutDrawn;
+ /** @see #isImeShowing() */
private boolean mImeShowing;
+ /** The latest received insets source. */
private final InsetsSource mLastSource = new InsetsSource(ID_IME, WindowInsets.Type.ime());
/** @see #setFrozen(boolean) */
private boolean mFrozen;
- /** @see #setServerVisible(boolean) */
+ /**
+ * The server visibility of the source provider's window container. This is out of sync with
+ * {@link InsetsSourceProvider#mServerVisible} while {@link #mFrozen} is {@code true}.
+ *
+ * @see #setServerVisible
+ */
private boolean mServerVisible;
ImeInsetsSourceProvider(@NonNull InsetsSource source,
@@ -73,6 +79,7 @@
super(source, stateController, displayContent);
}
+ @Nullable
@Override
InsetsSourceControl getControl(InsetsControlTarget target) {
final InsetsSourceControl control = super.getControl(target);
@@ -124,9 +131,9 @@
/**
* Freeze IME insets source state when required.
*
- * When setting {@param frozen} as {@code true}, the IME insets provider will freeze the
+ * <p>When setting {@param frozen} as {@code true}, the IME insets provider will freeze the
* current IME insets state and pending the IME insets state update until setting
- * {@param frozen} as {@code false}.
+ * {@param frozen} as {@code false}.</p>
*/
void setFrozen(boolean frozen) {
if (mFrozen == frozen) {
@@ -223,27 +230,29 @@
/**
* Called from {@link WindowManagerInternal#showImePostLayout}
* when {@link android.inputmethodservice.InputMethodService} requests to show IME
- * on {@param imeTarget}.
+ * on the given control target.
*
- * @param imeTarget imeTarget on which IME request is coming from.
+ * @param imeTarget the control target on which the IME request is coming from.
* @param statsToken the token tracking the current IME request.
*/
- void scheduleShowImePostLayout(InsetsControlTarget imeTarget,
+ void scheduleShowImePostLayout(@NonNull InsetsControlTarget imeTarget,
@NonNull ImeTracker.Token statsToken) {
- if (mImeRequesterStatsToken != null) {
- // Cancel the pre-existing stats token, if any.
- // Log state on pre-existing request cancel.
- logShowImePostLayoutState(false /* aborted */);
- ImeTracker.forLogging().onCancelled(
- mImeRequesterStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
+ if (mImeRequester == null) {
+ // Start tracing only on initial scheduled show IME request, to record end-to-end time.
+ Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
+ } else {
+ // We already have a scheduled show IME request, cancel the previous statsToken and
+ // continue with the new one.
+ logIsScheduledAndReadyToShowIme(false /* aborted */);
+ ImeTracker.forLogging().onCancelled(mStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
}
- mImeRequesterStatsToken = statsToken;
- boolean targetChanged = isTargetChangedWithinActivity(imeTarget);
+ final boolean targetChanged = isTargetChangedWithinActivity(imeTarget);
mImeRequester = imeTarget;
+ mStatsToken = statsToken;
if (targetChanged) {
// target changed, check if new target can show IME.
ProtoLog.d(WM_DEBUG_IME, "IME target changed within ActivityRecord");
- checkShowImePostLayout();
+ checkAndStartShowImePostLayout();
// if IME cannot be shown at this time, it is scheduled to be shown.
// once window that called IMM.showSoftInput() and DisplayContent's ImeTarget match,
// it will be shown.
@@ -252,79 +261,58 @@
ProtoLog.d(WM_DEBUG_IME, "Schedule IME show for %s", mImeRequester.getWindow() == null
? mImeRequester : mImeRequester.getWindow().getName());
- mShowImeRunner = () -> {
- ImeTracker.forLogging().onProgress(mImeRequesterStatsToken,
- ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
- ProtoLog.d(WM_DEBUG_IME, "Run showImeRunner");
- // Target should still be the same.
- if (isReadyToShowIme()) {
- ImeTracker.forLogging().onProgress(mImeRequesterStatsToken,
- ImeTracker.PHASE_WM_SHOW_IME_READY);
- final InsetsControlTarget target = mDisplayContent.getImeTarget(IME_TARGET_CONTROL);
-
- ProtoLog.i(WM_DEBUG_IME, "call showInsets(ime) on %s",
- target.getWindow() != null ? target.getWindow().getName() : "");
- setImeShowing(true);
- target.showInsets(WindowInsets.Type.ime(), true /* fromIme */,
- mImeRequesterStatsToken);
- Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
- if (target != mImeRequester && mImeRequester != null) {
- ProtoLog.w(WM_DEBUG_IME,
- "showInsets(ime) was requested by different window: %s ",
- (mImeRequester.getWindow() != null
- ? mImeRequester.getWindow().getName() : ""));
- }
- } else {
- ImeTracker.forLogging().onFailed(mImeRequesterStatsToken,
- ImeTracker.PHASE_WM_SHOW_IME_READY);
- }
- // Clear token here so we don't report an error in abortShowImePostLayout().
- mImeRequesterStatsToken = null;
- abortShowImePostLayout();
- };
mDisplayContent.mWmService.requestTraversal();
}
- void checkShowImePostLayout() {
- if (mWindowContainer == null) {
+ /**
+ * Checks whether there is a previously scheduled show IME request and we are ready to show,
+ * in which case also start handling the request.
+ */
+ void checkAndStartShowImePostLayout() {
+ if (!isScheduledAndReadyToShowIme()) {
+ // This can later become ready, so we don't want to cancel the pending request here.
return;
}
- WindowState windowState = mWindowContainer.asWindowState();
- if (windowState == null) {
- throw new IllegalArgumentException("IME insets must be provided by a window.");
+
+ ImeTracker.forLogging().onProgress(mStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
+ ProtoLog.d(WM_DEBUG_IME, "Run showImeRunner");
+
+ final InsetsControlTarget target = getControlTarget();
+
+ ProtoLog.i(WM_DEBUG_IME, "call showInsets(ime) on %s",
+ target.getWindow() != null ? target.getWindow().getName() : "");
+ setImeShowing(true);
+ target.showInsets(WindowInsets.Type.ime(), true /* fromIme */, mStatsToken);
+ Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
+ if (target != mImeRequester) {
+ ProtoLog.w(WM_DEBUG_IME, "showInsets(ime) was requested by different window: %s ",
+ (mImeRequester.getWindow() != null ? mImeRequester.getWindow().getName() : ""));
}
- // check if IME is drawn
- if (mIsImeLayoutDrawn
- || (isReadyToShowIme()
- && windowState.isDrawn()
- && !windowState.mGivenInsetsPending)) {
- mIsImeLayoutDrawn = true;
- // show IME if InputMethodService requested it to be shown.
- if (mShowImeRunner != null) {
- mShowImeRunner.run();
- }
- }
+ resetShowImePostLayout();
}
- /**
- * Abort any pending request to show IME post layout.
- */
+ /** Aborts the previously scheduled show IME request. */
void abortShowImePostLayout() {
- ProtoLog.d(WM_DEBUG_IME, "abortShowImePostLayout");
- if (mImeRequesterStatsToken != null) {
- // Log state on abort.
- logShowImePostLayoutState(true /* aborted */);
- ImeTracker.forLogging().onFailed(
- mImeRequesterStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT);
- mImeRequesterStatsToken = null;
+ if (mImeRequester == null) {
+ return;
}
- mImeRequester = null;
- mIsImeLayoutDrawn = false;
- mShowImeRunner = null;
+ ProtoLog.d(WM_DEBUG_IME, "abortShowImePostLayout");
+ Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
+ logIsScheduledAndReadyToShowIme(true /* aborted */);
+ ImeTracker.forLogging().onFailed(
+ mStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT);
+ resetShowImePostLayout();
}
+ /** Resets the state of the previously scheduled show IME request. */
+ private void resetShowImePostLayout() {
+ mImeRequester = null;
+ mStatsToken = null;
+ }
+
+ /** Checks whether there is a previously scheduled show IME request and we are ready to show. */
@VisibleForTesting
- boolean isReadyToShowIme() {
+ boolean isScheduledAndReadyToShowIme() {
// IMMS#mLastImeTargetWindow always considers focused window as
// IME target, however DisplayContent#computeImeTarget() can compute
// a different IME target.
@@ -334,32 +322,47 @@
// Also, if imeTarget is closing, it would be considered as outdated target.
// TODO(b/139861270): Remove the child & sublayer check once IMMS is aware of
// actual IME target.
+ if (mImeRequester == null) {
+ // No show IME request previously scheduled.
+ return false;
+ }
+ if (!mServerVisible || mFrozen) {
+ // The window container is not available and considered visible.
+ // If frozen, the server visibility is not set until unfrozen.
+ return false;
+ }
+ if (mWindowContainer == null) {
+ // No window container set.
+ return false;
+ }
+ final WindowState windowState = mWindowContainer.asWindowState();
+ if (windowState == null) {
+ throw new IllegalArgumentException("IME insets must be provided by a window.");
+ }
+ if (!windowState.isDrawn() || windowState.mGivenInsetsPending) {
+ // The window is not drawn, or it has pending insets.
+ return false;
+ }
final InsetsControlTarget dcTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
- if (dcTarget == null || mImeRequester == null) {
- // Not ready to show if there is no IME layering target, or no IME requester.
+ if (dcTarget == null) {
+ // No IME layering target.
return false;
}
final InsetsControlTarget controlTarget = getControlTarget();
if (controlTarget == null) {
- // Not ready to show if there is no IME control target.
+ // No IME control target.
return false;
}
if (controlTarget != mDisplayContent.getImeTarget(IME_TARGET_CONTROL)) {
- // Not ready to show if control target does not match the one in DisplayContent.
- return false;
- }
- if (!mServerVisible || mFrozen) {
- // Not ready to show if the window container is not available and considered visible.
- // If frozen, the server visibility is not set until unfrozen.
+ // The control target does not match the one in DisplayContent.
return false;
}
if (mStateController.hasPendingControls(controlTarget)) {
- // Not ready to show if control target has pending controls.
+ // The control target has pending controls.
return false;
}
if (getLeash(controlTarget) == null) {
- // Not ready to show if control target has no source control leash (or leash is not
- // ready for dispatching).
+ // The control target has no source control leash (or it is not ready for dispatching).
return false;
}
@@ -371,51 +374,44 @@
|| isAboveImeLayeringTarget(mImeRequester, dcTarget)
|| isImeFallbackTarget(mImeRequester)
|| isImeInputTarget(mImeRequester)
- || sameAsImeControlTarget();
+ || sameAsImeControlTarget(mImeRequester);
}
/**
- * Logs the current state required for showImePostLayout to be triggered.
+ * Logs the current state that can be checked by {@link #isScheduledAndReadyToShowIme}.
*
- * @param aborted whether the showImePostLayout was aborted or cancelled.
+ * @param aborted whether the scheduled show IME request was aborted or cancelled.
*/
- private void logShowImePostLayoutState(boolean aborted) {
+ private void logIsScheduledAndReadyToShowIme(boolean aborted) {
final var windowState = mWindowContainer != null ? mWindowContainer.asWindowState() : null;
final var dcTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
final var controlTarget = getControlTarget();
final var sb = new StringBuilder();
sb.append("showImePostLayout ").append(aborted ? "aborted" : "cancelled");
- sb.append(", mWindowContainer is: ");
- sb.append(mWindowContainer != null ? "non-null" : "null");
+ sb.append(", isScheduledAndReadyToShowIme: ").append(isScheduledAndReadyToShowIme());
+ sb.append(", mImeRequester: ").append(mImeRequester);
+ sb.append(", serverVisible: ").append(mServerVisible);
+ sb.append(", frozen: ").append(mFrozen);
+ sb.append(", mWindowContainer is: ").append(mWindowContainer != null ? "non-null" : "null");
sb.append(", windowState: ").append(windowState);
if (windowState != null) {
- sb.append(", windowState.isDrawn(): ");
- sb.append(windowState.isDrawn());
- sb.append(", windowState.mGivenInsetsPending: ");
- sb.append(windowState.mGivenInsetsPending);
+ sb.append(", isDrawn: ").append(windowState.isDrawn());
+ sb.append(", mGivenInsetsPending: ").append(windowState.mGivenInsetsPending);
}
- sb.append(", mIsImeLayoutDrawn: ").append(mIsImeLayoutDrawn);
- sb.append(", mShowImeRunner: ").append(mShowImeRunner);
- sb.append(", mImeRequester: ").append(mImeRequester);
sb.append(", dcTarget: ").append(dcTarget);
sb.append(", controlTarget: ").append(controlTarget);
- sb.append("\n");
- sb.append("isReadyToShowIme(): ").append(isReadyToShowIme());
if (mImeRequester != null && dcTarget != null && controlTarget != null) {
- sb.append(", controlTarget == DisplayContent.controlTarget: ");
+ sb.append("\n");
+ sb.append("controlTarget == DisplayContent.controlTarget: ");
sb.append(controlTarget == mDisplayContent.getImeTarget(IME_TARGET_CONTROL));
sb.append(", hasPendingControls: ");
sb.append(mStateController.hasPendingControls(controlTarget));
- sb.append(", serverVisible: ");
- sb.append(mServerVisible);
- sb.append(", frozen: ");
- sb.append(mFrozen);
- sb.append(", leash is: ");
- sb.append(getLeash(controlTarget) != null ? "non-null" : "null");
- sb.append(", control is: ");
- sb.append(mControl != null ? "non-null" : "null");
- sb.append(", mIsLeashReadyForDispatching: ");
- sb.append(mIsLeashReadyForDispatching);
+ final boolean hasLeash = getLeash(controlTarget) != null;
+ sb.append(", leash is: ").append(hasLeash ? "non-null" : "null");
+ if (!hasLeash) {
+ sb.append(", control is: ").append(mControl != null ? "non-null" : "null");
+ sb.append(", mIsLeashReadyForDispatching: ").append(mIsLeashReadyForDispatching);
+ }
sb.append(", isImeLayeringTarget: ");
sb.append(isImeLayeringTarget(mImeRequester, dcTarget));
sb.append(", isAboveImeLayeringTarget: ");
@@ -425,7 +421,7 @@
sb.append(", isImeInputTarget: ");
sb.append(isImeInputTarget(mImeRequester));
sb.append(", sameAsImeControlTarget: ");
- sb.append(sameAsImeControlTarget());
+ sb.append(sameAsImeControlTarget(mImeRequester));
}
Slog.d(TAG, sb.toString());
}
@@ -445,19 +441,18 @@
&& dcTarget.getWindow().mSubLayer > target.getWindow().mSubLayer;
}
- private boolean isImeFallbackTarget(InsetsControlTarget target) {
+ private boolean isImeFallbackTarget(@NonNull InsetsControlTarget target) {
return target == mDisplayContent.getImeFallback();
}
- private boolean isImeInputTarget(InsetsControlTarget target) {
+ private boolean isImeInputTarget(@NonNull InsetsControlTarget target) {
return target == mDisplayContent.getImeInputTarget();
}
- private boolean sameAsImeControlTarget() {
- final InsetsControlTarget target = mDisplayContent.getImeTarget(IME_TARGET_CONTROL);
- return target == mImeRequester
- && (mImeRequester.getWindow() == null
- || !isImeTargetWindowClosing(mImeRequester.getWindow()));
+ private boolean sameAsImeControlTarget(@NonNull InsetsControlTarget target) {
+ final InsetsControlTarget controlTarget = getControlTarget();
+ return controlTarget == target
+ && (target.getWindow() == null || !isImeTargetWindowClosing(target.getWindow()));
}
private static boolean isImeTargetWindowClosing(@NonNull WindowState win) {
@@ -467,16 +462,15 @@
|| win.mActivityRecord.willCloseOrEnterPip());
}
- private boolean isTargetChangedWithinActivity(InsetsControlTarget target) {
+ private boolean isTargetChangedWithinActivity(@NonNull InsetsControlTarget target) {
// We don't consider the target out of the activity.
- if (target == null || target.getWindow() == null) {
+ if (target.getWindow() == null) {
return false;
}
return mImeRequester != target
- && mImeRequester != null && mShowImeRunner != null
+ && mImeRequester != null
&& mImeRequester.getWindow() != null
- && mImeRequester.getWindow().mActivityRecord
- == target.getWindow().mActivityRecord;
+ && mImeRequester.getWindow().mActivityRecord == target.getWindow().mActivityRecord;
}
// ---------------------------------------------------------------------------------------
@@ -509,7 +503,6 @@
if (imeRequesterWindow != null) {
imeRequesterWindow.dumpDebug(proto, IME_TARGET_FROM_IME, logLevel);
}
- proto.write(IS_IME_LAYOUT_DRAWN, mIsImeLayoutDrawn);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index dfee164..7a1f57b 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -389,7 +389,7 @@
newControlTargets.clear();
// Check for and try to run the scheduled show IME request (if it exists), as we
// now applied the surface transaction and notified the target of the new control.
- getImeSourceProvider().checkShowImePostLayout();
+ getImeSourceProvider().checkAndStartShowImePostLayout();
});
}
diff --git a/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java b/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java
index 498182d..3606a34 100644
--- a/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java
+++ b/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java
@@ -41,8 +41,12 @@
PerfettoTransitionTracer() {
Producer.init(InitArguments.DEFAULTS);
- mDataSource.register(
- new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT));
+ DataSourceParams params =
+ new DataSourceParams.Builder()
+ .setBufferExhaustedPolicy(
+ PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+ .build();
+ mDataSource.register(params);
}
/**
diff --git a/services/core/java/com/android/server/wm/SnapshotCache.java b/services/core/java/com/android/server/wm/SnapshotCache.java
index 8680436..1e6ee7d 100644
--- a/services/core/java/com/android/server/wm/SnapshotCache.java
+++ b/services/core/java/com/android/server/wm/SnapshotCache.java
@@ -92,6 +92,7 @@
if (entry != null) {
mAppIdMap.remove(entry.topApp);
mRunningCache.remove(id);
+ entry.snapshot.removeReference(TaskSnapshot.REFERENCE_CACHE);
}
}
}
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index 3578971..42ca7b4 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -253,6 +253,7 @@
PersistInfoProvider provider) {
super(provider, userId);
mId = id;
+ snapshot.addReference(TaskSnapshot.REFERENCE_PERSIST);
mSnapshot = snapshot;
}
@@ -289,6 +290,7 @@
if (failed) {
deleteSnapshot(mId, mUserId, mPersistInfoProvider);
}
+ mSnapshot.removeReference(TaskSnapshot.REFERENCE_PERSIST);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8defec3..a555388 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -297,6 +297,10 @@
ActivityRecord mTranslucentActivityWaiting = null;
ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent = new ArrayList<>();
+ // The topmost Activity that was converted to translucent for scene transition, which should
+ // be converted from translucent once the transition is completed, or the app died.
+ private ActivityRecord mPendingConvertFromTranslucentActivity = null;
+
/**
* Set when we know we are going to be calling updateConfiguration()
* soon, so want to skip intermediate config checks.
@@ -4988,6 +4992,27 @@
}
}
+ void abortTranslucentActivityWaiting(@NonNull ActivityRecord r) {
+ if (r != mTranslucentActivityWaiting && r != mPendingConvertFromTranslucentActivity) {
+ return;
+ }
+
+ if (mTranslucentActivityWaiting != null) {
+ if (!mTranslucentActivityWaiting.finishing) {
+ mTranslucentActivityWaiting.setOccludesParent(true);
+ }
+ mTranslucentActivityWaiting = null;
+ }
+ if (mPendingConvertFromTranslucentActivity != null) {
+ if (!mPendingConvertFromTranslucentActivity.finishing) {
+ mPendingConvertFromTranslucentActivity.setOccludesParent(true);
+ }
+ mPendingConvertFromTranslucentActivity = null;
+ }
+ mUndrawnActivitiesBelowTopTranslucent.clear();
+ mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
+ }
+
void checkTranslucentActivityWaiting(ActivityRecord top) {
if (mTranslucentActivityWaiting != top) {
mUndrawnActivitiesBelowTopTranslucent.clear();
@@ -5002,10 +5027,19 @@
void convertActivityToTranslucent(ActivityRecord r) {
mTranslucentActivityWaiting = r;
+ mPendingConvertFromTranslucentActivity = r;
mUndrawnActivitiesBelowTopTranslucent.clear();
mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
}
+ void convertActivityFromTranslucent(ActivityRecord r) {
+ if (r != mPendingConvertFromTranslucentActivity) {
+ Slog.e(TAG, "convertFromTranslucent expects " + mPendingConvertFromTranslucentActivity
+ + " but is " + r);
+ }
+ mPendingConvertFromTranslucentActivity = null;
+ }
+
/**
* Called as activities below the top translucent activity are redrawn. When the last one is
* redrawn notify the top activity by calling
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 21e7a8d..586f3c3 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -247,6 +247,7 @@
break;
case NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG:
forAllRemoteListeners(mNotifyTaskSnapshotChanged, msg);
+ ((TaskSnapshot) msg.obj).removeReference(TaskSnapshot.REFERENCE_BROADCAST);
break;
case NOTIFY_BACK_PRESSED_ON_TASK_ROOT:
forAllRemoteListeners(mNotifyBackPressedOnTaskRoot, msg);
@@ -485,6 +486,7 @@
* Notify listeners that the snapshot of a task has changed.
*/
void notifyTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
+ snapshot.addReference(TaskSnapshot.REFERENCE_BROADCAST);
final Message msg = mHandler.obtainMessage(NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG,
taskId, 0, snapshot);
forAllLocalListeners(mNotifyTaskSnapshotChanged, msg);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 6a7f60b..a444c96 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -354,14 +354,21 @@
/**
* Whether the activity navigation should be isolated. That is, Activities cannot be launched
- * on an isolated TaskFragment, unless the activity is launched from an Activity in the same
- * isolated TaskFragment, or explicitly requested to be launched to.
- * <p>
- * Note that only an embedded TaskFragment can be isolated.
+ * on an isolated TaskFragment unless explicitly requested to be launched to.
*/
private boolean mIsolatedNav;
/**
+ * Whether the TaskFragment to be pinned.
+ * <p>
+ * If a TaskFragment is pinned, the TaskFragment should be the top-most TaskFragment among other
+ * sibling TaskFragments. Any newly launched and embeddable activity should not be placed in the
+ * pinned TaskFragment, unless the activity is launched from the pinned TaskFragment or
+ * explicitly requested to. Non-embeddable activities are not restricted to.
+ */
+ private boolean mPinned;
+
+ /**
* Whether the TaskFragment should move to bottom of task when any activity below it is
* launched in clear top mode.
*/
@@ -515,6 +522,18 @@
}
/**
+ * Sets whether this TaskFragment {@link #isPinned()}.
+ * <p>
+ * Note that this is no-op if the TaskFragment is not {@link #isEmbedded() embedded}.
+ */
+ void setPinned(boolean pinned) {
+ if (!isEmbedded()) {
+ return;
+ }
+ mPinned = pinned;
+ }
+
+ /**
* Sets whether transitions are allowed when the TaskFragment is empty. If {@code true},
* transitions are allowed when the TaskFragment is empty. If {@code false}, transitions
* will wait until the TaskFragment becomes non-empty or other conditions are met. Default
@@ -532,6 +551,15 @@
return isEmbedded() && mIsolatedNav;
}
+ /**
+ * Indicates whether this TaskFragment is pinned.
+ *
+ * @see android.window.TaskFragmentOperation#OP_TYPE_SET_PINNED
+ */
+ boolean isPinned() {
+ return isEmbedded() && mPinned;
+ }
+
TaskFragment getAdjacentTaskFragment() {
return mAdjacentTaskFragment;
}
@@ -564,7 +592,6 @@
}
void setResumedActivity(ActivityRecord r, String reason) {
- warnForNonLeafTaskFragment("setResumedActivity");
if (mResumedActivity == r) {
return;
}
@@ -850,15 +877,6 @@
return parentTaskFragment != null ? parentTaskFragment.getOrganizedTaskFragment() : null;
}
- /**
- * Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}.
- */
- private void warnForNonLeafTaskFragment(String func) {
- if (!isLeafTaskFragment()) {
- Slog.w(TAG, func + " on non-leaf task fragment " + this);
- }
- }
-
boolean hasDirectChildActivities() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
if (mChildren.get(i).asActivityRecord() != null) {
@@ -935,7 +953,6 @@
*/
void onActivityStateChanged(ActivityRecord record, ActivityRecord.State state,
String reason) {
- warnForNonLeafTaskFragment("onActivityStateChanged");
if (record == mResumedActivity && state != RESUMED) {
setResumedActivity(null, reason + " - onActivityStateChanged");
}
@@ -965,7 +982,6 @@
* @return {@code true} if the process of the pausing activity is died.
*/
boolean handleAppDied(WindowProcessController app) {
- warnForNonLeafTaskFragment("handleAppDied");
boolean isPausingDied = false;
if (mPausingActivity != null && mPausingActivity.app == app) {
ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s",
@@ -2895,6 +2911,13 @@
return !mCreatedByOrganizer || mIsRemovalRequested;
}
+ /**
+ * Returns whether this TaskFragment is going to be removed.
+ */
+ boolean isRemovalRequested() {
+ return mIsRemovalRequested;
+ }
+
@Override
void removeChild(WindowContainer child) {
removeChild(child, true /* removeSelfIfPossible */);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
index b69ac1b..64b9df5 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
@@ -35,9 +35,11 @@
void putSnapshot(Task task, TaskSnapshot snapshot) {
synchronized (mLock) {
+ snapshot.addReference(TaskSnapshot.REFERENCE_CACHE);
final CacheEntry entry = mRunningCache.get(task.mTaskId);
if (entry != null) {
mAppIdMap.remove(entry.topApp);
+ entry.snapshot.removeReference(TaskSnapshot.REFERENCE_CACHE);
}
final ActivityRecord top = task.getTopMostActivity();
mAppIdMap.put(top, task.mTaskId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e02e5be..b603551 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8302,7 +8302,6 @@
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET);
- Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
final InsetsControlTarget controlTarget = imeTarget.getImeControlTarget();
imeTarget = controlTarget.getWindow();
// If InsetsControlTarget doesn't have a window, it's using remoteControlTarget
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 90e7bd7..99c4736 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -39,6 +39,7 @@
import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_PINNED;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_RELATIVE_BOUNDS;
import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_UNKNOWN;
@@ -1627,6 +1628,11 @@
}
break;
}
+ case OP_TYPE_SET_PINNED: {
+ final boolean pinned = operation.getBooleanValue();
+ taskFragment.setPinned(pinned);
+ break;
+ }
}
return effects;
}
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 6143f1d..610b502 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -746,6 +746,20 @@
minOccurs="0" maxOccurs="1">
<xs:annotation name="final"/>
</xs:element>
+ <!-- list of supported modes for low power. Each point corresponds to one mode.
+ Mode format is : first = refreshRate, second = vsyncRate. E.g. :
+ <lowPowerSupportedModes>
+ <point>
+ <first>60</first> // refreshRate
+ <second>60</second> //vsyncRate
+ </point>
+ ....
+ </lowPowerSupportedModes>
+ -->
+ <xs:element type="nonNegativeFloatToFloatMap" name="lowPowerSupportedModes" minOccurs="0">
+ <xs:annotation name="nullable"/>
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:complexType>
<xs:complexType name="refreshRateZoneProfiles">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 45ec8f2..203a6d9 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -360,6 +360,7 @@
method public final java.math.BigInteger getDefaultRefreshRateInHbmHdr();
method public final java.math.BigInteger getDefaultRefreshRateInHbmSunlight();
method public final com.android.server.display.config.BlockingZoneConfig getHigherBlockingZoneConfigs();
+ method @Nullable public final com.android.server.display.config.NonNegativeFloatToFloatMap getLowPowerSupportedModes();
method public final com.android.server.display.config.BlockingZoneConfig getLowerBlockingZoneConfigs();
method public final com.android.server.display.config.RefreshRateZoneProfiles getRefreshRateZoneProfiles();
method public final void setDefaultPeakRefreshRate(java.math.BigInteger);
@@ -367,6 +368,7 @@
method public final void setDefaultRefreshRateInHbmHdr(java.math.BigInteger);
method public final void setDefaultRefreshRateInHbmSunlight(java.math.BigInteger);
method public final void setHigherBlockingZoneConfigs(com.android.server.display.config.BlockingZoneConfig);
+ method public final void setLowPowerSupportedModes(@Nullable com.android.server.display.config.NonNegativeFloatToFloatMap);
method public final void setLowerBlockingZoneConfigs(com.android.server.display.config.BlockingZoneConfig);
method public final void setRefreshRateZoneProfiles(com.android.server.display.config.RefreshRateZoneProfiles);
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 1d225ba..221a991 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -36,9 +36,10 @@
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static java.util.Objects.requireNonNull;
+
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -72,7 +73,10 @@
super.setUp();
mVisibilityApplier =
(DefaultImeVisibilityApplier) mInputMethodManagerService.getVisibilityApplier();
- mInputMethodManagerService.setAttachedClientForTesting(mock(ClientState.class));
+ synchronized (ImfLock.class) {
+ mInputMethodManagerService.setAttachedClientForTesting(requireNonNull(
+ mInputMethodManagerService.getClientStateLocked(mMockInputMethodClient)));
+ }
}
@Test
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index cff2265..28a99f2f 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -29,6 +29,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -45,6 +46,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.view.InputChannel;
import android.view.inputmethod.EditorInfo;
import android.window.ImeOnBackInvokedDispatcher;
@@ -53,6 +55,7 @@
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.inputmethod.IInputMethod;
import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IInputMethodSession;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteInputConnection;
import com.android.internal.inputmethod.InputBindResult;
@@ -104,6 +107,7 @@
@Mock protected UserManagerInternal mMockUserManagerInternal;
@Mock protected InputMethodBindingController mMockInputMethodBindingController;
@Mock protected IInputMethodClient mMockInputMethodClient;
+ @Mock protected IInputMethodSession mMockInputMethodSession;
@Mock protected IBinder mWindowToken;
@Mock protected IRemoteInputConnection mMockRemoteInputConnection;
@Mock protected IRemoteAccessibilityInputConnection mMockRemoteAccessibilityInputConnection;
@@ -246,6 +250,7 @@
// Call InputMethodManagerService#addClient() as a preparation to start interacting with it.
mInputMethodManagerService.addClient(mMockInputMethodClient, mMockRemoteInputConnection, 0);
+ createSessionForClient(mMockInputMethodClient);
}
@After
@@ -295,4 +300,13 @@
.hideSoftInput(any() /* hideInputToken */, notNull() /* statsToken */,
anyInt() /* flags */, any() /* resultReceiver */);
}
+
+ protected void createSessionForClient(IInputMethodClient client) {
+ synchronized (ImfLock.class) {
+ ClientState cs = mInputMethodManagerService.getClientStateLocked(client);
+ cs.mCurSession = new InputMethodManagerService.SessionState(cs,
+ mMockInputMethodInvoker, mMockInputMethodSession, mock(
+ InputChannel.class));
+ }
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index a0a611f..46d08b0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -21,7 +21,6 @@
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
import static com.android.server.display.config.SensorData.TEMPERATURE_TYPE_SKIN;
-import static com.android.server.display.config.SensorData.SupportedMode;
import static com.android.server.display.utils.DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat;
import static com.android.server.display.utils.DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat;
@@ -58,6 +57,7 @@
import com.android.server.display.config.HysteresisLevels;
import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint;
import com.android.server.display.config.RefreshRateData;
+import com.android.server.display.config.SupportedModeData;
import com.android.server.display.config.ThermalStatus;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.feature.flags.Flags;
@@ -613,7 +613,7 @@
assertEquals(mDisplayDeviceConfig.getProximitySensor().minRefreshRate, 60, SMALL_DELTA);
assertEquals(mDisplayDeviceConfig.getProximitySensor().maxRefreshRate, 90, SMALL_DELTA);
assertThat(mDisplayDeviceConfig.getProximitySensor().supportedModes).hasSize(2);
- SupportedMode mode = mDisplayDeviceConfig.getProximitySensor().supportedModes.get(0);
+ SupportedModeData mode = mDisplayDeviceConfig.getProximitySensor().supportedModes.get(0);
assertEquals(mode.refreshRate, 60, SMALL_DELTA);
assertEquals(mode.vsyncRate, 65, SMALL_DELTA);
mode = mDisplayDeviceConfig.getProximitySensor().supportedModes.get(1);
@@ -933,6 +933,21 @@
assertEquals(0.2f, mDisplayDeviceConfig.getNitsFromBacklight(0.0f), ZERO_DELTA);
}
+ @Test
+ public void testLowPowerSupportedModesFromConfigFile() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile();
+
+ RefreshRateData refreshRateData = mDisplayDeviceConfig.getRefreshRateData();
+ assertNotNull(refreshRateData);
+ assertThat(refreshRateData.lowPowerSupportedModes).hasSize(2);
+ SupportedModeData supportedModeData = refreshRateData.lowPowerSupportedModes.get(0);
+ assertThat(supportedModeData.refreshRate).isEqualTo(60);
+ assertThat(supportedModeData.vsyncRate).isEqualTo(60);
+ supportedModeData = refreshRateData.lowPowerSupportedModes.get(1);
+ assertThat(supportedModeData.refreshRate).isEqualTo(60);
+ assertThat(supportedModeData.vsyncRate).isEqualTo(120);
+ }
+
private String getValidLuxThrottling() {
return "<luxThrottling>\n"
+ " <brightnessLimitMap>\n"
@@ -1089,6 +1104,19 @@
+ "</proxSensor>\n";
}
+ private String getLowPowerConfig() {
+ return "<lowPowerSupportedModes>\n"
+ + " <point>\n"
+ + " <first>60</first>\n"
+ + " <second>60</second>\n"
+ + " </point>\n"
+ + " <point>\n"
+ + " <first>60</first>\n"
+ + " <second>120</second>\n"
+ + " </point>\n"
+ + "</lowPowerSupportedModes>\n";
+ }
+
private String getHdrBrightnessConfig() {
return "<hdrBrightnessConfig>\n"
+ " <brightnessMap>\n"
@@ -1620,6 +1648,7 @@
+ "</displayBrightnessPoint>\n"
+ "</blockingZoneThreshold>\n"
+ "</higherBlockingZoneConfigs>\n"
+ + getLowPowerConfig()
+ "</refreshRate>\n"
+ "<screenOffBrightnessSensorValueToLux>\n"
+ "<item>-1</item>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index cd1e9e8..714b423 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -131,7 +131,8 @@
/* defaultRefreshRate= */ 0,
/* defaultPeakRefreshRate= */ 0,
/* defaultRefreshRateInHbmHdr= */ 0,
- /* defaultRefreshRateInHbmSunlight= */ 0);
+ /* defaultRefreshRateInHbmSunlight= */ 0,
+ /* lowPowerSupportedModes =*/ List.of());
public static Collection<Object[]> getAppRequestedSizeTestCases() {
var appRequestedSizeTestCases = Arrays.asList(new Object[][] {
@@ -157,7 +158,7 @@
APP_MODE_HIGH_90.getPhysicalHeight()),
Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()),
- Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(),
LIMIT_MODE_70.getPhysicalHeight()))},
{/*expectedBaseModeId*/ LIMIT_MODE_70.getModeId(),
@@ -169,7 +170,7 @@
APP_MODE_65.getPhysicalHeight()),
Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
- Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(),
LIMIT_MODE_70.getPhysicalHeight()))},
{/*expectedBaseModeId*/ LIMIT_MODE_70.getModeId(),
@@ -181,7 +182,7 @@
APP_MODE_65.getPhysicalHeight()),
Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
- Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
Vote.forSizeAndPhysicalRefreshRatesRange(
0, 0,
LIMIT_MODE_70.getPhysicalWidth(),
@@ -197,7 +198,7 @@
APP_MODE_65.getPhysicalHeight()),
Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
- Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
Vote.forSizeAndPhysicalRefreshRatesRange(
0, 0,
LIMIT_MODE_70.getPhysicalWidth(),
@@ -213,7 +214,7 @@
APP_MODE_HIGH_90.getPhysicalHeight()),
Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()),
- Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
Vote.forSizeAndPhysicalRefreshRatesRange(
0, 0,
LIMIT_MODE_70.getPhysicalWidth(),
@@ -229,7 +230,7 @@
APP_MODE_HIGH_90.getPhysicalHeight()),
Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()),
- Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
Vote.forSizeAndPhysicalRefreshRatesRange(
0, 0,
LIMIT_MODE_70.getPhysicalWidth(),
@@ -245,7 +246,7 @@
Vote.PRIORITY_APP_REQUEST_SIZE,
Vote.forSize(APP_MODE_65.getPhysicalWidth(),
APP_MODE_65.getPhysicalHeight()),
- Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
Vote.forPhysicalRefreshRates(
0, 64.99f))}});
@@ -598,7 +599,7 @@
< Vote.PRIORITY_APP_REQUEST_SIZE);
assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH
- > Vote.PRIORITY_LOW_POWER_MODE);
+ > Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE);
Display.Mode[] modes = new Display.Mode[4];
modes[0] = new Display.Mode(
@@ -676,9 +677,9 @@
@Test
public void testLPMHasHigherPriorityThanUser() {
- assertTrue(Vote.PRIORITY_LOW_POWER_MODE
+ assertTrue(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE
> Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
- assertTrue(Vote.PRIORITY_LOW_POWER_MODE
+ assertTrue(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE
> Vote.PRIORITY_APP_REQUEST_SIZE);
Display.Mode[] modes = new Display.Mode[4];
@@ -700,7 +701,7 @@
Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
appRequestedMode.getPhysicalHeight()));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(60, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(2);
@@ -715,7 +716,7 @@
Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
appRequestedMode.getPhysicalHeight()));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(4);
@@ -730,7 +731,7 @@
Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
appRequestedMode.getPhysicalHeight()));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(60, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(2);
@@ -745,7 +746,7 @@
Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
appRequestedMode.getPhysicalHeight()));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(4);
@@ -906,7 +907,7 @@
Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(60, 60));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse();
@@ -946,7 +947,7 @@
votesByDisplay.put(DISPLAY_ID, votes);
votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(30, 90));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
assertThat(director.getModeSwitchingType())
@@ -987,7 +988,7 @@
votesByDisplay.put(DISPLAY_ID, votes);
votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(30, 90));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
assertThat(director.getModeSwitchingType())
@@ -1029,7 +1030,7 @@
votesByDisplay.put(DISPLAY_ID, votes);
votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(30, 90));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
assertThat(director.getModeSwitchingType())
@@ -1900,7 +1901,7 @@
director.start(createMockSensorManager());
SparseArray<Vote> votes = new SparseArray<>();
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 50f));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 50f));
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
votesByDisplay.put(DISPLAY_ID_2, votes);
@@ -2298,7 +2299,7 @@
votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(90, Float.POSITIVE_INFINITY));
votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(50);
@@ -2311,7 +2312,7 @@
votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(80, Float.POSITIVE_INFINITY));
votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 90));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 90));
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(80);
assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(80);
@@ -2323,7 +2324,7 @@
votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(80, Float.POSITIVE_INFINITY));
votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 90));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 90));
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
@@ -2343,7 +2344,7 @@
votesByDisplay.put(DISPLAY_ID, votes);
votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(70));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
@@ -2360,7 +2361,7 @@
votes.clear();
votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(55));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
@@ -2374,7 +2375,7 @@
Vote.forRenderFrameRates(0, 52));
votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(55));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
@@ -2392,7 +2393,7 @@
Vote.forRenderFrameRates(0, 58));
votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(55));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
@@ -2521,7 +2522,7 @@
votes.put(Vote.PRIORITY_UDFPS, Vote.forPhysicalRefreshRates(120, 120));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(120);
@@ -2542,7 +2543,7 @@
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
votesByDisplay.put(DISPLAY_ID, votes);
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(0, 30));
director.injectVotesByDisplay(votesByDisplay);
@@ -3168,7 +3169,8 @@
/* defaultRefreshRate= */ 60,
/* defaultPeakRefreshRate= */ 65,
/* defaultRefreshRateInHbmHdr= */ 65,
- /* defaultRefreshRateInHbmSunlight= */ 75);
+ /* defaultRefreshRateInHbmSunlight= */ 75,
+ /* lowPowerSupportedModes= */ List.of());
when(displayDeviceConfig.getRefreshRateData()).thenReturn(refreshRateData);
when(displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate()).thenReturn(50);
when(displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate()).thenReturn(55);
@@ -3390,9 +3392,10 @@
ArgumentCaptor<DisplayListener> displayListenerCaptor =
ArgumentCaptor.forClass(DisplayListener.class);
- verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
+ verify(mInjector, atLeastOnce()).registerDisplayListener(displayListenerCaptor.capture(),
any(Handler.class));
- DisplayListener displayListener = displayListenerCaptor.getValue();
+ // DisplayObserver should register first
+ DisplayListener displayListener = displayListenerCaptor.getAllValues().get(0);
float refreshRate = 60;
mInjector.mDisplayInfo.layoutLimitedRefreshRate =
@@ -3417,9 +3420,10 @@
ArgumentCaptor<DisplayListener> displayListenerCaptor =
ArgumentCaptor.forClass(DisplayListener.class);
- verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
+ verify(mInjector, atLeastOnce()).registerDisplayListener(displayListenerCaptor.capture(),
any(Handler.class));
- DisplayListener displayListener = displayListenerCaptor.getValue();
+ // DisplayObserver should register first
+ DisplayListener displayListener = displayListenerCaptor.getAllValues().get(0);
mInjector.mDisplayInfo.layoutLimitedRefreshRate = new RefreshRateRange(10, 10);
mInjector.mDisplayInfoValid = false;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
index 2d317af..ee79d19 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
@@ -407,7 +407,8 @@
assertThat(mObserver).isNull();
mObserver = invocation.getArgument(0);
return null;
- }).when(mInjector).registerDisplayListener(any(), any());
+ }).when(mInjector).registerDisplayListener(
+ any(DisplayModeDirector.DisplayObserver.class), any());
doAnswer(c -> {
DisplayInfo info = c.getArgument(1);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
index 4d910ce..e431c8c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
@@ -27,8 +27,11 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.test.FakeSettingsProvider
import com.android.server.display.DisplayDeviceConfig
+import com.android.server.display.config.RefreshRateData
+import com.android.server.display.config.SupportedModeData
import com.android.server.display.feature.DisplayManagerFlags
import com.android.server.display.mode.DisplayModeDirector.DisplayDeviceConfigProvider
+import com.android.server.display.mode.SupportedRefreshRatesVote.RefreshRates
import com.android.server.testutils.TestHandler
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
@@ -69,6 +72,13 @@
private val RANGES_MIN60_60TO90 = RefreshRateRanges(RANGE_60_INF, RANGE_60_90)
private val RANGES_MIN90_90TO90 = RefreshRateRanges(RANGE_90_INF, RANGE_90_90)
+private val LOW_POWER_GLOBAL_VOTE = Vote.forRenderFrameRates(0f, 60f)
+private val LOW_POWER_REFRESH_RATE_DATA = createRefreshRateData(
+ lowPowerSupportedModes = listOf(SupportedModeData(60f, 60f), SupportedModeData(60f, 240f)))
+private val LOW_POWER_EMPTY_REFRESH_RATE_DATA = createRefreshRateData()
+private val EXPECTED_SUPPORTED_MODES_VOTE = SupportedRefreshRatesVote(
+ listOf(RefreshRates(60f, 60f), RefreshRates(60f, 240f)))
+
@SmallTest
@RunWith(TestParameterInjector::class)
class SettingsObserverTest {
@@ -103,7 +113,7 @@
val displayModeDirector = DisplayModeDirector(
spyContext, testHandler, mockInjector, mockFlags, mockDisplayDeviceConfigProvider)
val ddcByDisplay = SparseArray<DisplayDeviceConfig>()
- whenever(mockDeviceConfig.isVrrSupportEnabled).thenReturn(testCase.vrrSupported)
+ whenever(mockDeviceConfig.refreshRateData).thenReturn(testCase.refreshRateData)
ddcByDisplay.put(Display.DEFAULT_DISPLAY, mockDeviceConfig)
displayModeDirector.injectDisplayDeviceConfigByDisplay(ddcByDisplay)
val settingsObserver = displayModeDirector.SettingsObserver(
@@ -113,27 +123,30 @@
false, Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE), 1)
assertThat(displayModeDirector.getVote(VotesStorage.GLOBAL_ID,
- Vote.PRIORITY_LOW_POWER_MODE)).isEqualTo(testCase.expectedVote)
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE)).isEqualTo(testCase.globalVote)
+ assertThat(displayModeDirector.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_LOW_POWER_MODE_MODES)).isEqualTo(testCase.displayVote)
}
enum class LowPowerTestCase(
- val vrrSupported: Boolean,
+ val refreshRateData: RefreshRateData,
val vsyncLowPowerVoteEnabled: Boolean,
val lowPowerModeEnabled: Boolean,
- internal val expectedVote: Vote?
+ internal val globalVote: Vote?,
+ internal val displayVote: Vote?
) {
- ALL_ENABLED(true, true, true,
- SupportedRefreshRatesVote(listOf(
- SupportedRefreshRatesVote.RefreshRates(60f, 240f),
- SupportedRefreshRatesVote.RefreshRates(60f, 60f)
- ))),
- LOW_POWER_OFF(true, true, false, null),
- DVRR_NOT_SUPPORTED_LOW_POWER_ON(false, true, true,
- RefreshRateVote.RenderVote(0f, 60f)),
- DVRR_NOT_SUPPORTED_LOW_POWER_OFF(false, true, false, null),
- VSYNC_VOTE_DISABLED_SUPPORTED_LOW_POWER_ON(true, false, true,
- RefreshRateVote.RenderVote(0f, 60f)),
- VSYNC_VOTE_DISABLED_LOW_POWER_OFF(true, false, false, null),
+ ALL_ENABLED(LOW_POWER_REFRESH_RATE_DATA, true, true,
+ LOW_POWER_GLOBAL_VOTE, EXPECTED_SUPPORTED_MODES_VOTE),
+ LOW_POWER_OFF(LOW_POWER_REFRESH_RATE_DATA, true, false,
+ null, null),
+ EMPTY_REFRESH_LOW_POWER_ON(LOW_POWER_EMPTY_REFRESH_RATE_DATA, true, true,
+ LOW_POWER_GLOBAL_VOTE, null),
+ EMPTY_REFRESH__LOW_POWER_OFF(LOW_POWER_EMPTY_REFRESH_RATE_DATA, true, false,
+ null, null),
+ VSYNC_VOTE_DISABLED_SUPPORTED_LOW_POWER_ON(LOW_POWER_REFRESH_RATE_DATA, false, true,
+ LOW_POWER_GLOBAL_VOTE, null),
+ VSYNC_VOTE_DISABLED_LOW_POWER_OFF(LOW_POWER_REFRESH_RATE_DATA, false, false,
+ null, null),
}
@Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
index 6b90bde..1206e30 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
@@ -16,6 +16,9 @@
package com.android.server.display.mode
+import com.android.server.display.config.RefreshRateData
+import com.android.server.display.config.SupportedModeData
+
internal fun createVotesSummary(
isDisplayResolutionRangeVotingEnabled: Boolean = true,
supportedModesVoteEnabled: Boolean = true,
@@ -24,4 +27,16 @@
): VoteSummary {
return VoteSummary(isDisplayResolutionRangeVotingEnabled, supportedModesVoteEnabled,
loggingEnabled, supportsFrameRateOverride)
-}
\ No newline at end of file
+}
+
+fun createRefreshRateData(
+ defaultRefreshRate: Int = 60,
+ defaultPeakRefreshRate: Int = 60,
+ defaultRefreshRateInHbmHdr: Int = 60,
+ defaultRefreshRateInHbmSunlight: Int = 60,
+ lowPowerSupportedModes: List<SupportedModeData> = emptyList()
+): RefreshRateData {
+ return RefreshRateData(defaultRefreshRate, defaultPeakRefreshRate,
+ defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight,
+ lowPowerSupportedModes)
+}
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 27c522d..b56af87 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -25,6 +25,13 @@
value="/data/local/tmp/cts/content/broken_shortcut.xml" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" />
+ <option name="set-global-setting" key="verifier_engprod" value="1" />
+ <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+ <option name="restore-settings" value="true" />
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="install-arg" value="-t" />
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index be5e262..c1ae852 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -24,8 +24,10 @@
import static org.mockito.Mockito.spy;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.os.Looper;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -72,6 +74,11 @@
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 5be3c8e..a5f7bb1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -22,8 +22,10 @@
import static org.mockito.Mockito.spy;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
import android.os.test.TestLooper;
@@ -84,6 +86,11 @@
protected Looper getServiceLooper() {
return mTestLooper.getLooper();
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index 7845c30..857ee1a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -22,8 +22,10 @@
import static org.mockito.Mockito.spy;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;
@@ -90,6 +92,11 @@
protected Looper getServiceLooper() {
return mTestLooper.getLooper();
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
Looper looper = mTestLooper.getLooper();
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 98789ac..6ace9f1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
@@ -33,8 +33,10 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
@@ -140,17 +142,8 @@
// do nothing
}
- /**
- * Override displayOsd to prevent it from broadcasting an intent, which
- * can trigger a SecurityException.
- */
@Override
- void displayOsd(int messageId) {
- // do nothing
- }
-
- @Override
- void displayOsd(int messageId, int extra) {
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
// do nothing
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
index 9b65762..2dd593c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
@@ -18,6 +18,8 @@
import static org.junit.Assert.assertEquals;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.content.Intent;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
@@ -103,6 +105,11 @@
protected Looper getServiceLooper() {
return mTestLooper.getLooper();
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiCecLocalDeviceAudioSystem =
new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
index 922706e..e669e7c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -25,8 +25,10 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -90,6 +92,11 @@
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
index 68ef80f..29d20a6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
@@ -30,7 +30,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -123,6 +125,11 @@
boolean isPowerStandby() {
return false;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
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 26b448a..d32b75b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
@@ -29,7 +29,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -132,6 +134,11 @@
boolean isPowerStandby() {
return false;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index a621055..c7574bd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -36,6 +36,7 @@
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -98,6 +99,8 @@
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
+ doNothing().when(mHdmiControlServiceSpy)
+ .sendBroadcastAsUser(any(Intent.class));
doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter();
HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
doReturn(hdmiCecConfig).when(mHdmiControlServiceSpy).getHdmiCecConfig();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java
index 30ce961..e1e101f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java
@@ -19,6 +19,7 @@
import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_UNKNOWN;
import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -30,6 +31,7 @@
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -90,6 +92,8 @@
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
+ doNothing().when(mHdmiControlServiceSpy)
+ .sendBroadcastAsUser(any(Intent.class));
doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter();
HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index 0870bac..7ed596e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -48,6 +48,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Binder;
@@ -110,6 +111,8 @@
doAnswer(__ -> mCecVersion).when(mHdmiControlServiceSpy).getCecVersion();
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
+ doNothing().when(mHdmiControlServiceSpy)
+ .sendBroadcastAsUser(any(Intent.class));
mHdmiControlServiceSpy.setDeviceConfig(new FakeDeviceConfigWrapper());
mNativeWrapper = new FakeNativeWrapper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 5520897..5502de8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -26,7 +26,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -114,6 +116,11 @@
return defVal;
}
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiControlService.getHdmiCecConfig().setIntValue(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 28da97c..8df7d54 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -28,7 +28,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -138,6 +140,11 @@
boolean canGoToStandby() {
return true;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 3dd8312..192be2a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -37,7 +37,9 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.tv.cec.V1_0.Result;
@@ -169,6 +171,11 @@
void wakeUp() {
mWakeupMessageReceived = true;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiControlService.setIoLooper(mTestLooper.getLooper());
mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 4faeea5..b5f0a52 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -41,7 +41,9 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -184,17 +186,8 @@
return mEarcBlocksArc;
}
- /**
- * Override displayOsd to prevent it from broadcasting an intent, which
- * can trigger a SecurityException.
- */
@Override
- void displayOsd(int messageId) {
- // do nothing
- }
-
- @Override
- void displayOsd(int messageId, int extra) {
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
// do nothing
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index c002067..9412ee0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -21,8 +21,10 @@
import static org.mockito.Mockito.spy;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -88,6 +90,11 @@
boolean isPowerStandby() {
return false;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiControlService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index e1b66b5..126a658 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -52,6 +52,7 @@
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -121,6 +122,8 @@
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
+ doNothing().when(mHdmiControlServiceSpy)
+ .sendBroadcastAsUser(any(Intent.class));
mMyLooper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index 4641802..298ff46 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -26,8 +26,10 @@
import static org.mockito.Mockito.spy;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -104,6 +106,11 @@
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index 9f0a44c..1d4a72f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -25,8 +25,10 @@
import static org.mockito.Mockito.spy;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -78,6 +80,11 @@
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
index 043db1e..cafe1e7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
@@ -21,7 +21,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.os.Looper;
@@ -115,6 +117,11 @@
boolean isPowerStandbyOrTransient() {
return false;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiControlService.setIoLooper(mMyLooper);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java
index 061e1f9..864a182 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java
@@ -21,7 +21,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
@@ -72,6 +74,11 @@
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mIsPowerStandby = false;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java
index b25ea2c..06709cd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java
@@ -21,7 +21,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
@@ -75,6 +77,11 @@
boolean verifyPhysicalAddresses(HdmiCecMessage message) {
return true;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mMyLooper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
index f608c235..5163e29 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
@@ -29,7 +29,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -177,6 +179,11 @@
protected HdmiCecConfig getHdmiCecConfig() {
return hdmiCecConfig;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiControlService.setIoLooper(mMyLooper);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java
index a73f4aa..e4297ef 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java
@@ -24,12 +24,14 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
@@ -87,6 +89,8 @@
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
+ doNothing().when(mHdmiControlServiceSpy)
+ .sendBroadcastAsUser(any(Intent.class));
mLooper = mTestLooper.getLooper();
mHdmiControlServiceSpy.setIoLooper(mLooper);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index 02bed22..4dcc6a4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -25,8 +25,10 @@
import static org.mockito.Mockito.spy;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.os.Looper;
@@ -82,17 +84,8 @@
// do nothing
}
- /**
- * Override displayOsd to prevent it from broadcasting an intent, which
- * can trigger a SecurityException.
- */
@Override
- void displayOsd(int messageId) {
- // do nothing
- }
-
- @Override
- void displayOsd(int messageId, int extra) {
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
// do nothing
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index df27e78..4aa074b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -23,7 +23,9 @@
import static org.junit.Assert.assertTrue;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
@@ -143,6 +145,11 @@
protected boolean isStandbyMessageReceived() {
return mStandbyMessageReceived;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
index 07fb9fc..570256b 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
@@ -19,9 +19,16 @@
import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_RULE_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_RULE_DEFAULT;
+import static android.net.ConnectivityManager.FIREWALL_RULE_DENY;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.util.DebugUtils.valueToString;
import static org.junit.Assert.assertEquals;
@@ -51,7 +58,10 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.test.FakePermissionEnforcer;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import androidx.test.filters.SmallTest;
@@ -62,6 +72,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -84,6 +95,9 @@
@Mock private IBatteryStats.Stub mBatteryStatsService;
@Mock private INetd.Stub mNetdService;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
private static final int TEST_UID = 111;
@NonNull
@@ -254,6 +268,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS)
public void testMeteredNetworkRestrictions() throws RemoteException {
// Make sure the mocked netd method returns true.
doReturn(true).when(mNetdService).bandwidthEnableDataSaver(anyBoolean());
@@ -295,6 +310,69 @@
}
@Test
+ @EnableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS)
+ public void testMeteredNetworkRestrictionsByAdminChain() {
+ mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID,
+ FIREWALL_RULE_DENY);
+ verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID,
+ FIREWALL_RULE_DENY);
+ assertTrue("Should be true since mobile data usage is restricted by admin chain",
+ mNMService.isNetworkRestricted(TEST_UID));
+
+ mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID,
+ FIREWALL_RULE_DEFAULT);
+ verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID,
+ FIREWALL_RULE_DEFAULT);
+ assertFalse("Should be false since mobile data usage is no longer restricted by admin",
+ mNMService.isNetworkRestricted(TEST_UID));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS)
+ public void testMeteredNetworkRestrictionsByUserChain() {
+ mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID,
+ FIREWALL_RULE_DENY);
+ verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID,
+ FIREWALL_RULE_DENY);
+ assertTrue("Should be true since mobile data usage is restricted by user chain",
+ mNMService.isNetworkRestricted(TEST_UID));
+
+ mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID,
+ FIREWALL_RULE_DEFAULT);
+ verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID,
+ FIREWALL_RULE_DEFAULT);
+ assertFalse("Should be false since mobile data usage is no longer restricted by user",
+ mNMService.isNetworkRestricted(TEST_UID));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS)
+ public void testDataSaverRestrictionsWithAllowChain() {
+ mNMService.setDataSaverModeEnabled(true);
+ verify(mCm).setDataSaverEnabled(true);
+
+ assertTrue("Should be true since data saver is on and the uid is not allowlisted",
+ mNMService.isNetworkRestricted(TEST_UID));
+
+ mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID, FIREWALL_RULE_ALLOW);
+ verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID, FIREWALL_RULE_ALLOW);
+ assertFalse("Should be false since data saver is on and the uid is allowlisted",
+ mNMService.isNetworkRestricted(TEST_UID));
+
+ // remove uid from allowlist and turn datasaver off again
+
+ mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID,
+ FIREWALL_RULE_DEFAULT);
+ verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID,
+ FIREWALL_RULE_DEFAULT);
+ mNMService.setDataSaverModeEnabled(false);
+ verify(mCm).setDataSaverEnabled(false);
+
+ assertFalse("Network should not be restricted when data saver is off",
+ mNMService.isNetworkRestricted(TEST_UID));
+ }
+
+ @Test
public void testFirewallChains() {
final ArrayMap<Integer, ArrayMap<Integer, Boolean>> expected = new ArrayMap<>();
// Dozable chain
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index 4af20a9..a3d57c3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -67,6 +67,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.pm.UserInfo;
+import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
@@ -93,6 +94,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.config.sysui.TestableFlagResolver;
import com.android.internal.logging.InstanceIdSequence;
@@ -210,6 +212,16 @@
verify(mAccessibilityService).addClient(any(IAccessibilityManagerClient.class), anyInt());
assertTrue(mAccessibilityManager.isEnabled());
+ // Enable LED pulse setting by default
+ Settings.System.putInt(getContext().getContentResolver(),
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 1);
+
+ Resources resources = spy(getContext().getResources());
+ when(resources.getBoolean(R.bool.config_useAttentionLight)).thenReturn(true);
+ when(resources.getBoolean(
+ com.android.internal.R.bool.config_intrusiveNotificationLed)).thenReturn(true);
+ when(getContext().getResources()).thenReturn(resources);
+
// TODO (b/291907312): remove feature flag
// Disable feature flags by default. Tests should enable as needed.
mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS,
@@ -239,7 +251,6 @@
mAttentionHelper.setKeyguardManager(mKeyguardManager);
mAttentionHelper.setScreenOn(false);
mAttentionHelper.setInCallStateOffHook(false);
- mAttentionHelper.mNotificationPulseEnabled = true;
if (Flags.crossAppPoliteNotifications()) {
// Capture BroadcastReceiver for avalanche triggers
@@ -611,6 +622,14 @@
verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
}
+ private void verifyAttentionLights() {
+ verify(mLight, times(1)).pulse();
+ }
+
+ private void verifyNeverAttentionLights() {
+ verify(mLight, never()).pulse();
+ }
+
//
// Tests
//
@@ -1524,7 +1543,10 @@
@Test
public void testLightsLightsOffGlobally() {
- mAttentionHelper.mNotificationPulseEnabled = false;
+ Settings.System.putInt(getContext().getContentResolver(),
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 0);
+ initAttentionHelper(mTestFlagResolver);
+
NotificationRecord r = getLightsNotification();
mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
verifyNeverLights();
@@ -1533,6 +1555,44 @@
}
@Test
+ public void testLightsLightsResConfigDisabled() {
+ Resources resources = spy(getContext().getResources());
+ when(resources.getBoolean(
+ com.android.internal.R.bool.config_intrusiveNotificationLed)).thenReturn(false);
+ when(getContext().getResources()).thenReturn(resources);
+ initAttentionHelper(mTestFlagResolver);
+
+ NotificationRecord r = getLightsNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testLightsUseAttentionLight() {
+ NotificationRecord r = getLightsNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyAttentionLights();
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testLightsUseAttentionLightDisabled() {
+ Resources resources = spy(getContext().getResources());
+ when(resources.getBoolean(R.bool.config_useAttentionLight)).thenReturn(false);
+ when(resources.getBoolean(
+ com.android.internal.R.bool.config_intrusiveNotificationLed)).thenReturn(true);
+ when(getContext().getResources()).thenReturn(resources);
+ initAttentionHelper(mTestFlagResolver);
+
+ NotificationRecord r = getLightsNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverAttentionLights();
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
public void testLightsDndIntercepted() {
NotificationRecord r = getLightsNotification();
r.setSuppressedVisualEffects(SUPPRESSED_EFFECT_LIGHTS);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 200952c..926a994 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -15638,4 +15638,39 @@
assertThat(mService.checkDisqualifyingFeatures(mUserId, mUid, 0, null, r, false, false))
.isTrue();
}
+
+ @Test
+ public void testClearUIJFromUninstallingPackage() throws Exception {
+ NotificationRecord r =
+ generateNotificationRecord(mTestNotificationChannel, 0, mUserId, "bar");
+ mService.addNotification(r);
+
+ when(mPackageManagerClient.getPackageUidAsUser(anyString(), anyInt()))
+ .thenThrow(PackageManager.NameNotFoundException.class);
+ when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(false);
+
+ mInternalService.cancelNotification(mPkg, mPkg, mUid, 0, r.getSbn().getTag(),
+ r.getSbn().getId(), mUserId);
+
+ // no exception
+ }
+
+ @Test
+ public void testPostFromMissingPackage_throws() throws Exception {
+ NotificationRecord r =
+ generateNotificationRecord(mTestNotificationChannel, 0, mUserId, "bar");
+
+ when(mPackageManagerClient.getPackageUidAsUser(anyString(), anyInt()))
+ .thenThrow(PackageManager.NameNotFoundException.class);
+ when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(false);
+
+ try {
+ mBinderService.enqueueNotificationWithTag(mPkg, mPkg, r.getSbn().getTag(),
+ r.getSbn().getId(), r.getSbn().getNotification(),
+ r.getSbn().getUserId());
+ fail("Allowed to post a notification for an absent package");
+ } catch (SecurityException e) {
+ // yay
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index 7380aec..d8d5729 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -65,7 +65,7 @@
performSurfacePlacementAndWaitForWindowAnimator();
mImeProvider.scheduleShowImePostLayout(appWin, ImeTracker.Token.empty());
- assertTrue(mImeProvider.isReadyToShowIme());
+ assertTrue(mImeProvider.isScheduledAndReadyToShowIme());
}
/**
@@ -84,13 +84,13 @@
// Schedule (without triggering) after everything is ready.
mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
- assertTrue(mImeProvider.isReadyToShowIme());
+ assertTrue(mImeProvider.isScheduledAndReadyToShowIme());
assertFalse(mImeProvider.isImeShowing());
// Manually trigger the show.
- mImeProvider.checkShowImePostLayout();
- // No longer ready as it was already shown.
- assertFalse(mImeProvider.isReadyToShowIme());
+ mImeProvider.checkAndStartShowImePostLayout();
+ // No longer scheduled as it was already shown.
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertTrue(mImeProvider.isImeShowing());
}
@@ -104,7 +104,7 @@
// Schedule before anything is ready.
mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
- assertFalse(mImeProvider.isReadyToShowIme());
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertFalse(mImeProvider.isImeShowing());
final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
@@ -115,8 +115,8 @@
mDisplayContent.updateImeInputAndControlTarget(target);
// Performing surface placement picks up the show scheduled above.
performSurfacePlacementAndWaitForWindowAnimator();
- // No longer ready as it was already shown.
- assertFalse(mImeProvider.isReadyToShowIme());
+ // No longer scheduled as it was already shown.
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertTrue(mImeProvider.isImeShowing());
}
@@ -137,19 +137,19 @@
// Schedule before starting the afterPrepareSurfacesRunnable.
mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
- assertFalse(mImeProvider.isReadyToShowIme());
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertFalse(mImeProvider.isImeShowing());
// This tries to pick up the show scheduled above, but must fail as the
// afterPrepareSurfacesRunnable was not started yet.
mDisplayContent.applySurfaceChangesTransaction();
- assertFalse(mImeProvider.isReadyToShowIme());
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertFalse(mImeProvider.isImeShowing());
// Starting the afterPrepareSurfacesRunnable picks up the show scheduled above.
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
- // No longer ready as it was already shown.
- assertFalse(mImeProvider.isReadyToShowIme());
+ // No longer scheduled as it was already shown.
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertTrue(mImeProvider.isImeShowing());
}
@@ -169,7 +169,7 @@
// Schedule before surface placement.
mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
- assertFalse(mImeProvider.isReadyToShowIme());
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertFalse(mImeProvider.isImeShowing());
// Performing surface placement picks up the show scheduled above, and succeeds.
@@ -177,8 +177,8 @@
// applySurfaceChangesTransaction. Both of them try to trigger the show,
// but only the second one can succeed, as it comes after onPostLayout.
performSurfacePlacementAndWaitForWindowAnimator();
- // No longer ready as it was already shown.
- assertFalse(mImeProvider.isReadyToShowIme());
+ // No longer scheduled as it was already shown.
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertTrue(mImeProvider.isImeShowing());
}
diff --git a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
index 1dc1037..82de070 100644
--- a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
index 57a58c8..4ffb11a 100644
--- a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
index 2cb86e0..0fa4d07 100644
--- a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
index 2cf85fa..4d9fefb 100644
--- a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/IME/AndroidTestTemplate.xml b/tests/FlickerTests/IME/AndroidTestTemplate.xml
index b93e1be..b879c54 100644
--- a/tests/FlickerTests/IME/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/IME/AndroidTestTemplate.xml
@@ -82,6 +82,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/IME/OWNERS b/tests/FlickerTests/IME/OWNERS
index ae1098d..e3a2e67 100644
--- a/tests/FlickerTests/IME/OWNERS
+++ b/tests/FlickerTests/IME/OWNERS
@@ -1,3 +1,3 @@
# ime
# Bug component: 34867
-include /services/core/java/com/android/server/inputmethod/OWNERS
+file:/services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/tests/FlickerTests/Notification/AndroidTestTemplate.xml b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
index 9c6a17d3..04b312a 100644
--- a/tests/FlickerTests/Notification/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
index ecbed28..8acdabc 100644
--- a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
index 1eacdfd..91ece21 100644
--- a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 9198ae1..3e500d9 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -18,7 +18,10 @@
package="com.android.server.wm.flicker.testapp">
<uses-sdk android:minSdkVersion="29"
- android:targetSdkVersion="29"/>
+ android:targetSdkVersion="35"/>
+
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+
<application android:allowBackup="false"
android:supportsRtl="true">
<uses-library android:name="androidx.window.extensions" android:required="false"/>
@@ -107,7 +110,7 @@
android:immersive="true"
android:resizeableActivity="true"
android:screenOrientation="portrait"
- android:theme="@android:style/Theme.NoTitleBar"
+ android:theme="@style/OptOutEdgeToEdge.NoTitleBar"
android:configChanges="screenSize"
android:label="PortraitImmersiveActivity"
android:exported="true">
@@ -119,7 +122,7 @@
<activity android:name=".LaunchTransparentActivity"
android:resizeableActivity="false"
android:screenOrientation="portrait"
- android:theme="@android:style/Theme"
+ android:theme="@style/OptOutEdgeToEdge"
android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchTransparentActivity"
android:label="LaunchTransparentActivity"
android:exported="true">
@@ -273,7 +276,7 @@
android:exported="true"
android:label="MailActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.MailActivity"
- android:theme="@style/Theme.AppCompat.Light">
+ android:theme="@style/OptOutEdgeToEdge.AppCompatTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -282,7 +285,7 @@
<activity android:name=".GameActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.GameActivity"
android:immersive="true"
- android:theme="@android:style/Theme.NoTitleBar"
+ android:theme="@style/OptOutEdgeToEdge.NoTitleBar"
android:configChanges="screenSize"
android:label="GameActivity"
android:exported="true">
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
index 9b742d9..47d1137 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -16,7 +16,19 @@
-->
<resources>
- <style name="DefaultTheme" parent="@android:style/Theme.DeviceDefault">
+ <style name="OptOutEdgeToEdge" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+ </style>
+
+ <style name="OptOutEdgeToEdge.NoTitleBar" parent="@android:style/Theme.NoTitleBar">
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+ </style>
+
+ <style name="OptOutEdgeToEdge.AppCompatTheme" parent="@style/Theme.AppCompat.Light">
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+ </style>
+
+ <style name="DefaultTheme" parent="@style/OptOutEdgeToEdge">
<item name="android:windowBackground">@android:color/darker_gray</item>
</style>
@@ -32,7 +44,7 @@
<item name="android:windowLayoutInDisplayCutoutMode">never</item>
</style>
- <style name="DialogTheme" parent="@android:style/Theme.DeviceDefault">
+ <style name="DialogTheme" parent="@style/OptOutEdgeToEdge">
<item name="android:windowAnimationStyle">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@null</item>
@@ -43,18 +55,18 @@
<item name="android:windowSoftInputMode">stateUnchanged</item>
</style>
- <style name="TransparentTheme" parent="@android:style/Theme.DeviceDefault">
+ <style name="TransparentTheme" parent="@style/OptOutEdgeToEdge">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
- <style name="no_starting_window" parent="@android:style/Theme.DeviceDefault">
+ <style name="no_starting_window" parent="@style/OptOutEdgeToEdge">
<item name="android:windowDisablePreview">true</item>
</style>
- <style name="SplashscreenAppTheme" parent="@android:style/Theme.DeviceDefault">
+ <style name="SplashscreenAppTheme" parent="@style/OptOutEdgeToEdge">
<!-- Splashscreen Attributes -->
<item name="android:windowSplashScreenAnimatedIcon">@drawable/avd_anim</item>
<!-- Here we want to match the duration of our AVD -->
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
index c92b82b..a86ba5f 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
@@ -125,7 +125,7 @@
.setContentTitle("BubbleChat")
.setContentIntent(PendingIntent.getActivity(mContext, 0,
new Intent(mContext, LaunchBubbleActivity.class),
- PendingIntent.FLAG_UPDATE_CURRENT))
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE))
.setStyle(new Notification.MessagingStyle(chatBot)
.setConversationTitle("BubbleChat")
.addMessage("BubbleChat",
@@ -140,7 +140,7 @@
Intent target = new Intent(mContext, BubbleActivity.class);
target.putExtra(EXTRA_BUBBLE_NOTIF_ID, info.id);
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, info.id, target,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
return new Notification.BubbleMetadata.Builder()
.setIntent(bubbleIntent)
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
index dea3444..37332c9 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
@@ -17,6 +17,9 @@
package com.android.server.wm.flicker.testapp;
+import static android.Manifest.permission.POST_NOTIFICATIONS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import android.app.Activity;
import android.app.Person;
import android.content.Context;
@@ -24,6 +27,7 @@
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.graphics.drawable.Icon;
+import android.os.Build;
import android.os.Bundle;
import android.view.View;
@@ -36,6 +40,13 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+ && checkSelfPermission(POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
+ // POST_NOTIFICATIONS permission required for notification post sdk 33.
+ requestPermissions(new String[] { POST_NOTIFICATIONS }, 0);
+ }
+
addInboxShortcut(getApplicationContext());
mBubbleHelper = BubbleHelper.getInstance(this);
setContentView(R.layout.activity_main);
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
index a4dd575..d6427ab 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
@@ -16,6 +16,9 @@
package com.android.server.wm.flicker.testapp;
+import static android.Manifest.permission.POST_NOTIFICATIONS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -23,6 +26,7 @@
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.Intent;
+import android.os.Build;
import android.os.Bundle;
import android.view.WindowManager;
import android.widget.Button;
@@ -34,6 +38,13 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+ && checkSelfPermission(POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
+ // POST_NOTIFICATIONS permission required for notification post sdk 33.
+ requestPermissions(new String[] { POST_NOTIFICATIONS }, 0);
+ }
+
WindowManager.LayoutParams p = getWindow().getAttributes();
p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
index 1ab8ddb..27eb5a0 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
@@ -198,7 +198,7 @@
filter.addAction(ACTION_SET_REQUESTED_ORIENTATION);
filter.addAction(ACTION_ENTER_PIP);
filter.addAction(ACTION_ASPECT_RATIO);
- registerReceiver(mBroadcastReceiver, filter);
+ registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED);
handleIntentExtra(getIntent());
}
@@ -222,8 +222,8 @@
private RemoteAction buildRemoteAction(Icon icon, String label, String action) {
final Intent intent = new Intent(action);
- final PendingIntent pendingIntent =
- PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
return new RemoteAction(icon, label, label, pendingIntent);
}