Merge "Add flicker test for snap resizing using keyboard shortcuts." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 97d28d1..2f843f9 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -345,7 +345,7 @@
name: "android.nfc.flags-aconfig",
package: "android.nfc",
container: "system",
- srcs: ["nfc/java/android/nfc/*.aconfig"],
+ srcs: ["nfc-non-updatable/flags/*.aconfig"],
}
cc_aconfig_library {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 8f44698..5dfb375 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -1034,7 +1034,7 @@
for (int p = preferredUidOnly.size() - 1; p >= 0; --p) {
final ContextAssignment assignment = preferredUidOnly.get(p);
final JobStatus runningJob = assignment.context.getRunningJobLocked();
- if (runningJob.getUid() != nextPending.getUid()) {
+ if (runningJob == null || runningJob.getUid() != nextPending.getUid()) {
continue;
}
final int jobBias = mService.evaluateJobBiasLocked(runningJob);
@@ -1916,8 +1916,9 @@
for (int i = 0; i < mActiveServices.size(); i++) {
final JobServiceContext jc = mActiveServices.get(i);
final JobStatus js = jc.getRunningJobLocked();
- if (jc.stopIfExecutingLocked(pkgName, userId, namespace, matchJobId, jobId,
- stopReason, internalStopReason)) {
+ if (js != null &&
+ jc.stopIfExecutingLocked(pkgName, userId, namespace,
+ matchJobId, jobId, stopReason, internalStopReason)) {
foundSome = true;
pw.print("Stopping job: ");
js.printUniqueId(pw);
diff --git a/core/api/current.txt b/core/api/current.txt
index ca4b2fa..32507df 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8110,7 +8110,7 @@
method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTION, conditional=true) public boolean addCrossProfileWidgetProvider(@Nullable android.content.ComponentName, String);
method public int addOverrideApn(@NonNull android.content.ComponentName, @NonNull android.telephony.data.ApnSetting);
method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_TASK, conditional=true) public void addPersistentPreferredActivity(@Nullable android.content.ComponentName, android.content.IntentFilter, @NonNull android.content.ComponentName);
- method public void addUserRestriction(@NonNull android.content.ComponentName, String);
+ method public void addUserRestriction(@Nullable android.content.ComponentName, String);
method public void addUserRestrictionGlobally(@NonNull String);
method public boolean bindDeviceAdminServiceAsUser(@NonNull android.content.ComponentName, @NonNull android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
method public boolean bindDeviceAdminServiceAsUser(@NonNull android.content.ComponentName, @NonNull android.content.Intent, @NonNull android.content.ServiceConnection, @NonNull android.content.Context.BindServiceFlags, @NonNull android.os.UserHandle);
@@ -8122,7 +8122,7 @@
method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_TASK, conditional=true) public void clearPackagePersistentPreferredActivities(@Nullable android.content.ComponentName, String);
method @Deprecated public void clearProfileOwner(@NonNull android.content.ComponentName);
method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD, conditional=true) public boolean clearResetPasswordToken(@Nullable android.content.ComponentName);
- method public void clearUserRestriction(@NonNull android.content.ComponentName, String);
+ method public void clearUserRestriction(@Nullable android.content.ComponentName, String);
method public android.content.Intent createAdminSupportIntent(@NonNull String);
method @Nullable public android.os.UserHandle createAndManageUser(@NonNull android.content.ComponentName, @NonNull String, @NonNull android.content.ComponentName, @Nullable android.os.PersistableBundle, int);
method public void enableSystemApp(@NonNull android.content.ComponentName, String);
@@ -8183,7 +8183,7 @@
method @Deprecated @ColorInt public int getOrganizationColor(@NonNull android.content.ComponentName);
method @Nullable @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, conditional=true) public CharSequence getOrganizationName(@Nullable android.content.ComponentName);
method public java.util.List<android.telephony.data.ApnSetting> getOverrideApns(@NonNull android.content.ComponentName);
- method @NonNull public android.app.admin.DevicePolicyManager getParentProfileInstance(@NonNull android.content.ComponentName);
+ method @NonNull public android.app.admin.DevicePolicyManager getParentProfileInstance(@Nullable android.content.ComponentName);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY}, conditional=true) public int getPasswordComplexity();
method public long getPasswordExpiration(@Nullable android.content.ComponentName);
method public long getPasswordExpirationTimeout(@Nullable android.content.ComponentName);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9ddc729..39c27a1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -12190,13 +12190,6 @@
* be enforced device-wide. These constants will also state in their documentation which
* permission is required to manage the restriction using this API.
*
- * <p>For callers targeting Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or
- * above, calling this API will result in applying the restriction locally on the calling user,
- * or locally on the parent profile if called from the
- * {@link DevicePolicyManager} instance obtained from
- * {@link #getParentProfileInstance(ComponentName)}. To set a restriction globally, call
- * {@link #addUserRestrictionGlobally} instead.
- *
* <p>
* Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, after the user restriction
* policy has been set, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
@@ -12217,13 +12210,18 @@
* same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
* will contain the reason why the policy changed.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with or
+ * {@code null} if the caller is not a device admin.
* @param key The key of the restriction.
* @throws SecurityException if {@code admin} is not a device or profile owner and if the caller
* has not been granted the permission to set the given user restriction.
*/
+ // NB: For permission-based callers using this API will result in applying the restriction
+ // locally on the calling user or locally on the parent profile if called from through parent
+ // instance. To set a restriction globally, call addUserRestrictionGlobally() instead.
+ // Permission-based callers must target Android U or above.
@SupportsCoexistence
- public void addUserRestriction(@NonNull ComponentName admin,
+ public void addUserRestriction(@Nullable ComponentName admin,
@UserManager.UserRestrictionKey String key) {
if (mService != null) {
try {
@@ -12358,10 +12356,6 @@
* constants state in their documentation which permission is required to manage the restriction
* using this API.
*
- * <p>For callers targeting Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or
- * above, calling this API will result in clearing any local and global restriction with the
- * specified key that was previously set by the caller.
- *
* <p>
* Starting from {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, after the user restriction
* policy has been cleared, {@link PolicyUpdateReceiver#onPolicySetResult(Context, String,
@@ -12382,13 +12376,17 @@
* same parameters as PolicyUpdateReceiver#onPolicySetResult and the {@link PolicyUpdateResult}
* will contain the reason why the policy changed.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with or
+ * {@code null} if the caller is not a device admin.
* @param key The key of the restriction.
* @throws SecurityException if {@code admin} is not a device or profile owner and if the
* caller has not been granted the permission to set the given user restriction.
*/
+ // NB: For permission-based callers using this API will result in clearing any local and global
+ // restriction with the specified key that was previously set by the caller.
+ // Permission-based callers must target Android U or above.
@SupportsCoexistence
- public void clearUserRestriction(@NonNull ComponentName admin,
+ public void clearUserRestriction(@Nullable ComponentName admin,
@UserManager.UserRestrictionKey String key) {
if (mService != null) {
try {
@@ -14556,8 +14554,8 @@
}
/**
- * Called by the profile owner of a managed profile to obtain a {@link DevicePolicyManager}
- * whose calls act on the parent profile.
+ * Called by the profile owner of a managed profile or other apps in a managed profile to
+ * obtain a {@link DevicePolicyManager} whose calls act on the parent profile.
*
* <p>The following methods are supported for the parent instance, all other methods will
* throw a SecurityException when called on the parent instance:
@@ -14614,10 +14612,12 @@
* <li>{@link #wipeData}</li>
* </ul>
*
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with or
+ * {@code null} if the caller is not a profile owner.
* @return a new instance of {@link DevicePolicyManager} that acts on the parent profile.
- * @throws SecurityException if {@code admin} is not a profile owner.
+ * @throws SecurityException if the current user is not a managed profile.
*/
- public @NonNull DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
+ public @NonNull DevicePolicyManager getParentProfileInstance(@Nullable ComponentName admin) {
throwIfParentInstance("getParentProfileInstance");
UserManager um = mContext.getSystemService(UserManager.class);
if (!um.isManagedProfile()) {
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index 47ef461..9f8505f 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -125,7 +125,10 @@
public static final int KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK = 75;
public static final int KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW = 76;
public static final int KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB = 77;
-
+ public static final int KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT = 78;
+ public static final int KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT = 79;
+ public static final int KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP = 80;
+ public static final int KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN = 81;
public static final int FLAG_CANCELLED = 1;
@@ -217,7 +220,11 @@
KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION,
KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK,
KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
- KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB
+ KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB,
+ KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT,
+ KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT,
+ KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP,
+ KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN,
})
@Retention(RetentionPolicy.SOURCE)
public @interface KeyGestureType {
@@ -792,6 +799,14 @@
return "KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW";
case KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB:
return "KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB";
+ case KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT:
+ return "KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT";
+ case KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT:
+ return "KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT";
+ case KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP:
+ return "KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP";
+ case KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN:
+ return "KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN";
default:
return Integer.toHexString(value);
}
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 289c5cf..be69d3d 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -86,7 +86,9 @@
ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS_BUGFIX(
Flags::enableDesktopAppLaunchAlttabTransitionsBugfix, false),
ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX(
- Flags::enableDesktopAppLaunchTransitionsBugfix, false);
+ Flags::enableDesktopAppLaunchTransitionsBugfix, false),
+ INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC(
+ Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true);
private static final String TAG = "DesktopModeFlagsUtil";
// Function called to obtain aconfig flag value.
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 1707e61b..3b77b1f 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -16,6 +16,17 @@
}
flag {
+ name: "include_top_transparent_fullscreen_task_in_desktop_heuristic"
+ namespace: "lse_desktop_experience"
+ description: "Whether to include any top transparent fullscreen task launched in desktop /n"
+ "mode in the heuristic for if desktop windowing is showing or not."
+ bug: "379543275"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_windowing_dynamic_initial_bounds"
namespace: "lse_desktop_experience"
description: "Enables new initial bounds for desktop windowing which adjust depending on app constraints"
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index d0d4af6..5a092b8 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -127,3 +127,14 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "track_system_ui_context_before_wms"
+ description: "Keep track of SystemUiContext before WMS is initialized"
+ bug: "384428048"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4a948dd..e133ca4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -9338,7 +9338,11 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
- <service android:name="com.android.server.profcollect.ProfcollectForwardingService$ProfcollectBGJobService"
+ <service android:name="com.android.server.profcollect.ProfcollectForwardingService$PeriodicTraceJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.profcollect.ProfcollectForwardingService$ReportProcessJobService"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a9c0667..565e28e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -7277,4 +7277,7 @@
features. Examples include the search functionality or the app
predictor. -->
<string name="config_systemVendorIntelligence" translatable="false"></string>
+
+ <!-- Whether the device supports Wi-Fi USD feature. -->
+ <bool name="config_deviceSupportsWifiUsd">false</bool>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3afb9d2..73e06f6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5801,5 +5801,7 @@
<java-symbol type="dimen" name="config_shapeCornerRadiusLarge"/>
<java-symbol type="dimen" name="config_shapeCornerRadiusXlarge"/>
+ <!-- Whether the device supports Wi-Fi USD feature. -->
+ <java-symbol type="bool" name="config_deviceSupportsWifiUsd" />
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
index bc56637..d1dcc9b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
@@ -18,22 +18,29 @@
package com.android.wm.shell.compatui
-import android.app.TaskInfo
+import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import com.android.internal.R
// TODO(b/347289970): Consider replacing with API
/**
* If the top activity should be exempt from desktop windowing and forced back to fullscreen.
- * Currently includes all system ui activities and modal dialogs. However is the top activity is not
+ * Currently includes all system ui activities and modal dialogs. However if the top activity is not
* being displayed, regardless of its configuration, we will not exempt it as to remain in the
* desktop windowing environment.
*/
-fun isTopActivityExemptFromDesktopWindowing(context: Context, task: TaskInfo) =
- (isSystemUiTask(context, task) || (task.numActivities > 0 && task.isActivityStackTransparent))
+fun isTopActivityExemptFromDesktopWindowing(context: Context, task: RunningTaskInfo) =
+ (isSystemUiTask(context, task) || isTransparentTask(task))
&& !task.isTopActivityNoDisplay
-private fun isSystemUiTask(context: Context, task: TaskInfo): Boolean {
+/**
+ * Returns true if all activities in a tasks stack are transparent. If there are no activities will
+ * return false.
+ */
+fun isTransparentTask(task: RunningTaskInfo): Boolean = task.isActivityStackTransparent
+ && task.numActivities > 0
+
+private fun isSystemUiTask(context: Context, task: RunningTaskInfo): Boolean {
val sysUiPackageName: String =
context.resources.getString(R.string.config_systemUi)
return task.baseActivity?.packageName == sysUiPackageName
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index c4abee3..c5b570dd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -54,7 +54,9 @@
* @property closingTasks task ids for tasks that are going to close, but are currently visible.
* @property freeformTasksInZOrder list of current freeform task ids ordered from top to bottom
* @property fullImmersiveTaskId the task id of the desktop task that is in full-immersive mode.
- * (top is at index 0).
+ * @property topTransparentFullscreenTaskId the task id of any current top transparent
+ * fullscreen task launched on top of Desktop Mode. Cleared when the transparent task is
+ * closed or sent to back. (top is at index 0).
*/
private data class DesktopTaskData(
val activeTasks: ArraySet<Int> = ArraySet(),
@@ -64,6 +66,7 @@
val closingTasks: ArraySet<Int> = ArraySet(),
val freeformTasksInZOrder: ArrayList<Int> = ArrayList(),
var fullImmersiveTaskId: Int? = null,
+ var topTransparentFullscreenTaskId: Int? = null,
) {
fun deepCopy(): DesktopTaskData =
DesktopTaskData(
@@ -73,6 +76,7 @@
closingTasks = ArraySet(closingTasks),
freeformTasksInZOrder = ArrayList(freeformTasksInZOrder),
fullImmersiveTaskId = fullImmersiveTaskId,
+ topTransparentFullscreenTaskId = topTransparentFullscreenTaskId,
)
fun clear() {
@@ -82,6 +86,7 @@
closingTasks.clear()
freeformTasksInZOrder.clear()
fullImmersiveTaskId = null
+ topTransparentFullscreenTaskId = null
}
}
@@ -322,13 +327,27 @@
fun getTaskInFullImmersiveState(displayId: Int): Int? =
desktopTaskDataByDisplayId.getOrCreate(displayId).fullImmersiveTaskId
+ /** Sets the top transparent fullscreen task id for a given display. */
+ fun setTopTransparentFullscreenTaskId(displayId: Int, taskId: Int) {
+ desktopTaskDataByDisplayId.getOrCreate(displayId).topTransparentFullscreenTaskId = taskId
+ }
+
+ /** Returns the top transparent fullscreen task id for a given display, or null. */
+ fun getTopTransparentFullscreenTaskId(displayId: Int): Int? =
+ desktopTaskDataByDisplayId.getOrCreate(displayId).topTransparentFullscreenTaskId
+
+ /** Clears the top transparent fullscreen task id info for a given display. */
+ fun clearTopTransparentFullscreenTaskId(displayId: Int) {
+ desktopTaskDataByDisplayId.getOrCreate(displayId).topTransparentFullscreenTaskId = null
+ }
+
private fun notifyVisibleTaskListeners(displayId: Int, visibleTasksCount: Int) {
visibleTasksListeners.forEach { (listener, executor) ->
executor.execute { listener.onTasksVisibilityChanged(displayId, visibleTasksCount) }
}
}
- /** Gets number of visible tasks on given [displayId] */
+ /** Gets number of visible freeform tasks on given [displayId] */
fun getVisibleTaskCount(displayId: Int): Int =
desktopTaskDataByDisplayId[displayId]?.visibleTasks?.size
?: 0.also { logD("getVisibleTaskCount=$it") }
@@ -526,6 +545,10 @@
)
pw.println("${innerPrefix}minimizedTasks=${data.minimizedTasks.toDumpString()}")
pw.println("${innerPrefix}fullImmersiveTaskId=${data.fullImmersiveTaskId}")
+ pw.println(
+ "${innerPrefix}topTransparentFullscreenTaskId=" +
+ "${data.topTransparentFullscreenTaskId}"
+ )
pw.println("${innerPrefix}wallpaperActivityToken=$wallpaperActivityToken")
}
}
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 d966aaa..a3d3a90 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -21,7 +21,6 @@
import android.app.ActivityOptions
import android.app.KeyguardManager
import android.app.PendingIntent
-import android.app.TaskInfo
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -84,6 +83,7 @@
import com.android.wm.shell.common.SingleInstanceRemoteListener
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing
+import com.android.wm.shell.compatui.isTransparentTask
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum
@@ -308,11 +308,23 @@
}
}
- /** Gets number of visible tasks in [displayId]. */
+ /** Gets number of visible freeform tasks in [displayId]. */
fun visibleTaskCount(displayId: Int): Int = taskRepository.getVisibleTaskCount(displayId)
- /** Returns true if any tasks are visible in Desktop Mode. */
- fun isDesktopModeShowing(displayId: Int): Boolean = visibleTaskCount(displayId) > 0
+ /**
+ * Returns true if any freeform tasks are visible or if a transparent fullscreen task exists on
+ * top in Desktop Mode.
+ */
+ fun isDesktopModeShowing(displayId: Int): Boolean {
+ if (
+ DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
+ .isTrue() && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
+ ) {
+ return visibleTaskCount(displayId) > 0 ||
+ taskRepository.getTopTransparentFullscreenTaskId(displayId) != null
+ }
+ return visibleTaskCount(displayId) > 0
+ }
/** Moves focused task to desktop mode for given [displayId]. */
fun moveFocusedTaskToDesktop(displayId: Int, transitionSource: DesktopModeTransitionSource) {
@@ -1596,7 +1608,7 @@
TransitionUtil.isOpeningType(request.type) &&
taskRepository.isActiveTask(triggerTask.taskId))
- private fun isIncompatibleTask(task: TaskInfo) =
+ private fun isIncompatibleTask(task: RunningTaskInfo) =
DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue() &&
isTopActivityExemptFromDesktopWindowing(context, task)
@@ -1852,6 +1864,15 @@
* fullscreen.
*/
private fun handleIncompatibleTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
+ logV("handleIncompatibleTaskLaunch")
+ if (!isDesktopModeShowing(task.displayId)) return null
+ // Only update task repository for transparent task.
+ if (
+ DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
+ .isTrue() && isTransparentTask(task)
+ ) {
+ taskRepository.setTopTransparentFullscreenTaskId(task.displayId, task.taskId)
+ }
// Already fullscreen, no-op.
if (task.isFullscreen) return null
return WindowContainerTransaction().also { wct -> addMoveToFullscreenChanges(wct, task) }
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 9625b71..5c79658 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
@@ -75,6 +75,12 @@
finishTransaction: SurfaceControl.Transaction,
) {
// TODO: b/332682201 Update repository state
+ if (
+ DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
+ .isTrue() && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
+ ) {
+ updateTopTransparentFullscreenTaskId(info)
+ }
updateWallpaperToken(info)
if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
handleBackNavigation(transition, info)
@@ -264,4 +270,22 @@
}
}
}
+
+ private fun updateTopTransparentFullscreenTaskId(info: TransitionInfo) {
+ info.changes.forEach { change ->
+ change.taskInfo?.let { task ->
+ val desktopRepository = desktopUserRepositories.getProfile(task.userId)
+ val displayId = task.displayId
+ // Clear `topTransparentFullscreenTask` information from repository if task
+ // is closed or sent to back.
+ if (
+ TransitionUtil.isClosingMode(change.mode) &&
+ task.taskId ==
+ desktopRepository.getTopTransparentFullscreenTaskId(displayId)
+ ) {
+ desktopRepository.clearTopTransparentFullscreenTaskId(displayId)
+ }
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
index 3ab7b34..7157a7f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
@@ -27,10 +27,9 @@
import org.junit.runner.RunWith
/**
- * Tests for {@link AppCompatUtils}.
+ * Tests for [@link AppCompatUtils].
*
- * Build/Install/Run:
- * atest WMShellUnitTests:AppCompatUtilsTest
+ * Build/Install/Run: atest WMShellUnitTests:AppCompatUtilsTest
*/
@RunWith(AndroidTestingRunner::class)
@SmallTest
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index e777ec7..5629127 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -56,6 +56,11 @@
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
+/**
+ * Tests for [@link DesktopRepository].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:DesktopRepositoryTest
+ */
@SmallTest
@RunWith(AndroidTestingRunner::class)
@ExperimentalCoroutinesApi
@@ -978,6 +983,22 @@
}
@Test
+ fun setTaskIdAsTopTransparentFullscreenTaskId_savesTaskId() {
+ repo.setTopTransparentFullscreenTaskId(displayId = DEFAULT_DISPLAY, taskId = 1)
+
+ assertThat(repo.getTopTransparentFullscreenTaskId(DEFAULT_DISPLAY)).isEqualTo(1)
+ }
+
+ @Test
+ fun clearTaskIdAsTopTransparentFullscreenTaskId_clearsTaskId() {
+ repo.setTopTransparentFullscreenTaskId(displayId = DEFAULT_DISPLAY, taskId = 1)
+
+ repo.clearTopTransparentFullscreenTaskId(DEFAULT_DISPLAY)
+
+ assertThat(repo.getTopTransparentFullscreenTaskId(DEFAULT_DISPLAY)).isNull()
+ }
+
+ @Test
fun setTaskInFullImmersiveState_savedAsInImmersiveState() {
assertThat(repo.isTaskInFullImmersiveState(taskId = 1)).isFalse()
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 0283c02..4f37180b 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
@@ -545,6 +545,18 @@
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+ )
+ fun isDesktopModeShowing_topTransparentFullscreenTask_returnsTrue() {
+ val topTransparentTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+ taskRepository.setTopTransparentFullscreenTaskId(DEFAULT_DISPLAY, topTransparentTask.taskId)
+
+ assertThat(controller.isDesktopModeShowing(displayId = DEFAULT_DISPLAY)).isTrue()
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_shouldNotShowWallpaper() {
val homeTask = setUpHomeTask(SECOND_DISPLAY)
@@ -2623,6 +2635,63 @@
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+ )
+ fun handleRequest_topActivityTransparentWithDisplay_savedToDesktopRepository() {
+ val freeformTask = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ markTaskVisible(freeformTask)
+
+ val transparentTask =
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY).apply {
+ isActivityStackTransparent = true
+ isTopActivityNoDisplay = false
+ numActivities = 1
+ }
+
+ controller.handleRequest(Binder(), createTransition(transparentTask))
+ assertThat(taskRepository.getTopTransparentFullscreenTaskId(DEFAULT_DISPLAY))
+ .isEqualTo(transparentTask.taskId)
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+ )
+ fun handleRequest_desktopNotShowing_topTransparentFullscreenTask_notSavedToDesktopRepository() {
+ val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+
+ controller.handleRequest(Binder(), createTransition(task))
+ assertThat(taskRepository.getTopTransparentFullscreenTaskId(DEFAULT_DISPLAY)).isNull()
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+ )
+ fun handleRequest_onlyTopTransparentFullscreenTask_returnSwitchToFreeformWCT() {
+ val topTransparentTask = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+ taskRepository.setTopTransparentFullscreenTaskId(DEFAULT_DISPLAY, topTransparentTask.taskId)
+
+ val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+
+ val result = controller.handleRequest(Binder(), createTransition(task))
+ assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun handleRequest_desktopNotShowing_topTransparentFullscreenTask_returnNull() {
+ val task = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+
+ assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
fun handleRequest_systemUIActivityWithDisplay_returnSwitchToFullscreenWCT() {
val freeformTask = setUpFreeformTask()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index c9623bc..c66d203 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -24,6 +24,7 @@
import android.content.Intent
import android.os.IBinder
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.view.Display.DEFAULT_DISPLAY
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_CLOSE
@@ -63,8 +64,15 @@
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
+/**
+ * Tests for [@link DesktopTasksTransitionObserver].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:DesktopTasksTransitionObserverTest
+ */
class DesktopTasksTransitionObserverTest {
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
@JvmField
@Rule
val extendedMockitoRule =
@@ -245,6 +253,48 @@
wct.assertRemoveAt(index = 0, wallpaperToken)
}
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+ )
+ fun topTransparentTaskClosed_clearTaskIdFromRepository() {
+ val mockTransition = Mockito.mock(IBinder::class.java)
+ val topTransparentTask = createTaskInfo(1)
+ whenever(taskRepository.getTopTransparentFullscreenTaskId(any()))
+ .thenReturn(topTransparentTask.taskId)
+
+ transitionObserver.onTransitionReady(
+ transition = mockTransition,
+ info = createCloseTransition(topTransparentTask),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ )
+
+ verify(taskRepository).clearTopTransparentFullscreenTaskId(topTransparentTask.displayId)
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY,
+ Flags.FLAG_INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC,
+ )
+ fun topTransparentTaskSentToBack_clearTaskIdFromRepository() {
+ val mockTransition = Mockito.mock(IBinder::class.java)
+ val topTransparentTask = createTaskInfo(1)
+ whenever(taskRepository.getTopTransparentFullscreenTaskId(any()))
+ .thenReturn(topTransparentTask.taskId)
+
+ transitionObserver.onTransitionReady(
+ transition = mockTransition,
+ info = createToBackTransition(topTransparentTask),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ )
+
+ verify(taskRepository).clearTopTransparentFullscreenTaskId(topTransparentTask.displayId)
+ }
+
private fun createBackNavigationTransition(
task: RunningTaskInfo?,
type: Int = TRANSIT_TO_BACK,
@@ -301,6 +351,19 @@
}
}
+ private fun createToBackTransition(task: RunningTaskInfo?): TransitionInfo {
+ return TransitionInfo(TRANSIT_TO_BACK, 0 /* flags */).apply {
+ addChange(
+ Change(mock(), mock()).apply {
+ mode = TRANSIT_TO_BACK
+ parent = null
+ taskInfo = task
+ flags = flags
+ }
+ )
+ }
+ }
+
private fun getLatestWct(
@WindowManager.TransitionType type: Int = TRANSIT_OPEN,
handlerClass: Class<out Transitions.TransitionHandler>? = null,
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 4f7132a..f7f10df 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -324,19 +324,6 @@
}
/**
- * Stops projection.
- * @hide
- */
- public void stop(@StopReason int stopReason) {
- try {
- Log.d(TAG, "Content Recording: stopping projection");
- mImpl.stop(stopReason);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to stop projection", e);
- }
- }
-
- /**
* Get the underlying IMediaProjection.
* @hide
*/
diff --git a/nfc-non-updatable/Android.bp b/nfc-non-updatable/Android.bp
new file mode 100644
index 0000000..ff987bb
--- /dev/null
+++ b/nfc-non-updatable/Android.bp
@@ -0,0 +1,23 @@
+package {
+ default_team: "trendy_team_fwk_nfc",
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "framework-nfc-non-updatable-sources",
+ path: "java",
+ srcs: [
+ "java/android/nfc/NfcServiceManager.java",
+ "java/android/nfc/cardemulation/ApduServiceInfo.aidl",
+ "java/android/nfc/cardemulation/ApduServiceInfo.java",
+ "java/android/nfc/cardemulation/NfcFServiceInfo.aidl",
+ "java/android/nfc/cardemulation/NfcFServiceInfo.java",
+ "java/android/nfc/cardemulation/AidGroup.aidl",
+ "java/android/nfc/cardemulation/AidGroup.java",
+ ],
+}
diff --git a/nfc-non-updatable/OWNERS b/nfc-non-updatable/OWNERS
new file mode 100644
index 0000000..f46dccd
--- /dev/null
+++ b/nfc-non-updatable/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 48448
+include platform/packages/apps/Nfc:/OWNERS
\ No newline at end of file
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc-non-updatable/flags/flags.aconfig
similarity index 100%
rename from nfc/java/android/nfc/flags.aconfig
rename to nfc-non-updatable/flags/flags.aconfig
diff --git a/nfc/java/android/nfc/NfcServiceManager.java b/nfc-non-updatable/java/android/nfc/NfcServiceManager.java
similarity index 100%
rename from nfc/java/android/nfc/NfcServiceManager.java
rename to nfc-non-updatable/java/android/nfc/NfcServiceManager.java
diff --git a/nfc/java/android/nfc/cardemulation/AidGroup.aidl b/nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.aidl
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/AidGroup.aidl
rename to nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.aidl
diff --git a/nfc/java/android/nfc/cardemulation/AidGroup.java b/nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.java
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/AidGroup.java
rename to nfc-non-updatable/java/android/nfc/cardemulation/AidGroup.java
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.aidl b/nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.aidl
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/ApduServiceInfo.aidl
rename to nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.aidl
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.java
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
rename to nfc-non-updatable/java/android/nfc/cardemulation/ApduServiceInfo.java
diff --git a/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.aidl b/nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.aidl
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/NfcFServiceInfo.aidl
rename to nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.aidl
diff --git a/nfc/java/android/nfc/cardemulation/NfcFServiceInfo.java b/nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.java
similarity index 100%
rename from nfc/java/android/nfc/cardemulation/NfcFServiceInfo.java
rename to nfc-non-updatable/java/android/nfc/cardemulation/NfcFServiceInfo.java
diff --git a/nfc/Android.bp b/nfc/Android.bp
index 9490487..0fdb3bd 100644
--- a/nfc/Android.bp
+++ b/nfc/Android.bp
@@ -9,20 +9,6 @@
}
filegroup {
- name: "framework-nfc-non-updatable-sources",
- path: "java",
- srcs: [
- "java/android/nfc/NfcServiceManager.java",
- "java/android/nfc/cardemulation/ApduServiceInfo.aidl",
- "java/android/nfc/cardemulation/ApduServiceInfo.java",
- "java/android/nfc/cardemulation/NfcFServiceInfo.aidl",
- "java/android/nfc/cardemulation/NfcFServiceInfo.java",
- "java/android/nfc/cardemulation/AidGroup.aidl",
- "java/android/nfc/cardemulation/AidGroup.java",
- ],
-}
-
-filegroup {
name: "framework-nfc-updatable-sources",
path: "java",
srcs: [
@@ -34,9 +20,6 @@
"//packages/apps/Nfc:__subpackages__",
"//packages/modules/Nfc:__subpackages__",
],
- exclude_srcs: [
- ":framework-nfc-non-updatable-sources",
- ],
}
java_sdk_library {
diff --git a/nfc/tests/Android.bp b/nfc/tests/Android.bp
index b6090e8..17fb810 100644
--- a/nfc/tests/Android.bp
+++ b/nfc/tests/Android.bp
@@ -29,7 +29,6 @@
"androidx.test.rules",
"androidx.test.runner",
"androidx.test.ext.junit",
- "framework-nfc.impl",
"mockito-target-extended-minus-junit4",
"frameworks-base-testutils",
"truth",
@@ -40,16 +39,25 @@
"testables",
],
libs: [
+ "androidx.annotation_annotation",
+ "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
+ "framework-permission-s.stubs.module_lib",
+ "framework-permission.stubs.module_lib",
"android.test.base.stubs.system",
"android.test.mock.stubs.system",
"android.test.runner.stubs.system",
+ "framework-nfc.impl",
],
jni_libs: [
// Required for ExtendedMockito
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
],
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ ":framework-nfc-updatable-sources",
+ ":framework-nfc-non-updatable-sources",
+ ],
platform_apis: true,
certificate: "platform",
test_suites: [
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
index 8b8ab58..31e1eb3 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
@@ -767,6 +767,70 @@
}
/**
+ * Indicates that the result of a mitigation executed during
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+ * {@link PackageHealthObserver#onExecuteBootLoopMitigation} is unknown.
+ */
+ public static final int MITIGATION_RESULT_UNKNOWN =
+ ObserverMitigationResult.MITIGATION_RESULT_UNKNOWN;
+
+ /**
+ * Indicates that a mitigation was successfully triggered or executed during
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+ * {@link PackageHealthObserver#onExecuteBootLoopMitigation}.
+ */
+ public static final int MITIGATION_RESULT_SUCCESS =
+ ObserverMitigationResult.MITIGATION_RESULT_SUCCESS;
+
+ /**
+ * Indicates that a mitigation executed during
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+ * {@link PackageHealthObserver#onExecuteBootLoopMitigation} was skipped.
+ */
+ public static final int MITIGATION_RESULT_SKIPPED =
+ ObserverMitigationResult.MITIGATION_RESULT_SKIPPED;
+
+ /**
+ * Indicates that a mitigation executed during
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+ * {@link PackageHealthObserver#onExecuteBootLoopMitigation} failed,
+ * but the failure is potentially retryable.
+ */
+ public static final int MITIGATION_RESULT_FAILURE_RETRYABLE =
+ ObserverMitigationResult.MITIGATION_RESULT_FAILURE_RETRYABLE;
+
+ /**
+ * Indicates that a mitigation executed during
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+ * {@link PackageHealthObserver#onExecuteBootLoopMitigation} failed,
+ * and the failure is not retryable.
+ */
+ public static final int MITIGATION_RESULT_FAILURE_NON_RETRYABLE =
+ ObserverMitigationResult.MITIGATION_RESULT_FAILURE_NON_RETRYABLE;
+
+ /**
+ * Possible return values of the for mitigations executed during
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} and
+ * {@link PackageHealthObserver#onExecuteBootLoopMitigation}.
+ * @hide
+ */
+ @Retention(SOURCE)
+ @IntDef(prefix = "MITIGATION_RESULT_", value = {
+ ObserverMitigationResult.MITIGATION_RESULT_UNKNOWN,
+ ObserverMitigationResult.MITIGATION_RESULT_SUCCESS,
+ ObserverMitigationResult.MITIGATION_RESULT_SKIPPED,
+ ObserverMitigationResult.MITIGATION_RESULT_FAILURE_RETRYABLE,
+ ObserverMitigationResult.MITIGATION_RESULT_FAILURE_NON_RETRYABLE,
+ })
+ public @interface ObserverMitigationResult {
+ int MITIGATION_RESULT_UNKNOWN = 0;
+ int MITIGATION_RESULT_SUCCESS = 1;
+ int MITIGATION_RESULT_SKIPPED = 2;
+ int MITIGATION_RESULT_FAILURE_RETRYABLE = 3;
+ int MITIGATION_RESULT_FAILURE_NON_RETRYABLE = 4;
+ }
+
+ /**
* The minimum value that can be returned by any observer.
* It represents that no mitigations were available.
*/
@@ -852,13 +916,20 @@
* health check.
*
* @param versionedPackage the package that is failing. This may be null if a native
- * service is crashing.
- * @param failureReason the type of failure that is occurring.
+ * service is crashing.
+ * @param failureReason the type of failure that is occurring.
* @param mitigationCount the number of times mitigation has been called for this package
- * (including this time).
- * @return {@code true} if action was executed successfully, {@code false} otherwise
+ * (including this time).
+ * @return {@link #MITIGATION_RESULT_SUCCESS} if the mitigation was successful,
+ * {@link #MITIGATION_RESULT_FAILURE_RETRYABLE} if the mitigation failed but can be
+ * retried,
+ * {@link #MITIGATION_RESULT_FAILURE_NON_RETRYABLE} if the mitigation failed and
+ * cannot be retried,
+ * {@link #MITIGATION_RESULT_UNKNOWN} if the result of the mitigation is unknown,
+ * or {@link #MITIGATION_RESULT_SKIPPED} if the mitigation was skipped.
*/
- boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage versionedPackage,
+ @ObserverMitigationResult int onExecuteHealthCheckMitigation(
+ @Nullable VersionedPackage versionedPackage,
@FailureReasons int failureReason, int mitigationCount);
@@ -885,10 +956,16 @@
* @param mitigationCount the number of times mitigation has been attempted for this
* boot loop (including this time).
*
- * @return {@code true} if action was executed successfully, {@code false} otherwise
+ * @return {@link #MITIGATION_RESULT_SUCCESS} if the mitigation was successful,
+ * {@link #MITIGATION_RESULT_FAILURE_RETRYABLE} if the mitigation failed but can be
+ * retried,
+ * {@link #MITIGATION_RESULT_FAILURE_NON_RETRYABLE} if the mitigation failed and
+ * cannot be retried,
+ * {@link #MITIGATION_RESULT_UNKNOWN} if the result of the mitigation is unknown,
+ * or {@link #MITIGATION_RESULT_SKIPPED} if the mitigation was skipped.
*/
- default boolean onExecuteBootLoopMitigation(int mitigationCount) {
- return false;
+ default @ObserverMitigationResult int onExecuteBootLoopMitigation(int mitigationCount) {
+ return ObserverMitigationResult.MITIGATION_RESULT_SKIPPED;
}
// TODO(b/120598832): Ensure uniqueness?
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
index bad6ab7..bb9e962 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SKIPPED;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent;
import android.annotation.IntDef;
@@ -728,10 +730,10 @@
}
@Override
- public boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
+ public int onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
@FailureReasons int failureReason, int mitigationCount) {
if (isDisabled()) {
- return false;
+ return MITIGATION_RESULT_SKIPPED;
}
Slog.i(TAG, "Executing remediation."
+ " failedPackage: "
@@ -753,9 +755,9 @@
}
executeRescueLevel(mContext,
failedPackage == null ? null : failedPackage.getPackageName(), level);
- return true;
+ return MITIGATION_RESULT_SUCCESS;
} else {
- return false;
+ return MITIGATION_RESULT_SKIPPED;
}
}
@@ -796,9 +798,9 @@
}
@Override
- public boolean onExecuteBootLoopMitigation(int mitigationCount) {
+ public int onExecuteBootLoopMitigation(int mitigationCount) {
if (isDisabled()) {
- return false;
+ return MITIGATION_RESULT_SKIPPED;
}
boolean mayPerformReboot = !shouldThrottleReboot();
final int level;
@@ -813,7 +815,7 @@
level = getRescueLevel(mitigationCount, mayPerformReboot);
}
executeRescueLevel(mContext, /*failedPackage=*/ null, level);
- return true;
+ return MITIGATION_RESULT_SUCCESS;
}
@Override
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index c80a1a4..c75f3aa 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -16,6 +16,8 @@
package com.android.server.rollback;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SKIPPED;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent;
import android.annotation.AnyThread;
@@ -172,7 +174,7 @@
}
@Override
- public boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
+ public int onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
@FailureReasons int rollbackReason, int mitigationCount) {
Slog.i(TAG, "Executing remediation."
+ " failedPackage: "
@@ -183,7 +185,7 @@
List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason));
- return true;
+ return MITIGATION_RESULT_SUCCESS;
}
List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel(
@@ -198,7 +200,7 @@
} else {
if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
mHandler.post(() -> rollbackAll(rollbackReason));
- return true;
+ return MITIGATION_RESULT_SUCCESS;
}
RollbackInfo rollback = getAvailableRollback(failedPackage);
@@ -210,7 +212,7 @@
}
// Assume rollbacks executed successfully
- return true;
+ return MITIGATION_RESULT_SUCCESS;
}
@Override
@@ -226,15 +228,15 @@
}
@Override
- public boolean onExecuteBootLoopMitigation(int mitigationCount) {
+ public int onExecuteBootLoopMitigation(int mitigationCount) {
if (Flags.recoverabilityDetection()) {
List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
triggerLeastImpactLevelRollback(availableRollbacks,
PackageWatchdog.FAILURE_REASON_BOOT_LOOP);
- return true;
+ return MITIGATION_RESULT_SUCCESS;
}
- return false;
+ return MITIGATION_RESULT_SKIPPED;
}
@Override
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
index 4fea937..ffae517 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
@@ -752,6 +752,70 @@
return mPackagesExemptFromImpactLevelThreshold;
}
+ /**
+ * Indicates that the result of a mitigation executed during
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+ * {@link PackageHealthObserver#onExecuteBootLoopMitigation} is unknown.
+ */
+ public static final int MITIGATION_RESULT_UNKNOWN =
+ ObserverMitigationResult.MITIGATION_RESULT_UNKNOWN;
+
+ /**
+ * Indicates that a mitigation was successfully triggered or executed during
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+ * {@link PackageHealthObserver#onExecuteBootLoopMitigation}.
+ */
+ public static final int MITIGATION_RESULT_SUCCESS =
+ ObserverMitigationResult.MITIGATION_RESULT_SUCCESS;
+
+ /**
+ * Indicates that a mitigation executed during
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+ * {@link PackageHealthObserver#onExecuteBootLoopMitigation} was skipped.
+ */
+ public static final int MITIGATION_RESULT_SKIPPED =
+ ObserverMitigationResult.MITIGATION_RESULT_SKIPPED;
+
+ /**
+ * Indicates that a mitigation executed during
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+ * {@link PackageHealthObserver#onExecuteBootLoopMitigation} failed,
+ * but the failure is potentially retryable.
+ */
+ public static final int MITIGATION_RESULT_FAILURE_RETRYABLE =
+ ObserverMitigationResult.MITIGATION_RESULT_FAILURE_RETRYABLE;
+
+ /**
+ * Indicates that a mitigation executed during
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} or
+ * {@link PackageHealthObserver#onExecuteBootLoopMitigation} failed,
+ * and the failure is not retryable.
+ */
+ public static final int MITIGATION_RESULT_FAILURE_NON_RETRYABLE =
+ ObserverMitigationResult.MITIGATION_RESULT_FAILURE_NON_RETRYABLE;
+
+ /**
+ * Possible return values of the for mitigations executed during
+ * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} and
+ * {@link PackageHealthObserver#onExecuteBootLoopMitigation}.
+ * @hide
+ */
+ @Retention(SOURCE)
+ @IntDef(prefix = "MITIGATION_RESULT_", value = {
+ ObserverMitigationResult.MITIGATION_RESULT_UNKNOWN,
+ ObserverMitigationResult.MITIGATION_RESULT_SUCCESS,
+ ObserverMitigationResult.MITIGATION_RESULT_SKIPPED,
+ ObserverMitigationResult.MITIGATION_RESULT_FAILURE_RETRYABLE,
+ ObserverMitigationResult.MITIGATION_RESULT_FAILURE_NON_RETRYABLE,
+ })
+ public @interface ObserverMitigationResult {
+ int MITIGATION_RESULT_UNKNOWN = 0;
+ int MITIGATION_RESULT_SUCCESS = 1;
+ int MITIGATION_RESULT_SKIPPED = 2;
+ int MITIGATION_RESULT_FAILURE_RETRYABLE = 3;
+ int MITIGATION_RESULT_FAILURE_NON_RETRYABLE = 4;
+ }
+
/** Possible severity values of the user impact of a
* {@link PackageHealthObserver#onExecuteHealthCheckMitigation}.
* @hide
@@ -809,16 +873,25 @@
int mitigationCount);
/**
- * Executes mitigation for {@link #onHealthCheckFailed}.
+ * This would be called after {@link #onHealthCheckFailed}.
+ * This is called only if current observer returned least impact mitigation for failed
+ * health check.
*
* @param versionedPackage the package that is failing. This may be null if a native
- * service is crashing.
- * @param failureReason the type of failure that is occurring.
+ * service is crashing.
+ * @param failureReason the type of failure that is occurring.
* @param mitigationCount the number of times mitigation has been called for this package
- * (including this time).
- * @return {@code true} if action was executed successfully, {@code false} otherwise
+ * (including this time).
+ * @return {@link #MITIGATION_RESULT_SUCCESS} if the mitigation was successful,
+ * {@link #MITIGATION_RESULT_FAILURE_RETRYABLE} if the mitigation failed but can be
+ * retried,
+ * {@link #MITIGATION_RESULT_FAILURE_NON_RETRYABLE} if the mitigation failed and
+ * cannot be retried,
+ * {@link #MITIGATION_RESULT_UNKNOWN} if the result of the mitigation is unknown,
+ * or {@link #MITIGATION_RESULT_SKIPPED} if the mitigation was skipped.
*/
- boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage versionedPackage,
+ @ObserverMitigationResult int onExecuteHealthCheckMitigation(
+ @Nullable VersionedPackage versionedPackage,
@FailureReasons int failureReason, int mitigationCount);
@@ -834,12 +907,23 @@
}
/**
- * Executes mitigation for {@link #onBootLoop}
+ * This would be called after {@link #onBootLoop}.
+ * This is called only if current observer returned least impact mitigation for fixing
+ * boot loop.
+ *
* @param mitigationCount the number of times mitigation has been attempted for this
* boot loop (including this time).
+ *
+ * @return {@link #MITIGATION_RESULT_SUCCESS} if the mitigation was successful,
+ * {@link #MITIGATION_RESULT_FAILURE_RETRYABLE} if the mitigation failed but can be
+ * retried,
+ * {@link #MITIGATION_RESULT_FAILURE_NON_RETRYABLE} if the mitigation failed and
+ * cannot be retried,
+ * {@link #MITIGATION_RESULT_UNKNOWN} if the result of the mitigation is unknown,
+ * or {@link #MITIGATION_RESULT_SKIPPED} if the mitigation was skipped.
*/
- default boolean onExecuteBootLoopMitigation(int mitigationCount) {
- return false;
+ default @ObserverMitigationResult int onExecuteBootLoopMitigation(int mitigationCount) {
+ return ObserverMitigationResult.MITIGATION_RESULT_SKIPPED;
}
// TODO(b/120598832): Ensure uniqueness?
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
index 2bb72fb..c6452d3 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java
@@ -18,6 +18,8 @@
import static android.provider.DeviceConfig.Properties;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SKIPPED;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent;
import android.annotation.IntDef;
@@ -859,10 +861,10 @@
}
@Override
- public boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
+ public int onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
@FailureReasons int failureReason, int mitigationCount) {
if (isDisabled()) {
- return false;
+ return MITIGATION_RESULT_SKIPPED;
}
Slog.i(TAG, "Executing remediation."
+ " failedPackage: "
@@ -884,9 +886,9 @@
}
executeRescueLevel(mContext,
failedPackage == null ? null : failedPackage.getPackageName(), level);
- return true;
+ return MITIGATION_RESULT_SUCCESS;
} else {
- return false;
+ return MITIGATION_RESULT_SKIPPED;
}
}
@@ -927,9 +929,9 @@
}
@Override
- public boolean onExecuteBootLoopMitigation(int mitigationCount) {
+ public int onExecuteBootLoopMitigation(int mitigationCount) {
if (isDisabled()) {
- return false;
+ return MITIGATION_RESULT_SKIPPED;
}
boolean mayPerformReboot = !shouldThrottleReboot();
final int level;
@@ -944,7 +946,7 @@
level = getRescueLevel(mitigationCount, mayPerformReboot);
}
executeRescueLevel(mContext, /*failedPackage=*/ null, level);
- return true;
+ return MITIGATION_RESULT_SUCCESS;
}
@Override
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 0692cdb..0411537 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -18,6 +18,8 @@
import static android.content.pm.Flags.provideInfoOfApkInApex;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SKIPPED;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent;
import android.annotation.AnyThread;
@@ -175,7 +177,7 @@
}
@Override
- public boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
+ public int onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage,
@FailureReasons int rollbackReason, int mitigationCount) {
Slog.i(TAG, "Executing remediation."
+ " failedPackage: "
@@ -186,7 +188,7 @@
List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason));
- return true;
+ return MITIGATION_RESULT_SUCCESS;
}
List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel(
@@ -201,7 +203,7 @@
} else {
if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
mHandler.post(() -> rollbackAll(rollbackReason));
- return true;
+ return MITIGATION_RESULT_SUCCESS;
}
RollbackInfo rollback = getAvailableRollback(failedPackage);
@@ -213,7 +215,7 @@
}
// Assume rollbacks executed successfully
- return true;
+ return MITIGATION_RESULT_SUCCESS;
}
@Override
@@ -229,15 +231,15 @@
}
@Override
- public boolean onExecuteBootLoopMitigation(int mitigationCount) {
+ public int onExecuteBootLoopMitigation(int mitigationCount) {
if (Flags.recoverabilityDetection()) {
List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
triggerLeastImpactLevelRollback(availableRollbacks,
PackageWatchdog.FAILURE_REASON_BOOT_LOOP);
- return true;
+ return MITIGATION_RESULT_SUCCESS;
}
- return false;
+ return MITIGATION_RESULT_SKIPPED;
}
@Override
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index f380e7f..81358ca 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -76,7 +76,6 @@
tools: ["soong_zip"],
cmd: "mkdir -p $(genDir)/META-INF/services/ && touch $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider &&" +
"echo -e 'org.robolectric.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
- "echo -e 'org.robolectric.shadows.multidex.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
"echo -e 'org.robolectric.shadows.httpclient.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
//"echo -e 'com.android.settings.testutils.shadow.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
"echo -e 'com.android.settingslib.testutils.shadow.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 74c8710..1b1c91d 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -496,14 +496,14 @@
}
flag {
- name: "status_bar_notification_chips_test"
+ name: "promote_notifications_automatically"
namespace: "systemui"
- description: "Flag to enable certain features that let us test the status bar notification "
- "chips with teamfooders. This flag should *never* be released to trunkfood or nextfood."
- bug: "361346412"
+ description: "Flag to automatically turn certain notifications into promoted notifications so "
+ " we can test promoted notifications with teamfooders. This flag should *never* be released "
+ "to trunkfood or nextfood."
+ bug: "367705002"
}
-
flag {
name: "compose_bouncer"
namespace: "systemui"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
index ead151e..b069855 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
@@ -106,7 +106,7 @@
repository.setKeyguardEnabled(true)
runCurrent()
- assertEquals(listOf(false, true, false), canWake)
+ assertEquals(listOf(false, true), canWake)
}
@Test
@@ -374,6 +374,8 @@
// Should be canceled by the wakeup, but there would still be an
// alarm in flight that should be canceled.
false,
+ // True once we're actually GONE.
+ true,
),
canWake,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index df4d5ab..ea2b3cd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -206,6 +206,9 @@
@Test
@RequiresFlagsDisabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
fun setSurfaceBehindVisibility_falseSetsLockscreenVisibility_without_keyguard_shell_transitions() {
+ // Show the surface behind, then hide it.
+ underTest.setLockscreenShown(true)
+ underTest.setSurfaceBehindVisibility(true)
underTest.setSurfaceBehindVisibility(false)
verify(activityTaskManagerService).setLockScreenShown(eq(true), any())
}
@@ -213,6 +216,9 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING)
fun setSurfaceBehindVisibility_falseSetsLockscreenVisibility_with_keyguard_shell_transitions() {
+ // Show the surface behind, then hide it.
+ underTest.setLockscreenShown(true)
+ underTest.setSurfaceBehindVisibility(true)
underTest.setSurfaceBehindVisibility(false)
verify(keyguardTransitions).startKeyguardTransition(eq(true), any())
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
index 6ec38ba..77be8c7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepositoryTest.kt
@@ -16,9 +16,8 @@
package com.android.systemui.mediarouter.data.repository
-import android.media.projection.StopReason
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
@@ -102,7 +101,7 @@
origin = CastDevice.CastOrigin.MediaRouter,
)
- underTest.stopCasting(device, StopReason.STOP_UNKNOWN)
+ underTest.stopCasting(device)
assertThat(castController.lastStoppedDevice).isEqualTo(device)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index 165ff7b..090e2e9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.dump.nano.SystemUIProtoDump
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTile.BooleanState
+import com.android.systemui.plugins.qs.TileDetailsViewModel
import com.android.systemui.qs.FakeQSFactory
import com.android.systemui.qs.FakeQSTile
import com.android.systemui.qs.external.CustomTile
@@ -154,7 +155,7 @@
TileSpec.create("e"),
CUSTOM_TILE_SPEC,
TileSpec.create("d"),
- TileSpec.create("non_existent")
+ TileSpec.create("non_existent"),
)
tileSpecRepository.setTiles(USER_INFO_0.id, specs)
@@ -190,11 +191,7 @@
@Test
fun logTileCreated() =
testScope.runTest(USER_INFO_0) {
- val specs =
- listOf(
- TileSpec.create("a"),
- CUSTOM_TILE_SPEC,
- )
+ val specs = listOf(TileSpec.create("a"), CUSTOM_TILE_SPEC)
tileSpecRepository.setTiles(USER_INFO_0.id, specs)
runCurrent()
@@ -204,10 +201,7 @@
@Test
fun logTileNotFoundInFactory() =
testScope.runTest(USER_INFO_0) {
- val specs =
- listOf(
- TileSpec.create("non_existing"),
- )
+ val specs = listOf(TileSpec.create("non_existing"))
tileSpecRepository.setTiles(USER_INFO_0.id, specs)
runCurrent()
@@ -218,10 +212,7 @@
@Test
fun tileNotAvailableDestroyed_logged() =
testScope.runTest(USER_INFO_0) {
- val specs =
- listOf(
- TileSpec.create("e"),
- )
+ val specs = listOf(TileSpec.create("e"))
tileSpecRepository.setTiles(USER_INFO_0.id, specs)
runCurrent()
@@ -229,7 +220,7 @@
verify(logger)
.logTileDestroyed(
specs[0],
- QSPipelineLogger.TileDestroyedReason.NEW_TILE_NOT_AVAILABLE
+ QSPipelineLogger.TileDestroyedReason.NEW_TILE_NOT_AVAILABLE,
)
}
@@ -238,11 +229,7 @@
testScope.runTest(USER_INFO_0) {
val tiles by collectLastValue(tileSpecRepository.tilesSpecs(USER_INFO_0.id))
- val specs =
- listOf(
- TileSpec.create("a"),
- TileSpec.create("e"),
- )
+ val specs = listOf(TileSpec.create("a"), TileSpec.create("e"))
tileSpecRepository.setTiles(USER_INFO_0.id, specs)
assertThat(tiles).isEqualTo(listOf(TileSpec.create("a")))
@@ -266,10 +253,7 @@
testScope.runTest(USER_INFO_0) {
val tiles by collectLastValue(underTest.currentTiles)
- val specs =
- listOf(
- TileSpec.create("a"),
- )
+ val specs = listOf(TileSpec.create("a"))
tileSpecRepository.setTiles(USER_INFO_0.id, specs)
val originalTileA = tiles!![0].tile
@@ -299,7 +283,7 @@
verify(logger)
.logTileDestroyed(
TileSpec.create("c"),
- QSPipelineLogger.TileDestroyedReason.TILE_REMOVED
+ QSPipelineLogger.TileDestroyedReason.TILE_REMOVED,
)
}
@@ -325,7 +309,7 @@
verify(logger)
.logTileDestroyed(
TileSpec.create("a"),
- QSPipelineLogger.TileDestroyedReason.EXISTING_TILE_NOT_AVAILABLE
+ QSPipelineLogger.TileDestroyedReason.EXISTING_TILE_NOT_AVAILABLE,
)
assertThat(tiles?.size).isEqualTo(1)
@@ -370,7 +354,7 @@
verify(logger)
.logTileDestroyed(
specs0[0],
- QSPipelineLogger.TileDestroyedReason.TILE_NOT_PRESENT_IN_NEW_USER
+ QSPipelineLogger.TileDestroyedReason.TILE_NOT_PRESENT_IN_NEW_USER,
)
}
@@ -418,21 +402,12 @@
testScope.runTest(USER_INFO_0) {
val tiles by collectLastValue(tileSpecRepository.tilesSpecs(USER_INFO_0.id))
val spec = TileSpec.create("a")
- val currentSpecs =
- listOf(
- TileSpec.create("b"),
- TileSpec.create("c"),
- )
+ val currentSpecs = listOf(TileSpec.create("b"), TileSpec.create("c"))
tileSpecRepository.setTiles(USER_INFO_0.id, currentSpecs)
underTest.addTile(spec, position = 1)
- val expectedSpecs =
- listOf(
- TileSpec.create("b"),
- spec,
- TileSpec.create("c"),
- )
+ val expectedSpecs = listOf(TileSpec.create("b"), spec, TileSpec.create("c"))
assertThat(tiles).isEqualTo(expectedSpecs)
}
@@ -442,11 +417,7 @@
val tiles0 by collectLastValue(tileSpecRepository.tilesSpecs(USER_INFO_0.id))
val tiles1 by collectLastValue(tileSpecRepository.tilesSpecs(USER_INFO_1.id))
val spec = TileSpec.create("a")
- val currentSpecs =
- listOf(
- TileSpec.create("b"),
- TileSpec.create("c"),
- )
+ val currentSpecs = listOf(TileSpec.create("b"), TileSpec.create("c"))
tileSpecRepository.setTiles(USER_INFO_0.id, currentSpecs)
tileSpecRepository.setTiles(USER_INFO_1.id, currentSpecs)
@@ -455,12 +426,7 @@
assertThat(tiles0).isEqualTo(currentSpecs)
- val expectedSpecs =
- listOf(
- TileSpec.create("b"),
- spec,
- TileSpec.create("c"),
- )
+ val expectedSpecs = listOf(TileSpec.create("b"), spec, TileSpec.create("c"))
assertThat(tiles1).isEqualTo(expectedSpecs)
}
@@ -515,11 +481,7 @@
val tiles0 by collectLastValue(tileSpecRepository.tilesSpecs(USER_INFO_0.id))
val tiles1 by collectLastValue(tileSpecRepository.tilesSpecs(USER_INFO_1.id))
val currentSpecs =
- listOf(
- TileSpec.create("a"),
- TileSpec.create("b"),
- TileSpec.create("c"),
- )
+ listOf(TileSpec.create("a"), TileSpec.create("b"), TileSpec.create("c"))
tileSpecRepository.setTiles(USER_INFO_0.id, currentSpecs)
tileSpecRepository.setTiles(USER_INFO_1.id, currentSpecs)
@@ -557,11 +519,7 @@
tileSpecRepository.setTiles(USER_INFO_0.id, currentSpecs)
runCurrent()
- val newSpecs =
- listOf(
- otherCustomTileSpec,
- TileSpec.create("a"),
- )
+ val newSpecs = listOf(otherCustomTileSpec, TileSpec.create("a"))
underTest.setTiles(newSpecs)
runCurrent()
@@ -615,7 +573,7 @@
tileSpecRepository.setTiles(
USER_INFO_0.id,
- listOf(TileSpec.create("a"), CUSTOM_TILE_SPEC)
+ listOf(TileSpec.create("a"), CUSTOM_TILE_SPEC),
)
val newTileA = tiles!![0].tile
assertThat(tileA).isSameInstanceAs(newTileA)
@@ -650,7 +608,7 @@
installedTilesPackageRepository.setInstalledPackagesForUser(
USER_INFO_0.id,
- setOf(TEST_COMPONENT)
+ setOf(TEST_COMPONENT),
)
assertThat(tiles!!.size).isEqualTo(3)
@@ -676,10 +634,7 @@
@Test
fun changeInPackagesTiles_doesntTriggerUserChange_logged() =
testScope.runTest(USER_INFO_0) {
- val specs =
- listOf(
- TileSpec.create("a"),
- )
+ val specs = listOf(TileSpec.create("a"))
tileSpecRepository.setTiles(USER_INFO_0.id, specs)
runCurrent()
// Settled on the same list of tiles.
@@ -691,7 +646,6 @@
verify(logger, never()).logTileUserChanged(TileSpec.create("a"), 0)
}
-
@Test
fun getTileDetails() =
testScope.runTest(USER_INFO_0) {
@@ -711,11 +665,19 @@
assertThat(tiles!![2].spec).isEqualTo(tileNoDetails)
(tiles!![2].tile as FakeQSTile).hasDetailsViewModel = false
- assertThat(tiles!![0].tile.detailsViewModel.getTitle()).isEqualTo("a")
- assertThat(tiles!![1].tile.detailsViewModel.getTitle()).isEqualTo("b")
- assertThat(tiles!![2].tile.detailsViewModel).isNull()
- }
+ var currentModel: TileDetailsViewModel? = null
+ val setCurrentModel = { model: TileDetailsViewModel? -> currentModel = model }
+ tiles!![0].tile.getDetailsViewModel(setCurrentModel)
+ assertThat(currentModel?.getTitle()).isEqualTo("a")
+ currentModel = null
+ tiles!![1].tile.getDetailsViewModel(setCurrentModel)
+ assertThat(currentModel?.getTitle()).isEqualTo("b")
+
+ currentModel = null
+ tiles!![2].tile.getDetailsViewModel(setCurrentModel)
+ assertThat(currentModel).isNull()
+ }
private fun QSTile.State.fillIn(state: Int, label: CharSequence, secondaryLabel: CharSequence) {
this.state = state
@@ -745,7 +707,7 @@
customTileAddedRepository.setTileAdded(
CUSTOM_TILE_SPEC.componentName,
currentUser,
- true
+ true,
)
}
in VALID_TILES -> FakeQSTile(currentUser, available = spec !in unavailableTiles)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 31a627f..9f12b18 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -20,7 +20,6 @@
import static junit.framework.TestCase.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
@@ -31,7 +30,6 @@
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
import android.media.projection.MediaProjectionInfo;
-import android.media.projection.StopReason;
import android.os.Handler;
import android.service.quicksettings.Tile;
import android.testing.TestableLooper;
@@ -338,8 +336,7 @@
mCastTile.handleClick(null /* view */);
mTestableLooper.processAllMessages();
- verify(mController, times(1))
- .stopCasting(same(device), eq(StopReason.STOP_QS_TILE));
+ verify(mController, times(1)).stopCasting(same(device));
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index fc1d73b..0fd7c98 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -27,13 +27,11 @@
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Dialog;
-import android.media.projection.StopReason;
import android.os.Handler;
import android.platform.test.flag.junit.FlagsParameterization;
import android.service.quicksettings.Tile;
@@ -236,7 +234,7 @@
mTile.handleClick(null /* view */);
- verify(mController, times(1)).stopRecording(eq(StopReason.STOP_QS_TILE));
+ verify(mController, times(1)).stopRecording();
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
index 778c73f..0b56d7b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor
import android.app.Dialog
-import android.media.projection.StopReason
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -93,7 +92,7 @@
underTest.handleInput(QSTileInputTestKtx.click(recordingModel))
- verify(recordingController).stopRecording(eq(StopReason.STOP_QS_TILE))
+ verify(recordingController).stopRecording()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 50fa9d2..a6a1d4a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -41,7 +41,6 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Intent;
-import android.media.projection.StopReason;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -200,16 +199,16 @@
public void testOnSystemRequestedStop_recordingInProgress_endsRecording() throws IOException {
doReturn(true).when(mController).isRecording();
- mRecordingService.onStopped(StopReason.STOP_UNKNOWN);
+ mRecordingService.onStopped();
- verify(mScreenMediaRecorder).end(eq(StopReason.STOP_UNKNOWN));
+ verify(mScreenMediaRecorder).end();
}
@Test
public void testOnSystemRequestedStop_recordingInProgress_updatesState() {
doReturn(true).when(mController).isRecording();
- mRecordingService.onStopped(StopReason.STOP_UNKNOWN);
+ mRecordingService.onStopped();
assertUpdateState(false);
}
@@ -219,18 +218,18 @@
throws IOException {
doReturn(false).when(mController).isRecording();
- mRecordingService.onStopped(StopReason.STOP_UNKNOWN);
+ mRecordingService.onStopped();
- verify(mScreenMediaRecorder, never()).end(StopReason.STOP_UNKNOWN);
+ verify(mScreenMediaRecorder, never()).end();
}
@Test
public void testOnSystemRequestedStop_recorderEndThrowsRuntimeException_releasesRecording()
throws IOException {
doReturn(true).when(mController).isRecording();
- doThrow(new RuntimeException()).when(mScreenMediaRecorder).end(StopReason.STOP_UNKNOWN);
+ doThrow(new RuntimeException()).when(mScreenMediaRecorder).end();
- mRecordingService.onStopped(StopReason.STOP_UNKNOWN);
+ mRecordingService.onStopped();
verify(mScreenMediaRecorder).release();
}
@@ -239,7 +238,7 @@
public void testOnSystemRequestedStop_whenRecordingInProgress_showsNotifications() {
doReturn(true).when(mController).isRecording();
- mRecordingService.onStopped(StopReason.STOP_UNKNOWN);
+ mRecordingService.onStopped();
// Processing notification
ArgumentCaptor<Notification> notifCaptor = ArgumentCaptor.forClass(Notification.class);
@@ -272,9 +271,9 @@
public void testOnSystemRequestedStop_recorderEndThrowsRuntimeException_showsErrorNotification()
throws IOException {
doReturn(true).when(mController).isRecording();
- doThrow(new RuntimeException()).when(mScreenMediaRecorder).end(anyInt());
+ doThrow(new RuntimeException()).when(mScreenMediaRecorder).end();
- mRecordingService.onStopped(StopReason.STOP_UNKNOWN);
+ mRecordingService.onStopped();
verify(mRecordingService).createErrorSavingNotification(any());
ArgumentCaptor<Notification> notifCaptor = ArgumentCaptor.forClass(Notification.class);
@@ -290,9 +289,9 @@
public void testOnSystemRequestedStop_recorderEndThrowsOOMError_releasesRecording()
throws IOException {
doReturn(true).when(mController).isRecording();
- doThrow(new OutOfMemoryError()).when(mScreenMediaRecorder).end(anyInt());
+ doThrow(new OutOfMemoryError()).when(mScreenMediaRecorder).end();
- assertThrows(Throwable.class, () -> mRecordingService.onStopped(StopReason.STOP_UNKNOWN));
+ assertThrows(Throwable.class, () -> mRecordingService.onStopped());
verify(mScreenMediaRecorder).release();
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
index ade5941..aceea90 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.screenrecord.data.repository
-import android.media.projection.StopReason
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -32,7 +31,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.argumentCaptor
-import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@@ -128,8 +126,8 @@
@Test
fun stopRecording_invokesController() =
testScope.runTest {
- underTest.stopRecording(StopReason.STOP_PRIVACY_CHIP)
+ underTest.stopRecording()
- verify(recordingController).stopRecording(eq(StopReason.STOP_PRIVACY_CHIP))
+ verify(recordingController).stopRecording()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt
index 533665e..8972f3e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt
@@ -83,13 +83,40 @@
}
@Test
- fun doesntTriggerGestureFinished_onTwoFingersSwipe() {
- assertStateAfterEvents(events = TwoFingerGesture.swipeRight(), expectedState = NotStarted)
+ fun triggersGestureError_onTwoFingersSwipe() {
+ assertStateAfterEvents(events = TwoFingerGesture.swipeRight(), expectedState = Error)
}
@Test
- fun doesntTriggerGestureFinished_onFourFingersSwipe() {
- assertStateAfterEvents(events = FourFingerGesture.swipeRight(), expectedState = NotStarted)
+ fun doesntTriggerGestureError_TwoFingerSwipeInProgress() {
+ assertStateAfterEvents(
+ events = TwoFingerGesture.eventsForGestureInProgress { move(deltaX = SWIPE_DISTANCE) },
+ expectedState = NotStarted,
+ )
+ }
+
+ @Test
+ fun triggersGestureError_onFourFingersSwipe() {
+ assertStateAfterEvents(events = FourFingerGesture.swipeRight(), expectedState = Error)
+ }
+
+ @Test
+ fun doesntTriggerGestureError_FourFingerSwipeInProgress() {
+ assertStateAfterEvents(
+ events = FourFingerGesture.eventsForGestureInProgress { move(deltaX = SWIPE_DISTANCE) },
+ expectedState = NotStarted,
+ )
+ }
+
+ @Test
+ fun ignoresOneFingerSwipes() {
+ val oneFingerSwipe =
+ listOf(
+ touchpadEvent(MotionEvent.ACTION_DOWN, 50f, 50f),
+ touchpadEvent(MotionEvent.ACTION_MOVE, 55f, 55f),
+ touchpadEvent(MotionEvent.ACTION_UP, 60f, 60f),
+ )
+ assertStateAfterEvents(events = oneFingerSwipe, expectedState = NotStarted)
}
private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 322da32..56176cf 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -33,6 +33,7 @@
import com.android.systemui.plugins.qs.QSTile.State;
import java.util.Objects;
+import java.util.function.Consumer;
import java.util.function.Supplier;
@ProvidesInterface(version = QSTile.VERSION)
@@ -122,9 +123,32 @@
boolean isListening();
/**
- * Return this tile's {@link TileDetailsViewModel} to be used to render the TileDetailsView.
+ * Get this tile's {@link TileDetailsViewModel} through a callback.
+ *
+ * Please only override this method if the tile can't get its {@link TileDetailsViewModel}
+ * synchronously and thus need a callback to defer it.
+ *
+ * @return a boolean indicating whether this tile has a {@link TileDetailsViewModel}. The tile's
+ * {@link TileDetailsViewModel} will be passed to the callback. Please always return true when
+ * overriding this method. Return false will make the tile display its dialog instead of details
+ * view, and it will not wait for the callback to be returned before proceeding to show the
+ * dialog.
*/
- default TileDetailsViewModel getDetailsViewModel() { return null; }
+ default boolean getDetailsViewModel(Consumer<TileDetailsViewModel> callback) {
+ TileDetailsViewModel tileDetailsViewModel = getDetailsViewModel();
+ callback.accept(tileDetailsViewModel);
+ return tileDetailsViewModel != null;
+ }
+
+ /**
+ * Return this tile's {@link TileDetailsViewModel} to be used to render the TileDetailsView.
+ *
+ * Please only override this method if the tile doesn't need a callback to set its
+ * {@link TileDetailsViewModel}.
+ */
+ default TileDetailsViewModel getDetailsViewModel() {
+ return null;
+ }
@ProvidesInterface(version = Callback.VERSION)
interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsViewModel.kt
new file mode 100644
index 0000000..9dd3b6d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsViewModel.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.bluetooth.qsdialog
+
+import android.view.LayoutInflater
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.viewinterop.AndroidView
+import com.android.systemui.plugins.qs.TileDetailsViewModel
+import com.android.systemui.res.R
+
+class BluetoothDetailsViewModel(onLongClick: () -> Unit) : TileDetailsViewModel() {
+ private val _onLongClick = onLongClick
+
+ @Composable
+ override fun GetContentView() {
+ AndroidView(
+ modifier = Modifier.fillMaxWidth().fillMaxHeight(),
+ factory = { context ->
+ // Inflate with the existing dialog xml layout
+ LayoutInflater.from(context).inflate(R.layout.bluetooth_tile_dialog, null)
+ // TODO: b/378513956 - Implement the bluetooth details view
+ },
+ )
+ }
+
+ override fun clickOnSettingsButton() {
+ _onLongClick()
+ }
+
+ override fun getTitle(): String {
+ // TODO: b/378513956 Update the placeholder text
+ return "Bluetooth"
+ }
+
+ override fun getSubTitle(): String {
+ // TODO: b/378513956 Update the placeholder text
+ return "Tap to connect or disconnect a device"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index a137d6c..5b28a3f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -131,8 +131,18 @@
Log.d(TAG, "ActivityTaskManagerService#keyguardGoingAway()")
activityTaskManagerService.keyguardGoingAway(0)
isKeyguardGoingAway = true
- } else {
- // Hide the surface by setting the lockscreen showing.
+ } else if (isLockscreenShowing == true) {
+ // Re-show the lockscreen if the surface was visible and we want to make it invisible,
+ // and the lockscreen is currently showing (this is the usual case of the going away
+ // animation). Re-showing the lockscreen will cancel the going away animation. If we
+ // want to hide the surface, but the lockscreen is not currently showing, do nothing and
+ // wait for lockscreenVisibility to emit if it's appropriate to show the lockscreen (it
+ // might be disabled/suppressed).
+ Log.d(
+ TAG,
+ "setLockscreenShown(true) because we're setting the surface invisible " +
+ "and lockscreen is already showing.",
+ )
setLockscreenShown(true)
}
}
@@ -153,6 +163,10 @@
nonApps: Array<RemoteAnimationTarget>,
finishedCallback: IRemoteAnimationFinishedCallback,
) {
+ // Make sure this is true - we set it true when requesting keyguardGoingAway, but there are
+ // cases where WM starts this transition on its own.
+ isKeyguardGoingAway = true
+
// 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
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
index 8641dfa..a133f06 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
@@ -90,6 +90,7 @@
private val selectedUserInteractor: SelectedUserInteractor,
keyguardEnabledInteractor: KeyguardEnabledInteractor,
keyguardServiceLockNowInteractor: KeyguardServiceLockNowInteractor,
+ keyguardInteractor: KeyguardInteractor,
) {
/**
@@ -106,13 +107,18 @@
.onStart { emit(false) }
/**
- * Whether we can wake from AOD/DOZING directly to GONE, bypassing LOCKSCREEN/BOUNCER states.
+ * Whether we can wake from AOD/DOZING or DREAMING directly to GONE, bypassing
+ * LOCKSCREEN/BOUNCER states.
*
* This is possible in the following cases:
* - Keyguard is disabled, either from an app request or from security being set to "None".
* - Keyguard is suppressed, via adb locksettings.
* - We're wake and unlocking (fingerprint auth occurred while asleep).
* - We're allowed to ignore auth and return to GONE, due to timeouts not elapsing.
+ * - We're DREAMING and dismissible.
+ * - We're already GONE. Technically you're already awake when GONE, but this makes it easier to
+ * reason about this state (for example, if canWakeDirectlyToGone, don't tell WM to pause the
+ * top activity - something you should never do while GONE as well).
*/
val canWakeDirectlyToGone =
combine(
@@ -120,14 +126,19 @@
shouldSuppressKeyguard,
repository.biometricUnlockState,
repository.canIgnoreAuthAndReturnToGone,
+ transitionInteractor.currentKeyguardState,
) {
keyguardEnabled,
shouldSuppressKeyguard,
biometricUnlockState,
- canIgnoreAuthAndReturnToGone ->
+ canIgnoreAuthAndReturnToGone,
+ currentState ->
(!keyguardEnabled || shouldSuppressKeyguard) ||
BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode) ||
- canIgnoreAuthAndReturnToGone
+ canIgnoreAuthAndReturnToGone ||
+ (currentState == KeyguardState.DREAMING &&
+ keyguardInteractor.isKeyguardDismissible.value) ||
+ currentState == KeyguardState.GONE
}
.distinctUntilChanged()
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 3b99bb4..184f302 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
@@ -34,7 +34,7 @@
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
-import com.android.systemui.util.kotlin.Utils.Companion.toTriple
+import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.sample
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import dagger.Lazy
@@ -240,10 +240,11 @@
combine(
transitionInteractor.currentKeyguardState,
wakeToGoneInteractor.canWakeDirectlyToGone,
- ::Pair,
+ surfaceBehindVisibility,
+ ::Triple,
)
- .sample(transitionInteractor.startedStepWithPrecedingStep, ::toTriple)
- .map { (currentState, canWakeDirectlyToGone, startedWithPrev) ->
+ .sample(transitionInteractor.startedStepWithPrecedingStep, ::toQuad)
+ .map { (currentState, canWakeDirectlyToGone, surfaceBehindVis, startedWithPrev) ->
val startedFromStep = startedWithPrev.previousValue
val startedStep = startedWithPrev.newValue
val returningToGoneAfterCancellation =
@@ -296,6 +297,11 @@
// we should simply tell WM that the lockscreen is no longer visible, and
// *not* play the going away animation or related animations.
false
+ } else if (!surfaceBehindVis) {
+ // If the surface behind is not visible, then the lockscreen has to be visible
+ // since there's nothing to show. The surface behind will never be invisible if
+ // the lockscreen is disabled or suppressed.
+ true
} else {
currentState != KeyguardState.GONE
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepository.kt b/packages/SystemUI/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepository.kt
index a19c9b3..debb667 100644
--- a/packages/SystemUI/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediarouter/data/repository/MediaRouterRepository.kt
@@ -16,7 +16,6 @@
package com.android.systemui.mediarouter.data.repository
-import android.media.projection.StopReason
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
@@ -41,7 +40,7 @@
val castDevices: StateFlow<List<CastDevice>>
/** Stops the cast to the given device. */
- fun stopCasting(device: CastDevice, @StopReason stopReason: Int)
+ fun stopCasting(device: CastDevice)
}
@SysUISingleton
@@ -68,8 +67,8 @@
.map { it.filter { device -> device.origin == CastDevice.CastOrigin.MediaRouter } }
.stateIn(scope, SharingStarted.WhileSubscribed(), emptyList())
- override fun stopCasting(device: CastDevice, @StopReason stopReason: Int) {
- castController.stopCasting(device, stopReason)
+ override fun stopCasting(device: CastDevice) {
+ castController.stopCasting(device)
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt
index d8361f5..03f0297 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt
@@ -23,11 +23,9 @@
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
@SysUISingleton
-class DetailsViewModel
-@Inject constructor(val currentTilesInteractor: CurrentTilesInteractor) {
+class DetailsViewModel @Inject constructor(val currentTilesInteractor: CurrentTilesInteractor) {
/**
* The current active [TileDetailsViewModel]. If it's `null`, it means the qs overlay is not
@@ -38,6 +36,7 @@
/**
* Update the active [TileDetailsViewModel] to `null`.
+ *
* @see activeTileDetails
*/
fun closeDetailedView() {
@@ -45,8 +44,9 @@
}
/**
- * Update the active [TileDetailsViewModel] to the `spec`'s corresponding view model.
- * Return if the [TileDetailsViewModel] is successfully found.
+ * Update the active [TileDetailsViewModel] to the `spec`'s corresponding view model. Return if
+ * the [TileDetailsViewModel] is successfully handled.
+ *
* @see activeTileDetails
*/
fun onTileClicked(spec: TileSpec?): Boolean {
@@ -55,11 +55,11 @@
return false
}
- _activeTileDetails.value = currentTilesInteractor
- .currentQSTiles
- .firstOrNull { it.tileSpec == spec.spec }
- ?.detailsViewModel
+ val currentTile =
+ currentTilesInteractor.currentQSTiles.firstOrNull { it.tileSpec == spec.spec }
- return _activeTileDetails.value != null
+ return currentTile?.getDetailsViewModel { detailsViewModel ->
+ _activeTileDetails.value = detailsViewModel
+ } ?: false
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 7eb0aaa..0109e70a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -43,6 +43,7 @@
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.satellite.SatelliteDialogUtils;
import com.android.systemui.animation.Expandable;
+import com.android.systemui.bluetooth.qsdialog.BluetoothDetailsViewModel;
import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogViewModel;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -51,6 +52,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.qs.TileDetailsViewModel;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
@@ -63,6 +65,7 @@
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import javax.inject.Inject;
@@ -121,6 +124,21 @@
@Override
protected void handleClick(@Nullable Expandable expandable) {
+ handleClickWithSatelliteCheck(() -> handleClickEvent(expandable));
+ }
+
+ @Override
+ public boolean getDetailsViewModel(Consumer<TileDetailsViewModel> callback) {
+ handleClickWithSatelliteCheck(() ->
+ callback.accept(new BluetoothDetailsViewModel(() -> {
+ longClick(null);
+ return null;
+ }))
+ );
+ return true;
+ }
+
+ private void handleClickWithSatelliteCheck(Runnable clickCallback) {
if (com.android.internal.telephony.flags.Flags.oemEnabledSatelliteFlag()) {
if (mClickJob != null && !mClickJob.isCompleted()) {
return;
@@ -130,12 +148,12 @@
if (!isAllowClick) {
return null;
}
- handleClickEvent(expandable);
+ clickCallback.run();
return null;
});
return;
}
- handleClickEvent(expandable);
+ clickCallback.run();
}
private void handleClickEvent(@Nullable Expandable expandable) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 30c2adf..ad027b4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -24,7 +24,6 @@
import android.app.Dialog;
import android.content.Intent;
import android.media.MediaRouter.RouteInfo;
-import android.media.projection.StopReason;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
@@ -184,7 +183,7 @@
});
}
} else {
- mController.stopCasting(activeDevices.get(0), StopReason.STOP_QS_TILE);
+ mController.stopCasting(activeDevices.get(0));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index ec8d30b..fc82592 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -18,7 +18,6 @@
import android.app.Dialog;
import android.content.Intent;
-import android.media.projection.StopReason;
import android.os.Handler;
import android.os.Looper;
import android.service.quicksettings.Tile;
@@ -227,7 +226,7 @@
}
private void stopRecording() {
- mController.stopRecording(StopReason.STOP_QS_TILE);
+ mController.stopRecording();
}
private final class Callback implements RecordingController.RecordingStateChangeCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
index 9453447..85aa674 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor
-import android.media.projection.StopReason
import android.util.Log
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.DialogCuj
@@ -62,9 +61,7 @@
Log.d(TAG, "Cancelling countdown")
withContext(backgroundContext) { recordingController.cancelCountdown() }
}
- is ScreenRecordModel.Recording -> {
- screenRecordRepository.stopRecording(StopReason.STOP_QS_TILE)
- }
+ is ScreenRecordModel.Recording -> screenRecordRepository.stopRecording()
is ScreenRecordModel.DoingNothing ->
withContext(mainContext) {
showPrompt(action.expandable, user.identifier)
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 9ee99e4..d7463f8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.media.projection.StopReason;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Process;
@@ -59,7 +58,6 @@
private boolean mIsStarting;
private boolean mIsRecording;
private PendingIntent mStopIntent;
- private @StopReason int mStopReason = StopReason.STOP_UNKNOWN;
private final Bundle mInteractiveBroadcastOption;
private CountDownTimer mCountDownTimer = null;
private final Executor mMainExecutor;
@@ -85,7 +83,7 @@
new UserTracker.Callback() {
@Override
public void onUserChanged(int newUser, @NonNull Context userContext) {
- stopRecording(StopReason.STOP_USER_SWITCH);
+ stopRecording();
}
};
@@ -242,11 +240,9 @@
}
/**
- * Stop the recording and sets the stop reason to be used by the RecordingService
- * @param stopReason the method of the recording stopped (i.e. QS tile, status bar chip, etc.)
+ * Stop the recording
*/
- public void stopRecording(@StopReason int stopReason) {
- mStopReason = stopReason;
+ public void stopRecording() {
try {
if (mStopIntent != null) {
mRecordingControllerLogger.logRecordingStopped();
@@ -281,10 +277,6 @@
}
}
- public @StopReason int getStopReason() {
- return mStopReason;
- }
-
@Override
public void addCallback(@NonNull RecordingStateChangeCallback listener) {
mListeners.add(listener);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index f7b5271..8c207d1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -26,7 +26,6 @@
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.media.MediaRecorder;
-import android.media.projection.StopReason;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -79,7 +78,6 @@
private static final String EXTRA_SHOW_TAPS = "extra_showTaps";
private static final String EXTRA_CAPTURE_TARGET = "extra_captureTarget";
private static final String EXTRA_DISPLAY_ID = "extra_displayId";
- private static final String EXTRA_STOP_REASON = "extra_stopReason";
protected static final String ACTION_START = "com.android.systemui.screenrecord.START";
protected static final String ACTION_SHOW_START_NOTIF =
@@ -244,8 +242,7 @@
// Check user ID - we may be getting a stop intent after user switch, in which case
// we want to post the notifications for that user, which is NOT current user
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_ID_NOT_SPECIFIED);
- int stopReason = intent.getIntExtra(EXTRA_STOP_REASON, mController.getStopReason());
- stopService(userId, stopReason);
+ stopService(userId);
break;
case ACTION_SHARE:
@@ -489,11 +486,11 @@
getTag(), notificationIdForGroup, groupNotif, currentUser);
}
- private void stopService(@StopReason int stopReason) {
- stopService(USER_ID_NOT_SPECIFIED, stopReason);
+ private void stopService() {
+ stopService(USER_ID_NOT_SPECIFIED);
}
- private void stopService(int userId, @StopReason int stopReason) {
+ private void stopService(int userId) {
if (userId == USER_ID_NOT_SPECIFIED) {
userId = mUserContextTracker.getUserContext().getUserId();
}
@@ -502,7 +499,7 @@
setTapsVisible(mOriginalShowTaps);
try {
if (getRecorder() != null) {
- getRecorder().end(stopReason);
+ getRecorder().end();
}
saveRecording(userId);
} catch (RuntimeException exception) {
@@ -601,8 +598,7 @@
* @return
*/
protected Intent getNotificationIntent(Context context) {
- return new Intent(context, this.getClass()).setAction(ACTION_STOP_NOTIF)
- .putExtra(EXTRA_STOP_REASON, StopReason.STOP_HOST_APP);
+ return new Intent(context, this.getClass()).setAction(ACTION_STOP_NOTIF);
}
private Intent getShareIntent(Context context, Uri path) {
@@ -614,17 +610,14 @@
@Override
public void onInfo(MediaRecorder mr, int what, int extra) {
Log.d(getTag(), "Media recorder info: " + what);
- // Stop due to record reaching size limits so log as stopping due to error
- Intent stopIntent = getStopIntent(this);
- stopIntent.putExtra(EXTRA_STOP_REASON, StopReason.STOP_ERROR);
- onStartCommand(stopIntent, 0, 0);
+ onStartCommand(getStopIntent(this), 0, 0);
}
@Override
- public void onStopped(@StopReason int stopReason) {
+ public void onStopped() {
if (mController.isRecording()) {
Log.d(getTag(), "Stopping recording because the system requested the stop");
- stopService(stopReason);
+ stopService();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index f4455bf..2ca0621 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -41,7 +41,6 @@
import android.media.projection.IMediaProjectionManager;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
-import android.media.projection.StopReason;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
@@ -301,7 +300,7 @@
/**
* End screen recording, throws an exception if stopping recording failed
*/
- void end(@StopReason int stopReason) throws IOException {
+ void end() throws IOException {
Closer closer = new Closer();
// MediaRecorder might throw RuntimeException if stopped immediately after starting
@@ -310,17 +309,7 @@
closer.register(mMediaRecorder::release);
closer.register(mInputSurface::release);
closer.register(mVirtualDisplay::release);
- closer.register(() -> {
- if (stopReason == StopReason.STOP_UNKNOWN) {
- // Attempt to call MediaProjection#stop() even if it might have already been called.
- // If projection has already been stopped, then nothing will happen. Else, stop
- // will be logged as a manually requested stop from host app.
- mMediaProjection.stop();
- } else {
- // In any other case, the stop reason is related to the recorder, so pass it on here
- mMediaProjection.stop(stopReason);
- }
- });
+ closer.register(mMediaProjection::stop);
closer.register(this::stopInternalAudioRecording);
closer.close();
@@ -334,7 +323,7 @@
@Override
public void onStop() {
Log.d(TAG, "The system notified about stopping the projection");
- mListener.onStopped(StopReason.STOP_UNKNOWN);
+ mListener.onStopped();
}
private void stopInternalAudioRecording() {
@@ -464,7 +453,7 @@
* For example, this might happen when doing partial screen sharing of an app
* and the app that is being captured is closed.
*/
- void onStopped(@StopReason int stopReason);
+ void onStopped();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt
index b6b8ffa..9eeb3b9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt
@@ -16,7 +16,6 @@
package com.android.systemui.screenrecord.data.repository
-import android.media.projection.StopReason
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.screenrecord.RecordingController
@@ -42,7 +41,7 @@
val screenRecordState: Flow<ScreenRecordModel>
/** Stops the recording. */
- suspend fun stopRecording(@StopReason stopReason: Int)
+ suspend fun stopRecording()
}
@SysUISingleton
@@ -96,7 +95,7 @@
}
}
- override suspend fun stopRecording(@StopReason stopReason: Int) {
- withContext(bgCoroutineContext) { recordingController.stopRecording(stopReason) }
+ override suspend fun stopRecording() {
+ withContext(bgCoroutineContext) { recordingController.stopRecording() }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt
index 229cef9..b3dbf29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/domain/interactor/MediaRouterChipInteractor.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.chips.casttootherdevice.domain.interactor
-import android.media.projection.StopReason
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
@@ -66,9 +65,7 @@
/** Stops the currently active MediaRouter cast. */
fun stopCasting() {
- activeCastDevice.value?.let {
- mediaRouterRepository.stopCasting(it, StopReason.STOP_PRIVACY_CHIP)
- }
+ activeCastDevice.value?.let { mediaRouterRepository.stopCasting(it) }
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
index 0b5e669..f5952f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.chips.screenrecord.domain.interactor
-import android.media.projection.StopReason
import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -141,7 +140,7 @@
/** Stops the recording. */
fun stopRecording() {
- scope.launch { screenRecordRepository.stopRecording(StopReason.STOP_PRIVACY_CHIP) }
+ scope.launch { screenRecordRepository.stopRecording() }
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
index ece5a3f..a3dcc3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.policy;
-import android.media.projection.StopReason;
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.policy.CastController.Callback;
@@ -27,7 +26,7 @@
void setCurrentUserId(int currentUserId);
List<CastDevice> getCastDevices();
void startCasting(CastDevice device);
- void stopCasting(CastDevice device, @StopReason int stopReason);
+ void stopCasting(CastDevice device);
/**
* @return whether we have a connected device.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index ab20850..52f80fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -185,13 +185,13 @@
}
@Override
- public void stopCasting(CastDevice device, @StopReason int stopReason) {
+ public void stopCasting(CastDevice device) {
final boolean isProjection = device.getTag() instanceof MediaProjectionInfo;
mLogger.logStopCasting(isProjection);
if (isProjection) {
final MediaProjectionInfo projection = (MediaProjectionInfo) device.getTag();
if (Objects.equals(mProjectionManager.getActiveProjectionInfo(), projection)) {
- mProjectionManager.stopActiveProjection(stopReason);
+ mProjectionManager.stopActiveProjection(StopReason.STOP_QS_TILE);
} else {
mLogger.logStopCastingNoProjection(projection);
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt
index 35ea0ea..64cda1f2 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt
@@ -41,7 +41,13 @@
}
override fun accept(event: MotionEvent) {
- if (!isThreeFingerTouchpadSwipe(event)) return
+ if (!isMultifingerTouchpadSwipe(event)) return
+ if (!isThreeFingerTouchpadSwipe(event)) {
+ if (event.actionMasked == MotionEvent.ACTION_UP) {
+ gestureStateChangedCallback(GestureState.Error)
+ }
+ return
+ }
val gestureState = distanceTracker.processEvent(event)
updateGestureState(
gestureStateChangedCallback,
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt
index 68a2ef9..884f08e 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt
@@ -35,6 +35,11 @@
event.getAxisValue(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT) == fingerCount.toFloat()
}
+fun isMultifingerTouchpadSwipe(event: MotionEvent): Boolean {
+ return event.classification == MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE ||
+ event.classification == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE
+}
+
fun isTwoFingerSwipe(event: MotionEvent): Boolean {
return event.classification == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt
index 9801626..7767a46 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt
@@ -40,7 +40,13 @@
}
override fun accept(event: MotionEvent) {
- if (!isThreeFingerTouchpadSwipe(event)) return
+ if (!isMultifingerTouchpadSwipe(event)) return
+ if (!isThreeFingerTouchpadSwipe(event)) {
+ if (event.actionMasked == MotionEvent.ACTION_UP) {
+ gestureStateChangedCallback(GestureState.Error)
+ }
+ return
+ }
val gestureState = distanceTracker.processEvent(event)
velocityTracker.accept(event)
updateGestureState(
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt
index 5ff583a..74746de 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt
@@ -43,7 +43,13 @@
}
override fun accept(event: MotionEvent) {
- if (!isThreeFingerTouchpadSwipe(event)) return
+ if (!isMultifingerTouchpadSwipe(event)) return
+ if (!isThreeFingerTouchpadSwipe(event)) {
+ if (event.actionMasked == MotionEvent.ACTION_UP) {
+ gestureStateChangedCallback(GestureState.Error)
+ }
+ return
+ }
val gestureState = distanceTracker.processEvent(event)
velocityTracker.accept(event)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt
index 21e2917..dd275bd 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt
@@ -40,9 +40,8 @@
return if (isFromTouchpad && !buttonClick) {
if (isTwoFingerSwipe(event)) {
easterEggGestureMonitor.processTouchpadEvent(event)
- } else {
- gestureRecognizer.accept(event)
}
+ gestureRecognizer.accept(event)
true
} else {
false
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
index d8d6f2e9..330b887 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
@@ -4,6 +4,8 @@
import android.os.Handler
import android.os.Looper
import android.os.UserManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import android.testing.TestableLooper
@@ -20,10 +22,12 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.qs.TileDetailsViewModel
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsDetailedView
import com.android.systemui.qs.flags.QsInCompose.isEnabled
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
@@ -35,6 +39,7 @@
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertTrue
import kotlinx.coroutines.Job
import org.junit.After
import org.junit.Before
@@ -199,6 +204,7 @@
}
@Test
+ @DisableFlags(QsDetailedView.FLAG_NAME)
fun handleClick_hasSatelliteFeatureButNoQsTileDialogAndClickIsProcessing_doNothing() {
mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
`when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
@@ -212,6 +218,7 @@
}
@Test
+ @DisableFlags(QsDetailedView.FLAG_NAME)
fun handleClick_noSatelliteFeatureAndNoQsTileDialog_directSetBtEnable() {
mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
`when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
@@ -223,6 +230,7 @@
}
@Test
+ @DisableFlags(QsDetailedView.FLAG_NAME)
fun handleClick_noSatelliteFeatureButHasQsTileDialog_showDialog() {
mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
`when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
@@ -234,6 +242,35 @@
}
@Test
+ @EnableFlags(QsDetailedView.FLAG_NAME)
+ fun handleClick_hasSatelliteFeatureAndQsDetailedViewIsEnabledAndClickIsProcessing_doNothing() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ `when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
+ .thenReturn(false)
+ `when`(clickJob.isCompleted).thenReturn(false)
+ tile.mClickJob = clickJob
+ var currentModel: TileDetailsViewModel? = null
+
+ tile.getDetailsViewModel { model: TileDetailsViewModel? -> currentModel = model }
+
+ // Click is not allowed.
+ assertThat(currentModel).isEqualTo(null)
+ }
+
+ @Test
+ @EnableFlags(QsDetailedView.FLAG_NAME)
+ fun handleClick_noSatelliteFeatureAndQsDetailedViewIsEnabled_returnDetailsViewModel() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ `when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
+ .thenReturn(false)
+ var currentModel: TileDetailsViewModel? = null
+
+ tile.getDetailsViewModel { model: TileDetailsViewModel? -> currentModel = model }
+
+ assertTrue(currentModel != null)
+ }
+
+ @Test
fun testMetadataListener_whenDisconnected_isUnregistered() {
val state = QSTile.BooleanState()
val cachedDevice = mock<CachedBluetoothDevice>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index a17f100..afff485 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -36,7 +36,6 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.media.projection.StopReason;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -155,7 +154,7 @@
PendingIntent stopIntent = Mockito.mock(PendingIntent.class);
mController.startCountdown(0, 0, startIntent, stopIntent);
- mController.stopRecording(StopReason.STOP_UNKNOWN);
+ mController.stopRecording();
assertFalse(mController.isStarting());
assertFalse(mController.isRecording());
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
index 4aa132c..8c9163d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
@@ -43,5 +43,6 @@
selectedUserInteractor,
keyguardEnabledInteractor,
keyguardServiceLockNowInteractor,
+ keyguardInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediarouter/data/repository/FakeMediaRouterRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediarouter/data/repository/FakeMediaRouterRepository.kt
index d5637cb..8aa7a03 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/mediarouter/data/repository/FakeMediaRouterRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediarouter/data/repository/FakeMediaRouterRepository.kt
@@ -16,7 +16,6 @@
package com.android.systemui.mediarouter.data.repository
-import android.media.projection.StopReason
import com.android.systemui.statusbar.policy.CastDevice
import kotlinx.coroutines.flow.MutableStateFlow
@@ -26,7 +25,7 @@
var lastStoppedDevice: CastDevice? = null
private set
- override fun stopCasting(device: CastDevice, @StopReason stopReason: Int) {
+ override fun stopCasting(device: CastDevice) {
lastStoppedDevice = device
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt
index 06822a6..4714969 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt
@@ -19,7 +19,6 @@
import com.android.internal.logging.InstanceId
import com.android.systemui.animation.Expandable
import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.plugins.qs.TileDetailsViewModel
class FakeQSTile(var user: Int, var available: Boolean = true) : QSTile {
private var tileSpec: String? = null
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt
index 4c9e174..30b4763 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt
@@ -16,7 +16,6 @@
package com.android.systemui.screenrecord.data.repository
-import android.media.projection.StopReason
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -26,7 +25,7 @@
var stopRecordingInvoked = false
- override suspend fun stopRecording(@StopReason stopReason: Int) {
+ override suspend fun stopRecording() {
stopRecordingInvoked = true
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeCastController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeCastController.kt
index da6b2ae..2df0c7a5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeCastController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeCastController.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.policy
-import android.media.projection.StopReason
import java.io.PrintWriter
class FakeCastController : CastController {
@@ -46,7 +45,7 @@
override fun startCasting(device: CastDevice?) {}
- override fun stopCasting(device: CastDevice?, @StopReason stopReason: Int) {
+ override fun stopCasting(device: CastDevice?) {
lastStoppedDevice = device
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckerCastController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckerCastController.java
index 857dc85..2249bc0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckerCastController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckerCastController.java
@@ -16,7 +16,6 @@
package com.android.systemui.utils.leaks;
-import android.media.projection.StopReason;
import android.testing.LeakCheck;
import com.android.systemui.statusbar.policy.CastController;
@@ -52,7 +51,7 @@
}
@Override
- public void stopCasting(CastDevice device, @StopReason int stopReason) {
+ public void stopCasting(CastDevice device) {
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index e1b6c9c..5133575 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -245,22 +245,45 @@
final boolean complete =
event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
&& !event.isCancelled();
+
+ // TODO(b/355499907): Receive and handle held key gestures, which can be used
+ // for continuous scaling and panning. In addition, handle multiple pan gestures
+ // at the same time (e.g. user may try to pan diagonally) reasonably, including
+ // decreasing diagonal movement by sqrt(2) to make it appear the same speed
+ // as non-diagonal movement.
+
+ if (!complete) {
+ return false;
+ }
+
final int gestureType = event.getKeyGestureType();
final int displayId = isDisplayIdValid(event.getDisplayId())
? event.getDisplayId() : Display.DEFAULT_DISPLAY;
switch (gestureType) {
case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN:
- if (complete) {
mAms.getMagnificationController().scaleMagnificationByStep(
displayId, MagnificationController.ZOOM_DIRECTION_IN);
- }
return true;
case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT:
- if (complete) {
mAms.getMagnificationController().scaleMagnificationByStep(
displayId, MagnificationController.ZOOM_DIRECTION_OUT);
- }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT:
+ mAms.getMagnificationController().panMagnificationByStep(
+ displayId, MagnificationController.PAN_DIRECTION_LEFT);
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT:
+ mAms.getMagnificationController().panMagnificationByStep(
+ displayId, MagnificationController.PAN_DIRECTION_RIGHT);
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP:
+ mAms.getMagnificationController().panMagnificationByStep(
+ displayId, MagnificationController.PAN_DIRECTION_UP);
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN:
+ mAms.getMagnificationController().panMagnificationByStep(
+ displayId, MagnificationController.PAN_DIRECTION_DOWN);
return true;
}
return false;
@@ -270,7 +293,11 @@
public boolean isKeyGestureSupported(int gestureType) {
return switch (gestureType) {
case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT -> true;
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN -> true;
default -> false;
};
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 058b2be..2e131b6 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -35,14 +35,19 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.display.DisplayManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseDoubleArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
+import android.util.TypedValue;
+import android.view.Display;
import android.view.accessibility.MagnificationAnimationCallback;
import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
@@ -102,6 +107,7 @@
/** Whether the platform supports window magnification feature. */
private final boolean mSupportWindowMagnification;
private final MagnificationScaleStepProvider mScaleStepProvider;
+ private final MagnificationPanStepProvider mPanStepProvider;
private final Executor mBackgroundExecutor;
@@ -132,7 +138,7 @@
.UiChangesForAccessibilityCallbacks> mAccessibilityCallbacksDelegateArray =
new SparseArray<>();
- // Direction magnifier scale can be altered.
+ // Direction magnification scale can be altered.
public static final int ZOOM_DIRECTION_IN = 0;
public static final int ZOOM_DIRECTION_OUT = 1;
@@ -140,6 +146,16 @@
public @interface ZoomDirection {
}
+ // Directions magnification center can be moved.
+ public static final int PAN_DIRECTION_LEFT = 0;
+ public static final int PAN_DIRECTION_RIGHT = 1;
+ public static final int PAN_DIRECTION_UP = 2;
+ public static final int PAN_DIRECTION_DOWN = 3;
+
+ @IntDef({PAN_DIRECTION_LEFT, PAN_DIRECTION_RIGHT, PAN_DIRECTION_UP, PAN_DIRECTION_DOWN})
+ public @interface PanDirection {
+ }
+
/**
* A callback to inform the magnification transition result on the given display.
*/
@@ -188,6 +204,87 @@
}
}
+ /**
+ * An interface to configure how much the magnification center should be affected when panning
+ * in steps.
+ */
+ public interface MagnificationPanStepProvider {
+ /**
+ * Calculate the next value based on the current scale.
+ *
+ * @param currentScale The current magnification scale value.
+ * @param displayId The displayId for the display being magnified.
+ * @return The next pan step value.
+ */
+ float nextPanStep(float currentScale, int displayId);
+ }
+
+ public static class DefaultMagnificationPanStepProvider implements
+ MagnificationPanStepProvider, DisplayManager.DisplayListener {
+ // We want panning to be 40 dip per keystroke at scale 2, and 1 dip per keystroke at scale
+ // 20. This can be defined using y = mx + b to get the slope and intercept.
+ // This works even if the device does not allow magnification up to 20x; we will still get
+ // a reasonable lineary ramp of panning movement for each scale step.
+ private static final float DEFAULT_SCALE = 2.0f;
+ private static final float PAN_STEP_AT_DEFAULT_SCALE_DIP = 40.0f;
+ private static final float SCALE_FOR_1_DIP_PAN = 20.0f;
+
+ private SparseDoubleArray mPanStepSlopes;
+ private SparseDoubleArray mPanStepIntercepts;
+
+ private final DisplayManager mDisplayManager;
+
+ DefaultMagnificationPanStepProvider(Context context) {
+ mDisplayManager = context.getSystemService(DisplayManager.class);
+ mDisplayManager.registerDisplayListener(this, /*handler=*/null);
+ mPanStepSlopes = new SparseDoubleArray();
+ mPanStepIntercepts = new SparseDoubleArray();
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ updateForDisplay(displayId);
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ updateForDisplay(displayId);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ mPanStepSlopes.delete(displayId);
+ mPanStepIntercepts.delete(displayId);
+ }
+
+ @Override
+ public float nextPanStep(float currentScale, int displayId) {
+ if (mPanStepSlopes.indexOfKey(displayId) < 0) {
+ updateForDisplay(displayId);
+ }
+ return Math.max((float) (mPanStepSlopes.get(displayId) * currentScale
+ + mPanStepIntercepts.get(displayId)), 1);
+ }
+
+ private void updateForDisplay(int displayId) {
+ Display display = mDisplayManager.getDisplay(displayId);
+ if (display == null) {
+ return;
+ }
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getMetrics(metrics);
+ final float panStepAtDefaultScaleInPx = TypedValue.convertDimensionToPixels(
+ TypedValue.COMPLEX_UNIT_DIP, PAN_STEP_AT_DEFAULT_SCALE_DIP, metrics);
+ final float panStepAtMaxScaleInPx = TypedValue.convertDimensionToPixels(
+ TypedValue.COMPLEX_UNIT_DIP, 1.0f, metrics);
+ final float panStepSlope = (panStepAtMaxScaleInPx - panStepAtDefaultScaleInPx)
+ / (SCALE_FOR_1_DIP_PAN - DEFAULT_SCALE);
+ mPanStepSlopes.put(displayId, panStepSlope);
+ mPanStepIntercepts.put(displayId,
+ panStepAtDefaultScaleInPx - panStepSlope * DEFAULT_SCALE);
+ }
+ }
+
public MagnificationController(AccessibilityManagerService ams, Object lock,
Context context, MagnificationScaleProvider scaleProvider,
Executor backgroundExecutor) {
@@ -201,6 +298,7 @@
mSupportWindowMagnification = context.getPackageManager().hasSystemFeature(
FEATURE_WINDOW_MAGNIFICATION);
mScaleStepProvider = new DefaultMagnificationScaleStepProvider();
+ mPanStepProvider = new DefaultMagnificationPanStepProvider(mContext);
mAlwaysOnMagnificationFeatureFlag = new AlwaysOnMagnificationFeatureFlag(context);
mAlwaysOnMagnificationFeatureFlag.addOnChangedListener(
@@ -935,13 +1033,12 @@
}
/**
- * Scales the magnifier on the given display one step in/out based on the zoomIn param.
+ * Scales the magnifier on the given display one step in/out based on the direction param.
*
* @param displayId The logical display id.
* @param direction Whether the scale should be zoomed in or out.
- * @return {@code true} if the magnification scale was affected.
*/
- public boolean scaleMagnificationByStep(int displayId, @ZoomDirection int direction) {
+ public void scaleMagnificationByStep(int displayId, @ZoomDirection int direction) {
if (getFullScreenMagnificationController().isActivated(displayId)) {
final float magnificationScale = getFullScreenMagnificationController().getScale(
displayId);
@@ -950,7 +1047,6 @@
getFullScreenMagnificationController().setScaleAndCenter(displayId,
nextMagnificationScale,
Float.NaN, Float.NaN, true, MAGNIFICATION_GESTURE_HANDLER_ID);
- return nextMagnificationScale != magnificationScale;
}
if (getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId)) {
@@ -959,10 +1055,51 @@
final float nextMagnificationScale = mScaleStepProvider.nextScaleStep(
magnificationScale, direction);
getMagnificationConnectionManager().setScale(displayId, nextMagnificationScale);
- return nextMagnificationScale != magnificationScale;
+ }
+ }
+
+ /**
+ * Pans the magnifier on the given display one step left/right/up/down based on the direction
+ * param.
+ *
+ * @param displayId The logical display id.
+ * @param direction Whether the direction should be left/right/up/down.
+ */
+ public void panMagnificationByStep(int displayId, @PanDirection int direction) {
+ final boolean fullscreenActivated =
+ getFullScreenMagnificationController().isActivated(displayId);
+ final boolean windowActivated =
+ getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId);
+ if (!fullscreenActivated && !windowActivated) {
+ return;
}
- return false;
+ final float scale = fullscreenActivated
+ ? getFullScreenMagnificationController().getScale(displayId)
+ : getMagnificationConnectionManager().getScale(displayId);
+ final float step = mPanStepProvider.nextPanStep(scale, displayId);
+
+ float offsetX = 0;
+ float offsetY = 0;
+ if (direction == PAN_DIRECTION_LEFT) {
+ offsetX = -step;
+ } else if (direction == PAN_DIRECTION_RIGHT) {
+ offsetX = step;
+ } else if (direction == PAN_DIRECTION_UP) {
+ offsetY = -step;
+ } else if (direction == PAN_DIRECTION_DOWN) {
+ offsetY = step;
+ }
+
+ if (fullscreenActivated) {
+ final float centerX = getFullScreenMagnificationController().getCenterX(displayId);
+ final float centerY = getFullScreenMagnificationController().getCenterY(displayId);
+ getFullScreenMagnificationController().setScaleAndCenter(displayId, scale,
+ centerX + offsetX, centerY + offsetY, true, MAGNIFICATION_GESTURE_HANDLER_ID);
+ } else {
+ getMagnificationConnectionManager().moveWindowMagnification(displayId, offsetX,
+ offsetY);
+ }
}
private final class DisableMagnificationCallback implements
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index 65c446e..ec6c3b7 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -74,6 +74,16 @@
}
flag {
+ name: "multiple_fill_history"
+ namespace: "autofill"
+ description: "Allows tracking per Session FillEventHistory. As a bugfix flag to guard against DeviceConfig flag"
+ bug: "365630157"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "add_session_id_to_client_state"
namespace: "autofill"
description: "Include the session id into the FillEventHistory events as part of ClientState"
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 0fa43ac..11710c9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -152,6 +152,15 @@
@GuardedBy("mLock")
private final SparseArray<Session> mSessions = new SparseArray<>();
+ /**
+ * Cache of FillEventHistory for active Sessions.
+ *
+ * <p>New histories are added whenever a Session is created and are kept until Sessions are
+ * removed through removeSessionLocked()
+ */
+ @GuardedBy("mLock")
+ private final SparseArray<FillEventHistory> mFillHistories = new SparseArray<>();
+
/** The last selection */
@GuardedBy("mLock")
private FillEventHistory mEventHistory;
@@ -663,6 +672,10 @@
flags, mInputMethodManagerInternal, isPrimaryCredential);
mSessions.put(newSession.id, newSession);
+ if (Flags.multipleFillHistory() && !forAugmentedAutofillOnly) {
+ mFillHistories.put(newSession.id, new FillEventHistory(sessionId, null));
+ }
+
return newSession;
}
@@ -756,6 +769,28 @@
TAG,
"removeSessionLocked(): removed " + sessionId);
}
+
+ FillEventHistory history = null;
+
+ if (Flags.multipleFillHistory() && mFillHistories != null) {
+ history = mFillHistories.get(sessionId);
+ mFillHistories.delete(sessionId);
+ }
+
+ if (mInfo == null || mInfo.getServiceInfo() == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "removeSessionLocked(): early return because mInfo is null");
+ }
+ return;
+ }
+
+ if (mMaster == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "removeSessionLocked(): early return because mMaster is null");
+ }
+ return;
+ }
+
RemoteFillService remoteService =
new RemoteFillService(
getContext(),
@@ -764,7 +799,8 @@
/* callbacks= */ null,
mMaster.isInstantServiceAllowed(),
/* credentialAutofillService= */ null);
- remoteService.onSessionDestroyed(null);
+
+ remoteService.onSessionDestroyed(history);
}
}
@@ -886,6 +922,10 @@
}
}
mSessions.clear();
+ if (Flags.multipleFillHistory()) {
+ mFillHistories.clear();
+ }
+
for (int i = 0; i < remoteFillServices.size(); i++) {
remoteFillServices.valueAt(i).destroy();
}
@@ -944,60 +984,132 @@
return true;
}
- /**
- * Updates the last fill selection when an authentication was selected.
- */
- void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState,
- int uiType, @Nullable AutofillId focusedId) {
- synchronized (mLock) {
- if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
- mEventHistory.addEvent(
- new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null,
- null, null, null, null, null, null,
- NO_SAVE_UI_REASON_NONE, uiType, focusedId));
+ @GuardedBy("mLock")
+ void addEventToHistory(String eventName, int sessionId, Event event) {
+ // For the singleton filleventhistory
+ if (isValidEventLocked(eventName, sessionId)) {
+ mEventHistory.addEvent(event);
+ }
+
+ if (Flags.multipleFillHistory()) {
+ FillEventHistory history = mFillHistories.get(sessionId);
+ if (history != null) {
+ history.addEvent(event);
+ } else {
+ if (sVerbose) {
+ Slog.v(TAG, eventName
+ + " not logged because FillEventHistory is not tracked for: "
+ + sessionId);
+ }
}
}
}
/**
- * Updates the last fill selection when an dataset authentication was selected.
+ * Updates the last fill selection when an authentication was selected.
*/
- void logDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId,
- @Nullable Bundle clientState, int uiType, @Nullable AutofillId focusedId) {
+ void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState,
+ int uiType, @Nullable AutofillId focusedId, boolean shouldAdd) {
synchronized (mLock) {
- if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) {
- mEventHistory.addEvent(
- new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
- clientState, null, null, null, null, null, null, null, null,
- NO_SAVE_UI_REASON_NONE, uiType, focusedId));
+
+ String methodName = "setAuthenticationSelected()";
+
+ if (!shouldAdd) {
+ if (sVerbose) {
+ Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+ }
+ return;
}
+
+ Event event =
+ new Event(
+ Event.TYPE_AUTHENTICATION_SELECTED,
+ null,
+ clientState,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ NO_SAVE_UI_REASON_NONE,
+ uiType,
+ focusedId);
+
+ addEventToHistory(methodName, sessionId, event);
+ }
+ }
+
+ /** Updates the last fill selection when a dataset authentication was selected. */
+ void logDatasetAuthenticationSelected(
+ @Nullable String selectedDataset,
+ int sessionId,
+ @Nullable Bundle clientState,
+ int uiType,
+ @Nullable AutofillId focusedId,
+ boolean shouldAdd) {
+ synchronized (mLock) {
+ String methodName = "logDatasetAuthenticationSelected()";
+
+ if (!shouldAdd) {
+ if (sVerbose) {
+ Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+ }
+ return;
+ }
+
+ Event event = new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
+ clientState, null, null, null, null, null, null, null, null,
+ NO_SAVE_UI_REASON_NONE, uiType, focusedId);
+ addEventToHistory(methodName, sessionId, event);
}
}
/**
* Updates the last fill selection when an save Ui is shown.
*/
- void logSaveShown(int sessionId, @Nullable Bundle clientState) {
+ void logSaveShown(int sessionId, @Nullable Bundle clientState, boolean shouldAdd) {
synchronized (mLock) {
- if (isValidEventLocked("logSaveShown()", sessionId)) {
- mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
- null, null, null, null, null, null, null, /* focusedId= */ null));
+ String methodName = "logSaveShown()";
+
+ if (!shouldAdd) {
+ if (sVerbose) {
+ Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+ }
+ return;
}
+
+ Event event = new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
+ null, null, null, null, null, null, null, /* focusedId= */ null);
+
+ addEventToHistory(methodName, sessionId, event);
}
}
- /**
- * Updates the last fill response when a dataset was selected.
- */
- void logDatasetSelected(@Nullable String selectedDataset, int sessionId,
- @Nullable Bundle clientState, int uiType, @Nullable AutofillId focusedId) {
+ /** Updates the last fill response when a dataset was selected. */
+ void logDatasetSelected(
+ @Nullable String selectedDataset,
+ int sessionId,
+ @Nullable Bundle clientState,
+ int uiType,
+ @Nullable AutofillId focusedId,
+ boolean shouldAdd) {
synchronized (mLock) {
- if (isValidEventLocked("logDatasetSelected()", sessionId)) {
- mEventHistory.addEvent(
- new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
- null, null, null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
- uiType, focusedId));
+ String methodName = "logDatasetSelected()";
+
+ if (!shouldAdd) {
+ if (sVerbose) {
+ Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+ }
+ return;
}
+
+ Event event = new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
+ null, null, null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
+ uiType, focusedId);
+ addEventToHistory(methodName, sessionId, event);
}
}
@@ -1005,40 +1117,75 @@
* Updates the last fill response when a dataset is shown.
*/
void logDatasetShown(int sessionId, @Nullable Bundle clientState, int uiType,
- @Nullable AutofillId focusedId) {
+ @Nullable AutofillId focusedId, boolean shouldAdd) {
synchronized (mLock) {
- if (isValidEventLocked("logDatasetShown", sessionId)) {
- mEventHistory.addEvent(
- new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
+ String methodName = "logDatasetShown()";
+
+ if (!shouldAdd) {
+ if (sVerbose) {
+ Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+ }
+ return;
+ }
+
+ Event event = new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
- uiType, focusedId));
+ uiType, focusedId);
+ addEventToHistory(methodName, sessionId, event);
+ }
+ }
+
+ void logViewEnteredForHistory(
+ int sessionId,
+ @Nullable Bundle clientState,
+ FillEventHistory history,
+ @Nullable AutofillId focusedId) {
+ if (history.getEvents() != null) {
+ // Do not log this event more than once
+ for (Event event : history.getEvents()) {
+ if (event.getType() == Event.TYPE_VIEW_REQUESTED_AUTOFILL) {
+ if (sVerbose) {
+ Slog.v(TAG, "logViewEntered: already logged TYPE_VIEW_REQUESTED_AUTOFILL");
+ }
+ return;
+ }
}
}
+
+ history.addEvent(
+ new Event(Event.TYPE_VIEW_REQUESTED_AUTOFILL, null, clientState, null,
+ null, null, null, null, null, null, null, focusedId));
}
/**
* Updates the last fill response when a view was entered.
*/
void logViewEntered(int sessionId, @Nullable Bundle clientState,
- @Nullable AutofillId focusedId) {
+ @Nullable AutofillId focusedId, boolean shouldAdd) {
synchronized (mLock) {
- if (!isValidEventLocked("logViewEntered", sessionId)) {
+ String methodName = "logViewEntered()";
+
+ if (!shouldAdd) {
+ if (sVerbose) {
+ Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+ }
return;
}
- if (mEventHistory.getEvents() != null) {
- // Do not log this event more than once
- for (Event event : mEventHistory.getEvents()) {
- if (event.getType() == Event.TYPE_VIEW_REQUESTED_AUTOFILL) {
- Slog.v(TAG, "logViewEntered: already logged TYPE_VIEW_REQUESTED_AUTOFILL");
- return;
- }
- }
+ // This log does not call addEventToHistory() because each distinct FillEventHistory
+ // can only contain 1 TYPE_VIEW_REQUESTED_AUTOFILL event. Therefore, checking both
+ // the singleton FillEventHistory and the per Session FillEventHistory is necessary
+
+ if (isValidEventLocked(methodName, sessionId)) {
+ logViewEnteredForHistory(sessionId, clientState, mEventHistory, focusedId);
}
- mEventHistory.addEvent(
- new Event(Event.TYPE_VIEW_REQUESTED_AUTOFILL, null, clientState, null,
- null, null, null, null, null, null, null, focusedId));
+ if (Flags.multipleFillHistory()) {
+ FillEventHistory history = mFillHistories.get(sessionId);
+ if (history != null) {
+ logViewEnteredForHistory(sessionId, clientState, history, focusedId);
+ }
+ }
}
}
@@ -1096,12 +1243,12 @@
@Nullable ArrayList<String> changedDatasetIds,
@Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
@Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
- @NonNull ComponentName appComponentName, boolean compatMode) {
+ @NonNull ComponentName appComponentName, boolean compatMode, boolean shouldAdd) {
logContextCommittedLocked(sessionId, clientState, selectedDatasets, ignoredDatasets,
changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
manuallyFilledDatasetIds, /* detectedFieldIdsList= */ null,
/* detectedFieldClassificationsList= */ null, appComponentName, compatMode,
- Event.NO_SAVE_UI_REASON_NONE);
+ Event.NO_SAVE_UI_REASON_NONE, shouldAdd);
}
@GuardedBy("mLock")
@@ -1115,9 +1262,19 @@
@Nullable ArrayList<AutofillId> detectedFieldIdsList,
@Nullable ArrayList<FieldClassification> detectedFieldClassificationsList,
@NonNull ComponentName appComponentName, boolean compatMode,
- @NoSaveReason int saveDialogNotShowReason) {
- if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {
+ @NoSaveReason int saveDialogNotShowReason,
+ boolean shouldAdd) {
+
+ String methodName = "logContextCommittedLocked()";
+
+ if (!shouldAdd) {
if (sVerbose) {
+ Slog.v(TAG, methodName + " not logged because shouldAdd is false");
+ }
+ return;
+ }
+
+ if (sVerbose) {
Slog.v(TAG, "logContextCommitted() with FieldClassification: id=" + sessionId
+ ", selectedDatasets=" + selectedDatasets
+ ", ignoredDatasetIds=" + ignoredDatasets
@@ -1129,44 +1286,58 @@
+ ", appComponentName=" + appComponentName.toShortString()
+ ", compatMode=" + compatMode
+ ", saveDialogNotShowReason=" + saveDialogNotShowReason);
- }
- AutofillId[] detectedFieldsIds = null;
- FieldClassification[] detectedFieldClassifications = null;
- if (detectedFieldIdsList != null) {
- detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()];
- detectedFieldIdsList.toArray(detectedFieldsIds);
- detectedFieldClassifications =
- new FieldClassification[detectedFieldClassificationsList.size()];
- detectedFieldClassificationsList.toArray(detectedFieldClassifications);
-
- final int numberFields = detectedFieldsIds.length;
- int totalSize = 0;
- float totalScore = 0;
- for (int i = 0; i < numberFields; i++) {
- final FieldClassification fc = detectedFieldClassifications[i];
- final List<Match> matches = fc.getMatches();
- final int size = matches.size();
- totalSize += size;
- for (int j = 0; j < size; j++) {
- totalScore += matches.get(j).getScore();
- }
- }
-
- final int averageScore = (int) ((totalScore * 100) / totalSize);
- mMetricsLogger.write(Helper
- .newLogMaker(MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES,
- appComponentName, getServicePackageName(), sessionId, compatMode)
- .setCounterValue(numberFields)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE,
- averageScore));
- }
- mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null,
- clientState, selectedDatasets, ignoredDatasets,
- changedFieldIds, changedDatasetIds,
- manuallyFilledFieldIds, manuallyFilledDatasetIds,
- detectedFieldsIds, detectedFieldClassifications, saveDialogNotShowReason,
- /* focusedId= */ null));
}
+
+ AutofillId[] detectedFieldsIds = null;
+ FieldClassification[] detectedFieldClassifications = null;
+ if (detectedFieldIdsList != null) {
+ detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()];
+ detectedFieldIdsList.toArray(detectedFieldsIds);
+ detectedFieldClassifications =
+ new FieldClassification[detectedFieldClassificationsList.size()];
+ detectedFieldClassificationsList.toArray(detectedFieldClassifications);
+
+ final int numberFields = detectedFieldsIds.length;
+ int totalSize = 0;
+ float totalScore = 0;
+ for (int i = 0; i < numberFields; i++) {
+ final FieldClassification fc = detectedFieldClassifications[i];
+ final List<Match> matches = fc.getMatches();
+ final int size = matches.size();
+ totalSize += size;
+ for (int j = 0; j < size; j++) {
+ totalScore += matches.get(j).getScore();
+ }
+ }
+
+ final int averageScore = (int) ((totalScore * 100) / totalSize);
+ mMetricsLogger.write(
+ Helper.newLogMaker(
+ MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES,
+ appComponentName,
+ getServicePackageName(),
+ sessionId,
+ compatMode)
+ .setCounterValue(numberFields)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE, averageScore));
+ }
+ Event event =
+ new Event(
+ Event.TYPE_CONTEXT_COMMITTED,
+ null,
+ clientState,
+ selectedDatasets,
+ ignoredDatasets,
+ changedFieldIds,
+ changedDatasetIds,
+ manuallyFilledFieldIds,
+ manuallyFilledDatasetIds,
+ detectedFieldsIds,
+ detectedFieldClassifications,
+ saveDialogNotShowReason,
+ /* focusedId= */ null);
+
+ addEventToHistory(methodName, sessionId, event);
}
/**
@@ -1174,7 +1345,9 @@
*
* @param callingUid The calling uid
* @return The history for the autofill or the augmented autofill events depending on the {@code
- * callingUid}, or {@code null} if there is none.
+ * callingUid}, or {@code null} if there is none.
+ * @deprecated Use {@link
+ * android.service.autofill.AutofillService#onSessionDestroyed(FillEventHistory)}
*/
FillEventHistory getFillEventHistory(int callingUid) {
synchronized (mLock) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index ba9865d..3ecff3b 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1962,7 +1962,7 @@
if (mLogViewEntered) {
mLogViewEntered = false;
- mService.logViewEntered(id, null, mCurrentViewId);
+ mService.logViewEntered(id, null, mCurrentViewId, shouldAddEventToHistory());
}
}
@@ -2866,7 +2866,12 @@
forceRemoveFromServiceLocked();
return;
}
- mService.setAuthenticationSelected(id, mClientState, uiType, mCurrentViewId);
+ mService.setAuthenticationSelected(
+ id,
+ mClientState,
+ uiType,
+ mCurrentViewId,
+ shouldAddEventToHistory());
}
@@ -2941,7 +2946,12 @@
if (!mLoggedInlineDatasetShown) {
// Chip inflation already logged, do not log again.
// This is needed because every chip inflation will call this.
- mService.logDatasetShown(this.id, mClientState, uiType, mCurrentViewId);
+ mService.logDatasetShown(
+ this.id,
+ mClientState,
+ uiType,
+ mCurrentViewId,
+ shouldAddEventToHistory());
Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown);
}
mLoggedInlineDatasetShown = true;
@@ -2949,7 +2959,12 @@
mPresentationStatsEventLogger.logWhenDatasetShown(numDatasetsShown);
// Explicitly sets maybeSetSuggestionPresentedTimestampMs
mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs();
- mService.logDatasetShown(this.id, mClientState, uiType, mCurrentViewId);
+ mService.logDatasetShown(
+ this.id,
+ mClientState,
+ uiType,
+ mCurrentViewId,
+ shouldAddEventToHistory());
Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown);
}
}
@@ -3943,7 +3958,8 @@
detectedFieldClassifications,
mComponentName,
mCompatMode,
- saveDialogNotShowReason);
+ saveDialogNotShowReason,
+ shouldAddEventToHistory());
mSessionCommittedEventLogger.maybeSetCommitReason(commitReason);
mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
mSaveEventLogger.maybeSetSaveUiNotShownReason(saveDialogNotShowReason);
@@ -4590,7 +4606,7 @@
}
private void logSaveShown() {
- mService.logSaveShown(id, mClientState);
+ mService.logSaveShown(id, mClientState, shouldAddEventToHistory());
}
@Nullable
@@ -5248,7 +5264,8 @@
// so this calling logViewEntered will be a nop.
// Calling logViewEntered() twice will only log it once
// TODO(271181979): this is broken for multiple partitions
- mService.logViewEntered(this.id, null, mCurrentViewId);
+ mService.logViewEntered(
+ this.id, null, mCurrentViewId, shouldAddEventToHistory());
}
// If this is the first time view is entered for inline, the last
@@ -6863,8 +6880,13 @@
// Autofill it directly...
if (dataset.getAuthentication() == null) {
if (generateEvent) {
- mService.logDatasetSelected(dataset.getId(), id, mClientState, uiType,
- mCurrentViewId);
+ mService.logDatasetSelected(
+ dataset.getId(),
+ id,
+ mClientState,
+ uiType,
+ mCurrentViewId,
+ shouldAddEventToHistory());
}
if (mCurrentViewId != null) {
mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
@@ -6875,7 +6897,7 @@
// ...or handle authentication.
mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType,
- mCurrentViewId);
+ mCurrentViewId, shouldAddEventToHistory());
mPresentationStatsEventLogger.maybeSetAuthenticationType(
AUTHENTICATION_TYPE_DATASET_AUTHENTICATION);
// does not matter the value of isPrimary because null response won't be overridden.
@@ -8018,6 +8040,32 @@
mService.getMaster().logRequestLocked(historyItem);
}
+ /**
+ * Don't add secondary providers to FillEventHistory
+ */
+ boolean shouldAddEventToHistory() {
+
+ FillResponse lastResponse = null;
+
+ synchronized (mLock) {
+ lastResponse = getLastResponseLocked("shouldAddEventToHistory(%s)");
+ }
+
+ // There might be events (like TYPE_VIEW_REQUESTED_AUTOFILL) that are
+ // generated before FillRequest/FillResponse mechanism are started, so
+ // still need to log it
+ if (lastResponse == null) {
+ return true;
+ }
+
+ if (mRequestId.isSecondaryProvider(lastResponse.getRequestId())) {
+ // The request was to a secondary provider - don't log these events
+ return false;
+ }
+
+ return true;
+ }
+
private void wtf(@Nullable Exception e, String fmt, Object... args) {
final String message = String.format(fmt, args);
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
index 73d5630..8681ea5 100644
--- a/services/core/java/com/android/server/input/InputGestureManager.java
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -221,6 +221,18 @@
systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_EQUALS,
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN));
+ systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_LEFT,
+ KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT));
+ systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_RIGHT,
+ KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT));
+ systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_UP,
+ KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP));
+ systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_DOWN,
+ KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN));
systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_M,
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION));
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6800142..c653dec 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2723,17 +2723,7 @@
return false;
}
- final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
- if (Flags.imeSwitcherRevamp()) {
- // The IME switcher button should be shown when the current IME specified a
- // language settings activity.
- final var curImi = settings.getMethodMap().get(settings.getSelectedInputMethod());
- if (curImi != null && curImi.createImeLanguageSettingsActivityIntent() != null) {
- return true;
- }
- }
-
- return hasMultipleSubtypesForSwitcher(false /* nonAuxOnly */, settings);
+ return hasMultipleSubtypesForSwitcher(false /* nonAuxOnly */, userId);
}
/**
@@ -2741,10 +2731,11 @@
* across all enabled IMEs for the given user.
*
* @param nonAuxOnly whether to check only for non auxiliary subtypes.
- * @param settings the input method settings under the given user ID.
+ * @param userId the id of the user for which to check the number of subtypes.
*/
private static boolean hasMultipleSubtypesForSwitcher(boolean nonAuxOnly,
- @NonNull InputMethodSettings settings) {
+ @UserIdInt int userId) {
+ final var settings = InputMethodSettingsRepository.get(userId);
List<InputMethodInfo> imes = settings.getEnabledInputMethodListWithFilter(
InputMethodInfo::shouldShowInInputMethodPicker);
final int numImes = imes.size();
@@ -4130,8 +4121,7 @@
@GuardedBy("ImfLock.class")
private void onImeSwitchButtonClickLocked(int displayId, @NonNull UserData userData) {
final int userId = userData.mUserId;
- final var settings = InputMethodSettingsRepository.get(userId);
- if (hasMultipleSubtypesForSwitcher(true /* nonAuxOnly */, settings)) {
+ if (hasMultipleSubtypesForSwitcher(true /* nonAuxOnly */, userId)) {
switchToNextInputMethodLocked(false /* onlyCurrentIme */, userData);
} else {
showInputMethodPickerFromSystem(
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index 556cc03..d29fde2 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -16,7 +16,6 @@
package com.android.server.location.contexthub;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.hardware.location.ContextHubManager.AUTHORIZATION_DENIED;
import static android.hardware.location.ContextHubManager.AUTHORIZATION_DENIED_GRACE_PERIOD;
import static android.hardware.location.ContextHubManager.AUTHORIZATION_GRANTED;
@@ -25,7 +24,6 @@
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.PendingIntent;
-import android.chre.flags.Flags;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
@@ -655,7 +653,13 @@
// If in the grace period, don't check permissions state since it'll cause cleanup
// messages to be dropped.
if (authState == AUTHORIZATION_DENIED
- || !notePermissions(messagePermissions, RECEIVE_MSG_NOTE + nanoAppId)) {
+ || !ContextHubServiceUtil.notePermissions(
+ mAppOpsManager,
+ mUid,
+ mPackage,
+ mAttributionTag,
+ messagePermissions,
+ RECEIVE_MSG_NOTE + nanoAppId)) {
Log.e(TAG, "Dropping message from " + Long.toHexString(nanoAppId) + ". " + mPackage
+ " doesn't have permission");
return ErrorCode.PERMISSION_DENIED;
@@ -754,56 +758,6 @@
}
/**
- * Checks that this client has all of the provided permissions.
- *
- * @param permissions list of permissions to check
- * @return true if the client has all of the permissions granted
- */
- boolean hasPermissions(List<String> permissions) {
- for (String permission : permissions) {
- if (mContext.checkPermission(permission, mPid, mUid) != PERMISSION_GRANTED) {
- Log.e(TAG, "no permission for " + permission);
- return false;
- }
- }
- return true;
- }
-
- /**
- * Attributes the provided permissions to the package of this client.
- *
- * @param permissions list of permissions covering data the client is about to receive
- * @param noteMessage message that should be noted alongside permissions attribution to
- * facilitate debugging
- * @return true if client has ability to use all of the provided permissions
- */
- boolean notePermissions(List<String> permissions, String noteMessage) {
- for (String permission : permissions) {
- int opCode = AppOpsManager.permissionToOpCode(permission);
- if (opCode != AppOpsManager.OP_NONE) {
- try {
- if (mAppOpsManager.noteOp(opCode, mUid, mPackage, mAttributionTag, noteMessage)
- != AppOpsManager.MODE_ALLOWED) {
- return false;
- }
- } catch (SecurityException e) {
- Log.e(
- TAG,
- "SecurityException: noteOp for pkg "
- + mPackage
- + " opcode "
- + opCode
- + ": "
- + e.getMessage());
- return false;
- }
- }
- }
-
- return true;
- }
-
- /**
* @return true if the client is a PendingIntent client that has been cancelled.
*/
boolean isPendingIntentCancelled() {
@@ -868,7 +822,8 @@
synchronized (mMessageChannelNanoappIdMap) {
// Check permission granted state synchronously since this method can be invoked from
// multiple threads.
- boolean hasPermissions = hasPermissions(nanoappPermissions);
+ boolean hasPermissions =
+ ContextHubServiceUtil.hasPermissions(mContext, mPid, mUid, nanoappPermissions);
curAuthState = mMessageChannelNanoappIdMap.getOrDefault(
nanoAppId, AUTHORIZATION_UNKNOWN);
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index 4e1df76..2c072d0 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -145,7 +145,8 @@
super.closeSession_enforcePermission();
if (!mIsRegistered.get()) throw new IllegalStateException("Endpoint is not registered");
try {
- mContextHubProxy.closeEndpointSession(sessionId, (byte) reason);
+ mContextHubProxy.closeEndpointSession(
+ sessionId, ContextHubServiceUtil.toHalReason(reason));
} catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
throw e;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index 77ec51a..957307a 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -16,7 +16,10 @@
package com.android.server.location.contexthub;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import android.Manifest;
+import android.app.AppOpsManager;
import android.content.Context;
import android.hardware.contexthub.EndpointInfo;
import android.hardware.contexthub.HubEndpoint;
@@ -535,4 +538,97 @@
return HubEndpoint.REASON_FAILURE;
}
}
+
+ /**
+ * Converts a byte integer defined by Reason.aidl to HubEndpoint.Reason values exposed to apps.
+ *
+ * @param reason The Reason.aidl value
+ * @return The converted HubEndpoint.Reason value
+ */
+ /* package */
+ static byte toHalReason(@HubEndpoint.Reason int reason) {
+ switch (reason) {
+ case HubEndpoint.REASON_FAILURE:
+ return Reason.UNSPECIFIED;
+ case HubEndpoint.REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED:
+ return Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED;
+ case HubEndpoint.REASON_CLOSE_ENDPOINT_SESSION_REQUESTED:
+ return Reason.CLOSE_ENDPOINT_SESSION_REQUESTED;
+ case HubEndpoint.REASON_ENDPOINT_INVALID:
+ return Reason.ENDPOINT_INVALID;
+ case HubEndpoint.REASON_ENDPOINT_STOPPED:
+ return Reason.ENDPOINT_GONE;
+ case HubEndpoint.REASON_PERMISSION_DENIED:
+ return Reason.PERMISSION_DENIED;
+ default:
+ Log.w(TAG, "toHalReason: invalid reason: " + reason);
+ return Reason.UNSPECIFIED;
+ }
+ }
+
+ /**
+ * Checks that the module with the provided context/pid/uid has all of the provided permissions.
+ *
+ * @param context The context to validate permissions for
+ * @param pid The PID to validate permissions for
+ * @param uid The UID to validate permissions for
+ * @param permissions The collection of permissions to check
+ * @return true if the module has all of the permissions granted
+ */
+ /* package */
+ static boolean hasPermissions(
+ Context context, int pid, int uid, Collection<String> permissions) {
+ for (String permission : permissions) {
+ if (context.checkPermission(permission, pid, uid) != PERMISSION_GRANTED) {
+ Log.e(TAG, "no permission for " + permission);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Attributes the provided permissions to the package of this client.
+ *
+ * @param appOpsManager The app ops manager to use
+ * @param uid The UID of the module to note permissions for
+ * @param packageName The package name of the module to note permissions for
+ * @param attributionTag The attribution tag of the module to note permissions for
+ * @param permissions The list of permissions covering data the client is about to receive
+ * @param noteMessage The message that should be noted alongside permissions attribution to
+ * facilitate debugging
+ * @return true if client has ability to use all of the provided permissions
+ */
+ /* package */
+ static boolean notePermissions(
+ AppOpsManager appOpsManager,
+ int uid,
+ String packageName,
+ String attributionTag,
+ List<String> permissions,
+ String noteMessage) {
+ for (String permission : permissions) {
+ int opCode = AppOpsManager.permissionToOpCode(permission);
+ if (opCode != AppOpsManager.OP_NONE) {
+ try {
+ if (appOpsManager.noteOp(opCode, uid, packageName, attributionTag, noteMessage)
+ != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ } catch (SecurityException e) {
+ Log.e(
+ TAG,
+ "SecurityException: noteOp for pkg "
+ + packageName
+ + " opcode "
+ + opCode
+ + ": "
+ + e.getMessage());
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/power/feature/power_flags.aconfig b/services/core/java/com/android/server/power/feature/power_flags.aconfig
index a6948fc..a975da3 100644
--- a/services/core/java/com/android/server/power/feature/power_flags.aconfig
+++ b/services/core/java/com/android/server/power/feature/power_flags.aconfig
@@ -31,7 +31,7 @@
name: "framework_wakelock_info"
namespace: "power"
description: "Feature flag to enable statsd pulling of FrameworkWakelockInfo atoms"
- bug: "352602149"
+ bug: "380847722"
}
flag {
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
index 7aed33d..16e2029 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
@@ -23,6 +23,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
import static android.content.pm.ActivityInfo.isFixedOrientation;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
+import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
import static android.content.pm.ActivityInfo.screenOrientationToString;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
@@ -66,6 +67,7 @@
final boolean shouldCameraCompatControlOrientation =
AppCompatCameraPolicy.shouldCameraCompatControlOrientation(mActivityRecord);
if (hasFullscreenOverride && isIgnoreOrientationRequestEnabled
+ && (isFixedOrientationLandscape(candidate) || isFixedOrientationPortrait(candidate))
// Do not override orientation to fullscreen for camera activities.
// Fixed-orientation activities are rarely tested in other orientations, and it
// often results in sideways or stretched previews. As the camera compat treatment
diff --git a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
index d0d3d43..f3b043b 100644
--- a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
@@ -60,7 +60,7 @@
/**
* The precomputed display insets for resolving configuration. It will be non-null if
- * {@link #shouldCreateAppCompatDisplayInsets} returns {@code true}.
+ * {@link ActivityRecord#shouldCreateAppCompatDisplayInsets} returns {@code true}.
*/
@Nullable
private AppCompatDisplayInsets mAppCompatDisplayInsets;
@@ -84,7 +84,7 @@
}
/**
- * @return The {@code true} if the current instance has {@link mAppCompatDisplayInsets} without
+ * @return The {@code true} if the current instance has {@link #mAppCompatDisplayInsets} without
* considering the inheritance implemented in {@link #getAppCompatDisplayInsets()}
*/
boolean hasAppCompatDisplayInsetsWithoutInheritance() {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0eac4f2..60130d1 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2167,13 +2167,14 @@
mSystemServiceManager.startServiceFromJar(
WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
t.traceEnd();
- // Start USD service
- if (android.net.wifi.flags.Flags.usd()) {
- t.traceBegin("StartUsd");
- mSystemServiceManager.startServiceFromJar(
- WIFI_USD_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
- t.traceEnd();
- }
+ }
+
+ if (android.net.wifi.flags.Flags.usd() && context.getResources().getBoolean(
+ com.android.internal.R.bool.config_deviceSupportsWifiUsd)) {
+ t.traceBegin("StartWifiUsd");
+ mSystemServiceManager.startServiceFromJar(WIFI_USD_SERVICE_CLASS,
+ WIFI_APEX_SERVICE_JAR_PATH);
+ t.traceEnd();
}
if (context.getPackageManager().hasSystemFeature(
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 228e32e..c31594a 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -16,6 +16,9 @@
package com.android.server.profcollect;
+import static android.content.Intent.ACTION_SCREEN_OFF;
+import static android.content.Intent.ACTION_SCREEN_ON;
+
import android.Manifest;
import android.annotation.RequiresPermission;
import android.app.job.JobInfo;
@@ -32,6 +35,7 @@
import android.os.Handler;
import android.os.IBinder.DeathRecipient;
import android.os.Looper;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
@@ -70,10 +74,11 @@
private int mUsageSetting;
private boolean mUploadEnabled;
- private static boolean sVerityEnforced;
- private boolean mAdbActive;
+ static boolean sVerityEnforced;
+ static boolean sIsInteractive;
+ static boolean sAdbActive;
- private IProfCollectd mIProfcollect;
+ private static IProfCollectd sIProfcollect;
private static ProfcollectForwardingService sSelfService;
private final Handler mHandler = new ProfcollectdHandler(IoThread.getHandler().getLooper());
@@ -86,17 +91,24 @@
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (INTENT_UPLOAD_PROFILES.equals(intent.getAction())) {
+ if (ACTION_SCREEN_ON.equals(intent.getAction())) {
+ Log.d(LOG_TAG, "Received broadcast that the device became interactive, was "
+ + sIsInteractive);
+ sIsInteractive = true;
+ } else if (ACTION_SCREEN_OFF.equals(intent.getAction())) {
+ Log.d(LOG_TAG, "Received broadcast that the device became noninteractive, was "
+ + sIsInteractive);
+ sIsInteractive = false;
+ } else if (INTENT_UPLOAD_PROFILES.equals(intent.getAction())) {
Log.d(LOG_TAG, "Received broadcast to pack and upload reports");
createAndUploadReport(sSelfService);
- }
- if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
+ } else if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
boolean isADB = intent.getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false);
if (isADB) {
boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
Log.d(LOG_TAG, "Received broadcast that ADB became " + connected
- + ", was " + mAdbActive);
- mAdbActive = connected;
+ + ", was " + sAdbActive);
+ sAdbActive = connected;
}
}
}
@@ -129,6 +141,8 @@
context.getResources().getBoolean(R.bool.config_profcollectReportUploaderEnabled);
final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_SCREEN_ON);
+ filter.addAction(ACTION_SCREEN_OFF);
filter.addAction(INTENT_UPLOAD_PROFILES);
filter.addAction(UsbManager.ACTION_USB_STATE);
context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
@@ -153,14 +167,24 @@
if (phase == PHASE_SYSTEM_SERVICES_READY) {
UsbManager usbManager = getContext().getSystemService(UsbManager.class);
if (usbManager == null) {
- mAdbActive = false;
- return;
+ sAdbActive = false;
+ Log.d(LOG_TAG, "USBManager is not ready");
+ } else {
+ sAdbActive = ((usbManager.getCurrentFunctions() & UsbManager.FUNCTION_ADB) == 1);
+ Log.d(LOG_TAG, "ADB is " + sAdbActive + " on system startup");
}
- mAdbActive = ((usbManager.getCurrentFunctions() & UsbManager.FUNCTION_ADB) == 1);
- Log.d(LOG_TAG, "ADB is " + mAdbActive + " on system startup");
+
+ PowerManager powerManager = getContext().getSystemService(PowerManager.class);
+ if (powerManager == null) {
+ sIsInteractive = true;
+ Log.d(LOG_TAG, "PowerManager is not ready");
+ } else {
+ sIsInteractive = powerManager.isInteractive();
+ Log.d(LOG_TAG, "Device is interactive " + sIsInteractive + " on system startup");
+ }
}
if (phase == PHASE_BOOT_COMPLETED) {
- if (mIProfcollect == null) {
+ if (sIProfcollect == null) {
return;
}
BackgroundThread.get().getThreadHandler().post(() -> {
@@ -172,22 +196,22 @@
}
private void registerProviderStatusCallback() {
- if (mIProfcollect == null) {
+ if (sIProfcollect == null) {
return;
}
try {
- mIProfcollect.registerProviderStatusCallback(mProviderStatusCallback);
+ sIProfcollect.registerProviderStatusCallback(mProviderStatusCallback);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Failed to register provider status callback: " + e.getMessage());
}
}
private boolean serviceHasSupportedTraceProvider() {
- if (mIProfcollect == null) {
+ if (sIProfcollect == null) {
return false;
}
try {
- return !mIProfcollect.get_supported_provider().isEmpty();
+ return !sIProfcollect.get_supported_provider().isEmpty();
} catch (RemoteException e) {
Log.e(LOG_TAG, "Failed to get supported provider: " + e.getMessage());
return false;
@@ -209,7 +233,7 @@
IProfCollectd.Stub.asInterface(
ServiceManager.getServiceOrThrow("profcollectd"));
profcollectd.asBinder().linkToDeath(new ProfcollectdDeathRecipient(), /*flags*/0);
- mIProfcollect = profcollectd;
+ sIProfcollect = profcollectd;
return true;
} catch (ServiceManager.ServiceNotFoundException | RemoteException e) {
Log.w(LOG_TAG, "Failed to connect profcollectd binder service.");
@@ -233,7 +257,8 @@
break;
case MESSAGE_REGISTER_SCHEDULERS:
registerObservers();
- ProfcollectBGJobService.schedule(getContext());
+ PeriodicTraceJobService.schedule(getContext());
+ ReportProcessJobService.schedule(getContext());
break;
default:
throw new AssertionError("Unknown message: " + message);
@@ -246,27 +271,66 @@
public void binderDied() {
Log.w(LOG_TAG, "profcollectd has died");
- mIProfcollect = null;
+ sIProfcollect = null;
tryConnectNativeService();
}
}
/**
- * Background trace process service.
+ * Background report process and upload service.
*/
- public static class ProfcollectBGJobService extends JobService {
- // Unique ID in system service
- private static final int JOB_IDLE_PROCESS = 260817;
+ public static class PeriodicTraceJobService extends JobService {
+ // Unique ID in system server
+ private static final int PERIODIC_TRACE_JOB_ID = 241207;
private static final ComponentName JOB_SERVICE_NAME = new ComponentName(
"android",
- ProfcollectBGJobService.class.getName());
+ PeriodicTraceJobService.class.getName());
+
+ /**
+ * Attach the service to the system job scheduler.
+ */
+ public static void schedule(Context context) {
+ final int interval = DeviceConfig.getInt(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
+ "collection_interval", 600);
+ JobScheduler js = context.getSystemService(JobScheduler.class);
+ js.schedule(new JobInfo.Builder(PERIODIC_TRACE_JOB_ID, JOB_SERVICE_NAME)
+ .setPeriodic(TimeUnit.SECONDS.toMillis(interval))
+ // PRIORITY_DEFAULT is the highest priority we can request for a periodic job.
+ .setPriority(JobInfo.PRIORITY_DEFAULT)
+ .build());
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ if (sIProfcollect != null) {
+ Utils.traceSystem(sIProfcollect, "periodic");
+ }
+ jobFinished(params, false);
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ return false;
+ }
+ }
+
+ /**
+ * Background report process and upload service.
+ */
+ public static class ReportProcessJobService extends JobService {
+ // Unique ID in system server
+ private static final int REPORT_PROCESS_JOB_ID = 260817;
+ private static final ComponentName JOB_SERVICE_NAME = new ComponentName(
+ "android",
+ ReportProcessJobService.class.getName());
/**
* Attach the service to the system job scheduler.
*/
public static void schedule(Context context) {
JobScheduler js = context.getSystemService(JobScheduler.class);
- js.schedule(new JobInfo.Builder(JOB_IDLE_PROCESS, JOB_SERVICE_NAME)
+ js.schedule(new JobInfo.Builder(REPORT_PROCESS_JOB_ID, JOB_SERVICE_NAME)
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
.setPeriodic(BG_PROCESS_INTERVAL)
@@ -283,7 +347,6 @@
@Override
public boolean onStopJob(JobParameters params) {
- // TODO: Handle this?
return false;
}
}
@@ -311,14 +374,8 @@
private class AppLaunchObserver extends ActivityMetricsLaunchObserver {
@Override
public void onIntentStarted(Intent intent, long timestampNanos) {
- if (mIProfcollect == null) {
- return;
- }
- if (mAdbActive) {
- return;
- }
if (Utils.withFrequency("applaunch_trace_freq", 5)) {
- Utils.traceSystem(mIProfcollect, "applaunch");
+ Utils.traceSystem(sIProfcollect, "applaunch");
}
}
}
@@ -336,15 +393,9 @@
}
private void traceOnDex2oatStart() {
- if (mIProfcollect == null) {
- return;
- }
- if (mAdbActive) {
- return;
- }
if (Utils.withFrequency("dex2oat_trace_freq", 25)) {
// Dex2oat could take a while before it starts. Add a short delay before start tracing.
- Utils.traceSystem(mIProfcollect, "dex2oat", /* delayMs */ 1000);
+ Utils.traceSystem(sIProfcollect, "dex2oat", /* delayMs */ 1000);
}
}
@@ -367,12 +418,12 @@
private static void createAndUploadReport(ProfcollectForwardingService pfs) {
BackgroundThread.get().getThreadHandler().post(() -> {
- if (pfs.mIProfcollect == null) {
+ if (pfs.sIProfcollect == null) {
return;
}
String reportName;
try {
- reportName = pfs.mIProfcollect.report(pfs.mUsageSetting) + ".zip";
+ reportName = pfs.sIProfcollect.report(pfs.mUsageSetting) + ".zip";
} catch (RemoteException e) {
Log.e(LOG_TAG, "Failed to create report: " + e.getMessage());
return;
@@ -411,7 +462,7 @@
return;
}
if (Utils.withFrequency("camera_trace_freq", 10)) {
- Utils.traceProcess(mIProfcollect,
+ Utils.traceProcess(sIProfcollect,
"camera",
"android.hardware.camera.provider",
/* durationMs */ 5000);
diff --git a/services/profcollect/src/com/android/server/profcollect/Utils.java b/services/profcollect/src/com/android/server/profcollect/Utils.java
index a8016a0..b754ca1 100644
--- a/services/profcollect/src/com/android/server/profcollect/Utils.java
+++ b/services/profcollect/src/com/android/server/profcollect/Utils.java
@@ -28,28 +28,29 @@
import java.time.Instant;
import java.util.concurrent.ThreadLocalRandom;
-public final class Utils {
+final class Utils {
private static Instant lastTraceTime = Instant.EPOCH;
private static final int TRACE_COOLDOWN_SECONDS = 30;
- public static boolean withFrequency(String configName, int defaultFrequency) {
+ static boolean withFrequency(String configName, int defaultFrequency) {
int threshold = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, configName, defaultFrequency);
int randomNum = ThreadLocalRandom.current().nextInt(100);
return randomNum < threshold;
}
- public static boolean traceSystem(IProfCollectd mIProfcollect, String eventName) {
- if (mIProfcollect == null) {
- return false;
- }
- if (isInCooldownOrReset()) {
+ /**
+ * Request a system-wide trace.
+ * Will be ignored if the device does not meet trace criteria or is being rate limited.
+ */
+ static boolean traceSystem(IProfCollectd iprofcollectd, String eventName) {
+ if (!checkPrerequisites(iprofcollectd)) {
return false;
}
BackgroundThread.get().getThreadHandler().post(() -> {
try {
- mIProfcollect.trace_system(eventName);
+ iprofcollectd.trace_system(eventName);
} catch (RemoteException | ServiceSpecificException e) {
Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
}
@@ -57,16 +58,17 @@
return true;
}
- public static boolean traceSystem(IProfCollectd mIProfcollect, String eventName, int delayMs) {
- if (mIProfcollect == null) {
- return false;
- }
- if (isInCooldownOrReset()) {
+ /**
+ * Request a system-wide trace after a delay.
+ * Will be ignored if the device does not meet trace criteria or is being rate limited.
+ */
+ static boolean traceSystem(IProfCollectd iprofcollectd, String eventName, int delayMs) {
+ if (!checkPrerequisites(iprofcollectd)) {
return false;
}
BackgroundThread.get().getThreadHandler().postDelayed(() -> {
try {
- mIProfcollect.trace_system(eventName);
+ iprofcollectd.trace_system(eventName);
} catch (RemoteException | ServiceSpecificException e) {
Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
}
@@ -74,17 +76,18 @@
return true;
}
- public static boolean traceProcess(IProfCollectd mIProfcollect,
+ /**
+ * Request a single-process trace.
+ * Will be ignored if the device does not meet trace criteria or is being rate limited.
+ */
+ static boolean traceProcess(IProfCollectd iprofcollectd,
String eventName, String processName, int durationMs) {
- if (mIProfcollect == null) {
- return false;
- }
- if (isInCooldownOrReset()) {
+ if (!checkPrerequisites(iprofcollectd)) {
return false;
}
BackgroundThread.get().getThreadHandler().post(() -> {
try {
- mIProfcollect.trace_process(eventName,
+ iprofcollectd.trace_process(eventName,
processName,
durationMs);
} catch (RemoteException | ServiceSpecificException e) {
@@ -105,4 +108,16 @@
}
return true;
}
+
+ private static boolean checkPrerequisites(IProfCollectd iprofcollectd) {
+ if (iprofcollectd == null) {
+ return false;
+ }
+ if (isInCooldownOrReset()) {
+ return false;
+ }
+ return ProfcollectForwardingService.sVerityEnforced
+ && !ProfcollectForwardingService.sAdbActive
+ && ProfcollectForwardingService.sIsInteractive;
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index f40d803..db04d39e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -25,6 +25,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SKIPPED;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
import static com.android.server.RescueParty.DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN;
import static com.android.server.RescueParty.LEVEL_FACTORY_RESET;
@@ -357,11 +359,13 @@
SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true));
assertEquals(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
- sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false);
+ sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
+ MITIGATION_RESULT_SKIPPED);
SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
- assertTrue(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
- sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1));
+ assertEquals(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
+ sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
+ MITIGATION_RESULT_SUCCESS);
}
@Test
@@ -370,7 +374,8 @@
SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true));
assertEquals(RescuePartyObserver.getInstance(mMockContext).onExecuteHealthCheckMitigation(
- sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false);
+ sFailingPackage, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
+ MITIGATION_RESULT_SKIPPED);
// Restore the property value initialized in SetUp()
SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index c878799..4ef602f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -25,6 +25,7 @@
import static com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -34,6 +35,7 @@
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.floatThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
@@ -50,9 +52,11 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.os.Looper;
import android.os.RemoteException;
@@ -60,6 +64,7 @@
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.testing.DexmakerShareClassLoaderRule;
+import android.util.DisplayMetrics;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
@@ -79,6 +84,8 @@
import com.android.server.input.InputManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import com.google.common.truth.Expect;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -96,6 +103,9 @@
@RunWith(AndroidJUnit4.class)
public class MagnificationControllerTest {
+ @Rule
+ public final Expect expect = Expect.create();
+
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
private static final int TEST_SERVICE_ID = 1;
private static final Region INITIAL_SCREEN_MAGNIFICATION_REGION =
@@ -119,6 +129,8 @@
@Mock
private Context mContext;
@Mock
+ private Resources mResources;
+ @Mock
private PackageManager mPackageManager;
@Mock
@@ -156,6 +168,7 @@
@Mock
private DisplayManagerInternal mDisplayManagerInternal;
+ private Display mDisplay;
@Mock
private Scroller mMockScroller;
@@ -210,6 +223,12 @@
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternal);
+ DisplayManager displayManager = new DisplayManager(mContext);
+ mDisplay = displayManager.getDisplay(TEST_DISPLAY);
+ when(mContext.getSystemServiceName(DisplayManager.class)).thenReturn(
+ Context.DISPLAY_SERVICE);
+ when(mContext.getSystemService(DisplayManager.class)).thenReturn(displayManager);
+
mScreenMagnificationController =
spy(
new FullScreenMagnificationController(
@@ -686,16 +705,19 @@
float currentScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
while (currentScale < SCALE_MAX_VALUE) {
- assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
- MagnificationController.ZOOM_DIRECTION_IN)).isTrue();
+ mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_IN);
final float nextScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
assertThat(nextScale).isGreaterThan(currentScale);
currentScale = nextScale;
}
assertThat(currentScale).isEqualTo(SCALE_MAX_VALUE);
- assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
- MagnificationController.ZOOM_DIRECTION_IN)).isFalse();
+ // Trying to scale further does not change the scale.
+ mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_IN);
+ final float finalScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+ assertThat(finalScale).isEqualTo(currentScale);
}
@Test
@@ -706,16 +728,19 @@
float currentScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
while (currentScale > SCALE_MIN_VALUE) {
- assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
- MagnificationController.ZOOM_DIRECTION_OUT)).isTrue();
+ mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_OUT);
final float nextScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
assertThat(nextScale).isLessThan(currentScale);
currentScale = nextScale;
}
assertThat(currentScale).isEqualTo(SCALE_MIN_VALUE);
- assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
- MagnificationController.ZOOM_DIRECTION_OUT)).isFalse();
+ // Trying to scale further does not change the scale.
+ mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_OUT);
+ final float finalScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+ assertThat(finalScale).isEqualTo(currentScale);
}
@Test
@@ -740,6 +765,121 @@
}
@Test
+ public void panMagnificationByStep_fullscreenMode_stepSizeAtScale2() throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ // At scale 2.0f, each step should be about 40 dpi.
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 2.0f, false);
+ reset(mScreenMagnificationController);
+
+ testFullscreenMagnificationPanWithStepSize(40.0f);
+ }
+
+ @Test
+ public void panMagnificationByStep_fullscreenMode_stepSizeAtScale8() throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ // At scale 8.0f, each step should be about 27 dpi.
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false);
+ reset(mScreenMagnificationController);
+
+ testFullscreenMagnificationPanWithStepSize(27.0f);
+ }
+
+ @Test
+ public void panMagnificationByStep_windowMode_stepSizeAtScale2() throws RemoteException {
+ mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, 100f, 200f);
+
+ testWindowMagnificationPanWithStepSize(40.0f);
+ }
+
+ @Test
+ public void panMagnificationByStep_windowMode_stepSizeAtScale8() throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+ // At scale 8.0f, each step should be about 27.
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false);
+ reset(mMagnificationConnectionManager);
+
+ testWindowMagnificationPanWithStepSize(27.0f);
+ }
+
+ @Test
+ public void panMagnificationByStep_fullscreenMode_reachesRightEdgeOfScreen()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ // At scale 2.0f, each step should be about 40.
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, DEFAULT_SCALE, false);
+ reset(mScreenMagnificationController);
+
+ float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+
+ DisplayMetrics metrics = new DisplayMetrics();
+ mDisplay.getMetrics(metrics);
+ float expectedStep = 40.0f * metrics.density;
+
+ // Move right, eventually we should reach the edge.
+ int maxNumSteps = (int) (metrics.widthPixels / expectedStep) + 1;
+ int numSteps = 0;
+ while (numSteps < maxNumSteps) {
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_RIGHT);
+ float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ assertThat(currentCenterY).isEqualTo(newCenterY);
+
+ assertThat(newCenterX).isAtLeast(currentCenterX);
+ if (newCenterX == currentCenterX) {
+ break;
+ }
+
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+ numSteps++;
+ }
+ assertWithMessage("Still not at edge after panning right " + numSteps
+ + " steps. Current position: " + currentCenterX + "," + currentCenterY)
+ .that(numSteps).isLessThan(maxNumSteps);
+ }
+
+ @Test
+ public void panMagnificationByStep_fullscreenMode_reachesBottomEdgeOfScreen()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ // At scale 2.0f, each step should be about 40.
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, DEFAULT_SCALE, false);
+ reset(mScreenMagnificationController);
+
+ float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+
+ DisplayMetrics metrics = new DisplayMetrics();
+ mDisplay.getMetrics(metrics);
+ float expectedStep = 40.0f * metrics.density;
+
+ // Move down, eventually we should reach the edge.
+ int maxNumSteps = (int) (metrics.heightPixels / expectedStep) + 1;
+ int numSteps = 0;
+ while (numSteps < maxNumSteps) {
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_DOWN);
+ float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ assertThat(currentCenterX).isEqualTo(newCenterX);
+
+ assertThat(newCenterY).isAtLeast(currentCenterY);
+ if (newCenterY == currentCenterY) {
+ break;
+ }
+
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+ numSteps++;
+ }
+ assertWithMessage("Still not at edge after panning down "
+ + numSteps + " steps. Current position: " + currentCenterX + "," + currentCenterY)
+ .that(numSteps).isLessThan(maxNumSteps);
+ }
+
+ @Test
public void enableWindowMode_notifyMagnificationChanged() throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
@@ -1425,6 +1565,91 @@
return captor.getValue();
}
+ private void testFullscreenMagnificationPanWithStepSize(float expectedStep) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ mDisplay.getMetrics(metrics);
+ expectedStep *= metrics.density;
+
+ float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+
+ // Move right.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_RIGHT);
+ float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isLessThan(newCenterX);
+ expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
+ expect.that(currentCenterY).isEqualTo(newCenterY);
+
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+
+ // Move left.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_LEFT);
+ newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isGreaterThan(newCenterX);
+ expect.that(currentCenterX - newCenterX).isWithin(0.01f).of(expectedStep);
+ expect.that(currentCenterY).isEqualTo(newCenterY);
+
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+
+ // Move down.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_DOWN);
+ newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isEqualTo(newCenterX);
+ expect.that(currentCenterY).isLessThan(newCenterY);
+ expect.that(newCenterY - currentCenterY).isWithin(0.1f).of(expectedStep);
+
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+
+ // Move up.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_UP);
+ newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isEqualTo(newCenterX);
+ expect.that(currentCenterY).isGreaterThan(newCenterY);
+ expect.that(currentCenterY - newCenterY).isWithin(0.01f).of(expectedStep);
+ }
+
+ private void testWindowMagnificationPanWithStepSize(float expectedStepDip)
+ throws RemoteException {
+ DisplayMetrics metrics = new DisplayMetrics();
+ mDisplay.getMetrics(metrics);
+ final float expectedStep = expectedStepDip * metrics.density;
+
+ // Move right.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_RIGHT);
+ verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
+ floatThat(step -> Math.abs(step - expectedStep) < 0.0001), eq(0.0f));
+
+ // Move left.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_LEFT);
+ verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
+ floatThat(step -> Math.abs(expectedStep - step) < 0.0001), eq(0.0f));
+
+ // Move down.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_DOWN);
+ verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
+ eq(0.0f), floatThat(step -> Math.abs(expectedStep - step) < 0.0001));
+
+ // Move up.
+ mMagnificationController.panMagnificationByStep(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_UP);
+ verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY),
+ eq(0.0f), floatThat(step -> Math.abs(expectedStep - step) < 0.0001));
+ }
+
private static class WindowMagnificationMgrCallbackDelegate implements
MagnificationConnectionManager.Callback {
private MagnificationConnectionManager.Callback mCallback;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 80e86a1..fbd53f7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -3169,7 +3169,7 @@
doThrow(new SecurityException("no access")).when(mUgmInternal)
.checkGrantUriPermission(eq(UID_N_MR1), any(), eq(sound),
- anyInt(), eq(Process.myUserHandle().getIdentifier()));
+ anyInt(), eq(UserHandle.getUserId(UID_N_MR1)));
final NotificationChannel channel = new NotificationChannel("id2", "name2",
NotificationManager.IMPORTANCE_DEFAULT);
@@ -3189,7 +3189,7 @@
doThrow(new SecurityException("no access")).when(mUgmInternal)
.checkGrantUriPermission(eq(UID_N_MR1), any(), any(),
- anyInt(), eq(Process.myUserHandle().getIdentifier()));
+ anyInt(), eq(UserHandle.getUserId(UID_N_MR1)));
final NotificationChannel channel = new NotificationChannel("id2", "name2",
NotificationManager.IMPORTANCE_DEFAULT);
@@ -3208,7 +3208,7 @@
doThrow(new SecurityException("no access")).when(mUgmInternal)
.checkGrantUriPermission(eq(UID_N_MR1), any(), any(),
- anyInt(), eq(Process.myUserHandle().getIdentifier()));
+ anyInt(), eq(UserHandle.getUserId(UID_N_MR1)));
final NotificationChannel channel = new NotificationChannel("id2", "name2",
NotificationManager.IMPORTANCE_DEFAULT);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
index 09ed9ba..90bf5f0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
@@ -302,15 +302,15 @@
}
@Test
- public void testOverrideOrientationIfNeeded_userFullscreenOverride_returnsUser() {
+ public void testOverrideOrientationIfNeeded_userFullscreenOverride_notLetterboxed_unchanged() {
runTestScenarioWithActivity((robot) -> {
robot.applyOnActivity((a) -> {
a.setShouldApplyUserFullscreenOverride(true);
a.setIgnoreOrientationRequest(true);
});
- robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
- /* expected */ SCREEN_ORIENTATION_USER);
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_LOCKED,
+ /* expected */ SCREEN_ORIENTATION_LOCKED);
});
}
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index fafb0e0..4959cb3 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -782,6 +782,54 @@
KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
+ TestData(
+ "META + ALT + 'Down' -> Magnification Pan Down",
+ intArrayOf(
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_DPAD_DOWN
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_DOWN,
+ intArrayOf(KeyEvent.KEYCODE_DPAD_DOWN),
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + ALT + 'Up' -> Magnification Pan Up",
+ intArrayOf(
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_DPAD_UP
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_UP,
+ intArrayOf(KeyEvent.KEYCODE_DPAD_UP),
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + ALT + 'Left' -> Magnification Pan Left",
+ intArrayOf(
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_DPAD_LEFT
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_LEFT,
+ intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT),
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + ALT + 'Right' -> Magnification Pan Right",
+ intArrayOf(
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_DPAD_RIGHT
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_PAN_RIGHT,
+ intArrayOf(KeyEvent.KEYCODE_DPAD_RIGHT),
+ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
)
}
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index c64dc72..928e232 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -19,6 +19,7 @@
import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
import static com.google.common.truth.Truth.assertThat;
@@ -1933,12 +1934,12 @@
return mImpact;
}
- public boolean onExecuteHealthCheckMitigation(VersionedPackage versionedPackage,
+ public int onExecuteHealthCheckMitigation(VersionedPackage versionedPackage,
int failureReason, int mitigationCount) {
mMitigatedPackages.add(versionedPackage.getPackageName());
mMitigationCounts.add(mitigationCount);
mLastFailureReason = failureReason;
- return true;
+ return MITIGATION_RESULT_SUCCESS;
}
public String getUniqueIdentifier() {
@@ -1957,11 +1958,10 @@
return mImpact;
}
- public boolean onExecuteBootLoopMitigation(int level) {
- Slog.w("hrm1243", "I'm here " + level);
+ public int onExecuteBootLoopMitigation(int level) {
mMitigatedBootLoop = true;
mBootMitigationCounts.add(level);
- return true;
+ return MITIGATION_RESULT_SUCCESS;
}
public boolean mitigatedBootLoop() {