Merge "Implementing a power scale in SliderHapticFeedbackProvider." into main
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index e3a5520..3ab889d 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -3317,7 +3317,7 @@
* stream configurations are the same as for applications targeting SDK version older than
* 31.</p>
* <p>Refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} and
- * {@link android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations }
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#legacy-level-guaranteed-configurations">the table</a>
* for additional mandatory stream configurations on a per-capability basis.</p>
* <p>*1: For JPEG format, the sizes may be restricted by below conditions:</p>
* <ul>
@@ -3436,11 +3436,12 @@
* {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL }
* and {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES }.
* This is an app-readable conversion of the mandatory stream combination
- * {@link android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations tables}.</p>
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#legacy-level-guaranteed-configurations">tables</a>.</p>
* <p>The array of
* {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
* generated according to the documented
- * {@link android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations guideline} based on specific device level and capabilities.
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#legacy-level-guaranteed-configurations">guideline</a>.
+ * based on specific device level and capabilities.
* Clients can use the array as a quick reference to find an appropriate camera stream
* combination.
* As per documentation, the stream combinations with given PREVIEW, RECORD and
@@ -3469,11 +3470,12 @@
/**
* <p>An array of mandatory concurrent stream combinations.
* This is an app-readable conversion of the concurrent mandatory stream combination
- * {@link android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations tables}.</p>
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#concurrent-stream-guaranteed-configurations">tables</a>.</p>
* <p>The array of
* {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
* generated according to the documented
- * {@link android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations guideline} for each device which has its Id present in the set returned by
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#concurrent-stream-guaranteed-configurations">guideline</a>
+ * for each device which has its Id present in the set returned by
* {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds }.
* Clients can use the array as a quick reference to find an appropriate camera stream
* combination.
@@ -3578,7 +3580,7 @@
* <p>If a camera device supports multi-resolution output streams for a particular format, for
* each of its mandatory stream combinations, the camera device will support using a
* MultiResolutionImageReader for the MAXIMUM stream of supported formats. Refer to
- * {@link android.hardware.camera2.CameraDevice#legacy-level-additional-guaranteed-combinations-with-multiresolutionoutputs }
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#legacy-level-additional-guaranteed-combinations-with-multiresolutionoutputs">the table</a>
* for additional details.</p>
* <p>To use multi-resolution input streams, the supported formats can be queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getInputFormats }.
* A reprocessable CameraCaptureSession can then be created using an {@link android.hardware.camera2.params.InputConfiguration InputConfiguration} constructed with
@@ -3587,7 +3589,7 @@
* {@code YUV} output, or multi-resolution {@code PRIVATE} input and multi-resolution
* {@code PRIVATE} output, {@code JPEG} and {@code YUV} are guaranteed to be supported
* multi-resolution output stream formats. Refer to
- * {@link android.hardware.camera2.CameraDevice#legacy-level-additional-guaranteed-combinations-with-multiresolutionoutputs }}
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#legacy-level-additional-guaranteed-combinations-with-multiresolutionoutputs">the table</a>
* for details about the additional mandatory stream combinations in this case.</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
*/
@@ -3700,11 +3702,12 @@
* {@link android.hardware.camera2.CaptureRequest } has {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} set
* to {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.
* This is an app-readable conversion of the maximum resolution mandatory stream combination
- * {@link android.hardware.camera2.CameraDevice#additional-guaranteed-combinations-for-ultra-high-resolution-sensors tables}.</p>
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#additional-guaranteed-combinations-for-ultra-high-resolution-sensors">tables</a>.</p>
* <p>The array of
* {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
* generated according to the documented
- * {@link android.hardware.camera2.CameraDevice#additional-guaranteed-combinations-for-ultra-high-resolution-sensors guideline} for each device which has the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#additional-guaranteed-combinations-for-ultra-high-resolution-sensors">guideline</a>
+ * for each device which has the
* {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR }
* capability.
* Clients can use the array as a quick reference to find an appropriate camera stream
@@ -3727,11 +3730,12 @@
* 10-bit output capability
* {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
* This is an app-readable conversion of the 10 bit output mandatory stream combination
- * {@link android.hardware.camera2.CameraDevice#10-bit-output-additional-guaranteed-configurations tables}.</p>
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#10-bit-output-additional-guaranteed-configurations">tables</a>.</p>
* <p>The array of
* {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
* generated according to the documented
- * {@link android.hardware.camera2.CameraDevice#10-bit-output-additional-guaranteed-configurations guideline} for each device which has the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#10-bit-output-additional-guaranteed-configurations">guideline</a>
+ * for each device which has the
* {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
* capability.
* Clients can use the array as a quick reference to find an appropriate camera stream
@@ -3752,11 +3756,12 @@
* {@code PREVIEW_STABILIZATION} in {@link CameraCharacteristics#CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES android.control.availableVideoStabilizationModes}.
* This is an app-readable conversion of the preview stabilization mandatory stream
* combination
- * {@link android.hardware.camera2.CameraDevice#preview-stabilization-guaranteed-stream-configurations tables}.</p>
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#preview-stabilization-guaranteed-stream-configurations">tables</a>.</p>
* <p>The array of
* {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
* generated according to the documented
- * {@link android.hardware.camera2.CameraDevice#preview-stabilization-guaranteed-stream-configurations guideline} for each device which supports {@code PREVIEW_STABILIZATION}
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#preview-stabilization-guaranteed-stream-configurations">guideline</a>
+ * for each device which supports {@code PREVIEW_STABILIZATION}
* Clients can use the array as a quick reference to find an appropriate camera stream
* combination.
* The mandatory stream combination array will be {@code null} in case the device does not
@@ -3829,8 +3834,8 @@
* <p>The guaranteed stream combinations related to stream use case for a camera device with
* {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE }
* capability is documented in the camera device
- * {@link android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations guideline}. The application is strongly recommended to use one of the guaranteed stream
- * combinations.
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#stream-use-case-capability-additional-guaranteed-configurations">guideline</a>.
+ * The application is strongly recommended to use one of the guaranteed stream combinations.
* If the application creates a session with a stream combination not in the guaranteed
* list, or with mixed DEFAULT and non-DEFAULT use cases within the same session,
* the camera device may ignore some stream use cases due to hardware constraints
@@ -3866,11 +3871,13 @@
/**
* <p>An array of mandatory stream combinations with stream use cases.
* This is an app-readable conversion of the mandatory stream combination
- * {@link android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations tables} with each stream's use case being set.</p>
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#stream-use-case-capability-additional-guaranteed-configurations">tables</a>
+ * with each stream's use case being set.</p>
* <p>The array of
* {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
* generated according to the documented
- * {@link android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations guideline} for a camera device with
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#stream-use-case-capability-additional-guaranteed-configurations">guildeline</a>
+ * for a camera device with
* {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE }
* capability.
* The mandatory stream combination array will be {@code null} in case the device doesn't
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 765a8f7..93fbe8a 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -1216,8 +1216,7 @@
* <ul>
* <li>Profile {@link android.hardware.camera2.params.DynamicRangeProfiles#HLG10 }</li>
* <li>All mandatory stream combinations for this specific capability as per
- * documentation
- * {@link android.hardware.camera2.CameraDevice#10-bit-output-additional-guaranteed-configurations }</li>
+ * <a href="CameraDevice#10-bit-output-additional-guaranteed-configurations">documentation</a></li>
* <li>In case the device is not able to capture some combination of supported
* standard 8-bit and/or 10-bit dynamic range profiles within the same capture request,
* then those constraints must be listed in
@@ -1256,8 +1255,8 @@
* </ul>
* <p>{@link android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES }
* lists all of the supported stream use cases.</p>
- * <p>Refer to
- * {@link android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations }
+ * <p>Refer to the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#stream-use-case-capability-additional-guaranteed-configurations">guideline</a>
* for the mandatory stream combinations involving stream use cases, which can also be
* queried via {@link android.hardware.camera2.params.MandatoryStreamCombination }.</p>
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
@@ -1756,9 +1755,9 @@
/**
* <p>This camera device does not have enough capabilities to qualify as a <code>FULL</code> device or
* better.</p>
- * <p>Only the stream configurations listed in the <code>LEGACY</code> and <code>LIMITED</code> tables in the
- * {@link android.hardware.camera2.CameraDevice#limited-level-additional-guaranteed-configurations }
- * documentation are guaranteed to be supported.</p>
+ * <p>Only the stream configurations listed in the <code>LEGACY</code> and <code>LIMITED</code>
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#limited-level-additional-guaranteed-configurations">tables</a>
+ * in the documentation are guaranteed to be supported.</p>
* <p>All <code>LIMITED</code> devices support the <code>BACKWARDS_COMPATIBLE</code> capability, indicating basic
* support for color image capture. The only exception is that the device may
* alternatively support only the <code>DEPTH_OUTPUT</code> capability, if it can only output depth
@@ -1784,9 +1783,9 @@
/**
* <p>This camera device is capable of supporting advanced imaging applications.</p>
- * <p>The stream configurations listed in the <code>FULL</code>, <code>LEGACY</code> and <code>LIMITED</code> tables in the
- * {@link android.hardware.camera2.CameraDevice#full-level-additional-guaranteed-configurations }
- * documentation are guaranteed to be supported.</p>
+ * <p>The stream configurations listed in the <code>FULL</code>, <code>LEGACY</code> and <code>LIMITED</code>
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#full-level-additional-guaranteed-configurations">tables</a>
+ * in the documentation are guaranteed to be supported.</p>
* <p>A <code>FULL</code> device will support below capabilities:</p>
* <ul>
* <li><code>BURST_CAPTURE</code> capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
@@ -1814,9 +1813,9 @@
/**
* <p>This camera device is running in backward compatibility mode.</p>
- * <p>Only the stream configurations listed in the <code>LEGACY</code> table in the
- * {@link android.hardware.camera2.CameraDevice#legacy-level-guaranteed-configurations }
- * documentation are supported.</p>
+ * <p>Only the stream configurations listed in the <code>LEGACY</code>
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#legacy-level-guaranteed-configurations">table</a>
+ * in the documentation are supported.</p>
* <p>A <code>LEGACY</code> device does not support per-frame control, manual sensor control, manual
* post-processing, arbitrary cropping regions, and has relaxed performance constraints.
* No additional capabilities beyond <code>BACKWARD_COMPATIBLE</code> will ever be listed by a
@@ -1839,9 +1838,9 @@
* <p>This camera device is capable of YUV reprocessing and RAW data capture, in addition to
* FULL-level capabilities.</p>
* <p>The stream configurations listed in the <code>LEVEL_3</code>, <code>RAW</code>, <code>FULL</code>, <code>LEGACY</code> and
- * <code>LIMITED</code> tables in the
- * {@link android.hardware.camera2.CameraDevice#level-3-additional-guaranteed-configurations }
- * documentation are guaranteed to be supported.</p>
+ * <code>LIMITED</code>
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#level-3-additional-guaranteed-configurations">tables</a>
+ * in the documentation are guaranteed to be supported.</p>
* <p>The following additional capabilities are guaranteed to be supported:</p>
* <ul>
* <li><code>YUV_REPROCESSING</code> capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 8e49c4c..134a510 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -403,14 +403,13 @@
/**
* Virtual display flag: Indicates that the display should support system decorations. Virtual
- * displays without this flag shouldn't show home, IME or any other system decorations.
+ * displays without this flag shouldn't show home, navigation bar or wallpaper.
* <p>This flag doesn't work without {@link #VIRTUAL_DISPLAY_FLAG_TRUSTED}</p>
*
* @see #createVirtualDisplay
* @see #VIRTUAL_DISPLAY_FLAG_TRUSTED
* @hide
*/
- // TODO (b/114338689): Remove the flag and use IWindowManager#setShouldShowSystemDecors
@TestApi
public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 9;
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 7388b5b..9e09759 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -447,7 +447,8 @@
* Sets whether this display supports showing home activities and wallpaper.
*
* <p>If set to {@code true}, then the home activity relevant to this display will be
- * automatically launched upon the display creation.</p>
+ * automatically launched upon the display creation. If unset or set to {@code false}, the
+ * display will not host any activities upon creation.</p>
*
* <p>Note: setting to {@code true} requires the display to be trusted. If the display is
* not trusted, this property is ignored.</p>
diff --git a/core/java/android/service/notification/DeviceEffectsApplier.java b/core/java/android/service/notification/DeviceEffectsApplier.java
index 234ff4d..5194cdd 100644
--- a/core/java/android/service/notification/DeviceEffectsApplier.java
+++ b/core/java/android/service/notification/DeviceEffectsApplier.java
@@ -16,6 +16,8 @@
package android.service.notification;
+import android.service.notification.ZenModeConfig.ConfigChangeOrigin;
+
/**
* Responsible for making any service calls needed to apply the set of {@link ZenDeviceEffects} that
* make sense for the current platform.
@@ -33,6 +35,13 @@
*
* <p>This will be called whenever the set of consolidated effects changes (normally through
* the activation or deactivation of zen rules).
+ *
+ * @param effects The effects that should be active and inactive.
+ * @param source The origin of the change. Because the application of specific effects can be
+ * disruptive (e.g. lead to Activity recreation), that operation can in some
+ * cases be deferred (e.g. until screen off). However, if the effects are
+ * changing as a result of an explicit user action, then it makes sense to
+ * apply them immediately regardless.
*/
- void apply(ZenDeviceEffects effects);
+ void apply(ZenDeviceEffects effects, @ConfigChangeOrigin int source);
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index f6128ea..a5b087c 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -26,6 +26,7 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AlarmManager;
@@ -61,6 +62,8 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
@@ -79,7 +82,66 @@
* @hide
*/
public class ZenModeConfig implements Parcelable {
- private static String TAG = "ZenModeConfig";
+ private static final String TAG = "ZenModeConfig";
+
+ /**
+ * The {@link ZenModeConfig} is being updated because of an unknown reason.
+ */
+ public static final int UPDATE_ORIGIN_UNKNOWN = 0;
+
+ /**
+ * The {@link ZenModeConfig} is being updated because of system initialization (i.e. load from
+ * storage, on device boot).
+ */
+ public static final int UPDATE_ORIGIN_INIT = 1;
+
+ /** The {@link ZenModeConfig} is being updated (replaced) because of a user switch or unlock. */
+ public static final int UPDATE_ORIGIN_INIT_USER = 2;
+
+ /** The {@link ZenModeConfig} is being updated because of a user action, for example:
+ * <ul>
+ * <li>{@link NotificationManager#setAutomaticZenRuleState} with a
+ * {@link Condition#source} equal to {@link Condition#SOURCE_USER_ACTION}.</li>
+ * <li>Adding, updating, or removing a rule from Settings.</li>
+ * <li>Directly activating or deactivating/snoozing a rule through some UI affordance (e.g.
+ * Quick Settings).</li>
+ * </ul>
+ */
+ public static final int UPDATE_ORIGIN_USER = 3;
+
+ /**
+ * The {@link ZenModeConfig} is being "independently" updated by an app, and not as a result of
+ * a user's action inside that app (for example, activating an {@link AutomaticZenRule} based on
+ * a previously set schedule).
+ */
+ public static final int UPDATE_ORIGIN_APP = 4;
+
+ /**
+ * The {@link ZenModeConfig} is being updated by the System or SystemUI. Note that this only
+ * includes cases where the call is coming from the System/SystemUI but the change is not due to
+ * a user action (e.g. automatically activating a schedule-based rule). If the change is a
+ * result of a user action (e.g. activating a rule by tapping on its QS tile) then
+ * {@link #UPDATE_ORIGIN_USER} is used instead.
+ */
+ public static final int UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI = 5;
+
+ /**
+ * The {@link ZenModeConfig} is being updated (replaced) because the user's DND configuration
+ * is being restored from a backup.
+ */
+ public static final int UPDATE_ORIGIN_RESTORE_BACKUP = 6;
+
+ @IntDef(prefix = { "UPDATE_ORIGIN_" }, value = {
+ UPDATE_ORIGIN_UNKNOWN,
+ UPDATE_ORIGIN_INIT,
+ UPDATE_ORIGIN_INIT_USER,
+ UPDATE_ORIGIN_USER,
+ UPDATE_ORIGIN_APP,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+ UPDATE_ORIGIN_RESTORE_BACKUP
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ConfigChangeOrigin {}
public static final int SOURCE_ANYONE = Policy.PRIORITY_SENDERS_ANY;
public static final int SOURCE_CONTACT = Policy.PRIORITY_SENDERS_CONTACTS;
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 07dd882..1908c64c 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -276,15 +276,14 @@
/**
* Display flag: Indicates that the display should show system decorations.
* <p>
- * This flag identifies secondary displays that should show system decorations, such as status
- * bar, navigation bar, home activity or IME.
+ * This flag identifies secondary displays that should show system decorations, such as
+ * navigation bar, home activity or wallpaper.
* </p>
* <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p>
*
* @see #getFlags()
* @hide
*/
- // TODO (b/114338689): Remove the flag and use IWindowManager#setShouldShowSystemDecors
public static final int FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 6;
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 8eb4a5a..f5c01d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -770,6 +770,7 @@
}
if (mContentOverlay != null) {
mContentOverlay.onAnimationEnd(tx, destBounds);
+ clearContentOverlay();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
index 850b06a0..a2bd47c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
@@ -109,7 +109,7 @@
@Override
public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) {
- // Do nothing. Color overlay should be fully opaque by now.
+ // Do nothing. Color overlay should be fully opaque by now, ready for fade out.
}
private float[] getContentOverlayColor(Context context) {
@@ -167,7 +167,7 @@
@Override
public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) {
- atomicTx.remove(mLeash);
+ // Do nothing. Snapshot overlay should be fully opaque by now, ready for fade out.
}
}
@@ -193,19 +193,12 @@
MAX_APP_ICON_SIZE_DP, context.getResources().getDisplayMetrics());
mAppIconSizePx = Math.min(maxAppIconSizePx, appIconSizePx);
- final int appWidth = appBounds.width();
- final int appHeight = appBounds.height();
-
- // In order to have the overlay always cover the pip window during the transition, the
- // overlay will be drawn with the max size of the start and end bounds in different
- // rotation.
- final int overlaySize = Math.max(Math.max(appWidth, appHeight),
- Math.max(destinationBounds.width(), destinationBounds.height())) + 1;
+ final int overlaySize = getOverlaySize(appBounds, destinationBounds);
mOverlayHalfSize = overlaySize >> 1;
// When the activity is in the secondary split, make sure the scaling center is not
// offset.
- mAppBounds = new Rect(0, 0, appWidth, appHeight);
+ mAppBounds = new Rect(0, 0, appBounds.width(), appBounds.height());
mBitmap = Bitmap.createBitmap(overlaySize, overlaySize, Bitmap.Config.ARGB_8888);
prepareAppIconOverlay(appIcon);
@@ -215,6 +208,21 @@
.build();
}
+ /**
+ * Returns the size of the app icon overlay.
+ *
+ * In order to have the overlay always cover the pip window during the transition,
+ * the overlay will be drawn with the max size of the start and end bounds in different
+ * rotation.
+ */
+ public static int getOverlaySize(Rect appBounds, Rect destinationBounds) {
+ final int appWidth = appBounds.width();
+ final int appHeight = appBounds.height();
+
+ return Math.max(Math.max(appWidth, appHeight),
+ Math.max(destinationBounds.width(), destinationBounds.height())) + 1;
+ }
+
@Override
public void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash) {
tx.show(mLeash);
@@ -248,7 +256,7 @@
@Override
public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) {
- atomicTx.remove(mLeash);
+ // Do nothing. Icon overlay should be fully opaque by now, ready for fade out.
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index c1164fc..743b1ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -156,74 +156,77 @@
// These callbacks are called on the update thread
private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
new PipAnimationController.PipAnimationCallback() {
- private boolean mIsCancelled;
- @Override
- public void onPipAnimationStart(TaskInfo taskInfo,
- PipAnimationController.PipTransitionAnimator animator) {
- final int direction = animator.getTransitionDirection();
- mIsCancelled = false;
- sendOnPipTransitionStarted(direction);
- }
+ private boolean mIsCancelled;
- @Override
- public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx,
- PipAnimationController.PipTransitionAnimator animator) {
- final int direction = animator.getTransitionDirection();
- if (mIsCancelled) {
- sendOnPipTransitionFinished(direction);
- maybePerformFinishResizeCallback();
- return;
- }
- final int animationType = animator.getAnimationType();
- final Rect destinationBounds = animator.getDestinationBounds();
- if (isInPipDirection(direction) && animator.getContentOverlayLeash() != null) {
- fadeOutAndRemoveOverlay(animator.getContentOverlayLeash(),
- animator::clearContentOverlay, true /* withStartDelay*/);
- }
- if (mWaitForFixedRotation && animationType == ANIM_TYPE_BOUNDS
- && direction == TRANSITION_DIRECTION_TO_PIP) {
- // Notify the display to continue the deferred orientation change.
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.scheduleFinishEnterPip(mToken, destinationBounds);
- mTaskOrganizer.applyTransaction(wct);
- // The final task bounds will be applied by onFixedRotationFinished so that all
- // coordinates are in new rotation.
- mSurfaceTransactionHelper.round(tx, mLeash, isInPip());
- mDeferredAnimEndTransaction = tx;
- return;
- }
- final boolean isExitPipDirection = isOutPipDirection(direction)
- || isRemovePipDirection(direction);
- if (mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP
- || isExitPipDirection) {
- // execute the finish resize callback if needed after the transaction is committed
- tx.addTransactionCommittedListener(mMainExecutor,
- PipTaskOrganizer.this::maybePerformFinishResizeCallback);
+ @Override
+ public void onPipAnimationStart(TaskInfo taskInfo,
+ PipAnimationController.PipTransitionAnimator animator) {
+ final int direction = animator.getTransitionDirection();
+ mIsCancelled = false;
+ sendOnPipTransitionStarted(direction);
+ }
- // Finish resize as long as we're not exiting PIP, or, if we are, only if this is
- // the end of an exit PIP animation.
- // This is necessary in case there was a resize animation ongoing when exit PIP
- // started, in which case the first resize will be skipped to let the exit
- // operation handle the final resize out of PIP mode. See b/185306679.
- finishResizeDelayedIfNeeded(() -> {
- finishResize(tx, destinationBounds, direction, animationType);
- sendOnPipTransitionFinished(direction);
- });
- }
- }
+ @Override
+ public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx,
+ PipAnimationController.PipTransitionAnimator animator) {
+ final int direction = animator.getTransitionDirection();
+ if (mIsCancelled) {
+ sendOnPipTransitionFinished(direction);
+ maybePerformFinishResizeCallback();
+ return;
+ }
+ final int animationType = animator.getAnimationType();
+ final Rect destinationBounds = animator.getDestinationBounds();
+ if (isInPipDirection(direction) && mPipOverlay != null) {
+ fadeOutAndRemoveOverlay(mPipOverlay,
+ null /* callback */, true /* withStartDelay*/);
+ }
+ if (mWaitForFixedRotation && animationType == ANIM_TYPE_BOUNDS
+ && direction == TRANSITION_DIRECTION_TO_PIP) {
+ // Notify the display to continue the deferred orientation change.
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.scheduleFinishEnterPip(mToken, destinationBounds);
+ mTaskOrganizer.applyTransaction(wct);
+ // The final task bounds will be applied by onFixedRotationFinished so
+ // that all coordinates are in new rotation.
+ mSurfaceTransactionHelper.round(tx, mLeash, isInPip());
+ mDeferredAnimEndTransaction = tx;
+ return;
+ }
+ final boolean isExitPipDirection = isOutPipDirection(direction)
+ || isRemovePipDirection(direction);
+ if (mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP
+ || isExitPipDirection) {
+ // execute the finish resize callback if needed after the transaction is
+ // committed
+ tx.addTransactionCommittedListener(mMainExecutor,
+ PipTaskOrganizer.this::maybePerformFinishResizeCallback);
- @Override
- public void onPipAnimationCancel(TaskInfo taskInfo,
- PipAnimationController.PipTransitionAnimator animator) {
- final int direction = animator.getTransitionDirection();
- mIsCancelled = true;
- if (isInPipDirection(direction) && animator.getContentOverlayLeash() != null) {
- fadeOutAndRemoveOverlay(animator.getContentOverlayLeash(),
- animator::clearContentOverlay, true /* withStartDelay */);
- }
- sendOnPipTransitionCancelled(direction);
- }
- };
+ // Finish resize as long as we're not exiting PIP, or, if we are, only if
+ // this is the end of an exit PIP animation.
+ // This is necessary in case there was a resize animation ongoing when
+ // exit PIP started, in which case the first resize will be skipped to
+ // let the exit operation handle the final resize out of PIP mode.
+ // See b/185306679.
+ finishResizeDelayedIfNeeded(() -> {
+ finishResize(tx, destinationBounds, direction, animationType);
+ sendOnPipTransitionFinished(direction);
+ });
+ }
+ }
+
+ @Override
+ public void onPipAnimationCancel(TaskInfo taskInfo,
+ PipAnimationController.PipTransitionAnimator animator) {
+ final int direction = animator.getTransitionDirection();
+ mIsCancelled = true;
+ if (isInPipDirection(direction) && mPipOverlay != null) {
+ fadeOutAndRemoveOverlay(mPipOverlay,
+ null /* callback */, true /* withStartDelay */);
+ }
+ sendOnPipTransitionCancelled(direction);
+ }
+ };
/**
* Finishes resizing the PiP, delaying the operation if it has to be synced with the PiP menu.
@@ -327,11 +330,18 @@
/**
* An optional overlay used to mask content changing between an app in/out of PiP, only set if
- * {@link PipTransitionState#getInSwipePipToHomeTransition()} is true.
+ * {@link PipTransitionState#getInSwipePipToHomeTransition()} is true, only in gesture nav.
*/
@Nullable
SurfaceControl mSwipePipToHomeOverlay;
+ /**
+ * An optional overlay used to mask content changing between an app in/out of PiP, only set if
+ * {@link PipTransitionState#getInSwipePipToHomeTransition()} is false.
+ */
+ @Nullable
+ SurfaceControl mPipOverlay;
+
public PipTaskOrganizer(Context context,
@NonNull SyncTransactionQueue syncTransactionQueue,
@NonNull PipTransitionState pipTransitionState,
@@ -1766,6 +1776,7 @@
animator.setSnapshotContentOverlay(snapshot, sourceHintRect);
}
}
+ mPipOverlay = animator.getContentOverlayLeash();
// The destination bounds are used for the end rect of animation and the final bounds
// after animation finishes. So after the animation is started, the destination bounds
// can be updated to new rotation (computeRotatedBounds has changed the DisplayLayout
@@ -1879,6 +1890,14 @@
}
private void removeContentOverlay(SurfaceControl surface, Runnable callback) {
+ if (mPipOverlay != null) {
+ if (mPipOverlay != surface) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: trying to remove overlay (%s) which is not local reference (%s)",
+ TAG, surface, mPipOverlay);
+ }
+ mPipOverlay = null;
+ }
if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) {
// Avoid double removal, which is fatal.
return;
@@ -1907,11 +1926,11 @@
private void cancelCurrentAnimator() {
final PipAnimationController.PipTransitionAnimator<?> animator =
mPipAnimationController.getCurrentAnimator();
+ // remove any overlays if present
+ if (mPipOverlay != null) {
+ removeContentOverlay(mPipOverlay, null /* callback */);
+ }
if (animator != null) {
- if (animator.getContentOverlayLeash() != null) {
- removeContentOverlay(animator.getContentOverlayLeash(),
- animator::clearContentOverlay);
- }
PipAnimationController.quietCancel(animator);
mPipAnimationController.resetAnimatorState();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index fe4980a..0f3c162 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -43,6 +43,7 @@
import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
import android.animation.Animator;
+import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.Context;
@@ -76,6 +77,8 @@
import com.android.wm.shell.util.TransitionUtil;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Optional;
/**
@@ -86,6 +89,18 @@
private static final String TAG = PipTransition.class.getSimpleName();
+ /** No fixed rotation, or fixed rotation state is undefined. */
+ private static final int FIXED_ROTATION_UNDEFINED = 0;
+ /**
+ * Fixed rotation detected via callbacks (see PipController#startSwipePipToHome());
+ * this is used in the swipe PiP to home case, since the transitions itself isn't supposed to
+ * see the fixed rotation.
+ */
+ private static final int FIXED_ROTATION_CALLBACK = 1;
+
+ /** Fixed rotation detected in the incoming transition. */
+ private static final int FIXED_ROTATION_TRANSITION = 2;
+
private final Context mContext;
private final PipTransitionState mPipTransitionState;
private final PipDisplayLayoutState mPipDisplayLayoutState;
@@ -106,17 +121,28 @@
/** The Task window that is currently in PIP windowing mode. */
@Nullable
private WindowContainerToken mCurrentPipTaskToken;
- /** Whether display is in fixed rotation. */
- private boolean mInFixedRotation;
+
+ @IntDef(prefix = { "FIXED_ROTATION_" }, value = {
+ FIXED_ROTATION_UNDEFINED,
+ FIXED_ROTATION_CALLBACK,
+ FIXED_ROTATION_TRANSITION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FixedRotationState {}
+
+ /** Fixed rotation state of the display. */
+ private @FixedRotationState int mFixedRotationState = FIXED_ROTATION_UNDEFINED;
/**
* The rotation that the display will apply after expanding PiP to fullscreen. This is only
- * meaningful if {@link #mInFixedRotation} is true.
+ * meaningful if {@link #mFixedRotationState} is {@link #FIXED_ROTATION_TRANSITION}.
*/
@Surface.Rotation
private int mEndFixedRotation;
/** Whether the PIP window has fade out for fixed rotation. */
private boolean mHasFadeOut;
+ private Rect mInitBounds = new Rect();
+
/** Used for setting transform to a transaction from animator. */
private final PipAnimationController.PipTransactionHandler mTransactionConsumer =
new PipAnimationController.PipTransactionHandler() {
@@ -181,8 +207,16 @@
@NonNull Transitions.TransitionFinishCallback finishCallback) {
final TransitionInfo.Change currentPipTaskChange = findCurrentPipTaskChange(info);
final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
- mInFixedRotation = fixedRotationChange != null;
- mEndFixedRotation = mInFixedRotation
+ if (mFixedRotationState == FIXED_ROTATION_TRANSITION) {
+ // If we are just about to process potential fixed rotation information,
+ // then fixed rotation state should either be UNDEFINED or CALLBACK.
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "%s: startAnimation() should start with clear fixed rotation state", TAG);
+ mFixedRotationState = FIXED_ROTATION_UNDEFINED;
+ }
+ mFixedRotationState = fixedRotationChange != null
+ ? FIXED_ROTATION_TRANSITION : mFixedRotationState;
+ mEndFixedRotation = mFixedRotationState == FIXED_ROTATION_TRANSITION
? fixedRotationChange.getEndFixedRotation()
: ROTATION_UNDEFINED;
@@ -347,6 +381,10 @@
@Override
public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
@Nullable SurfaceControl.Transaction finishT) {
+ // Transition either finished pre-emptively, got merged, or aborted,
+ // so update fixed rotation state to default.
+ mFixedRotationState = FIXED_ROTATION_UNDEFINED;
+
if (transition != mExitTransition) {
return;
}
@@ -408,7 +446,8 @@
// done at the start. But if it is running fixed rotation, there will be a seamless
// display transition later. So the last rotation transform needs to be kept to
// avoid flickering, and then the display transition will reset the transform.
- if (!mInFixedRotation && mFinishTransaction != null) {
+ if (mFixedRotationState != FIXED_ROTATION_TRANSITION
+ && mFinishTransaction != null) {
mFinishTransaction.merge(tx);
}
} else {
@@ -426,12 +465,27 @@
mSurfaceTransactionHelper.crop(tx, leash, destinationBounds)
.resetScale(tx, leash, destinationBounds)
.round(tx, leash, true /* applyCornerRadius */);
+ if (mPipOrganizer.mSwipePipToHomeOverlay != null && !mInitBounds.isEmpty()) {
+ // Resetting the scale for pinned task while re-adjusting its crop,
+ // also scales the overlay. So we need to update the overlay leash too.
+ Rect overlayBounds = new Rect(destinationBounds);
+ final int overlaySize = PipContentOverlay.PipAppIconOverlay
+ .getOverlaySize(mInitBounds, destinationBounds);
+
+ overlayBounds.offsetTo(
+ (destinationBounds.width() - overlaySize) / 2,
+ (destinationBounds.height() - overlaySize) / 2);
+ mSurfaceTransactionHelper.resetScale(tx,
+ mPipOrganizer.mSwipePipToHomeOverlay, overlayBounds);
+ }
}
+ mInitBounds.setEmpty();
wct.setBoundsChangeTransaction(taskInfo.token, tx);
}
final int displayRotation = taskInfo.getConfiguration().windowConfiguration
.getDisplayRotation();
- if (enteringPip && mInFixedRotation && mEndFixedRotation != displayRotation
+ if (enteringPip && mFixedRotationState == FIXED_ROTATION_TRANSITION
+ && mEndFixedRotation != displayRotation
&& hasValidLeash) {
// Launcher may update the Shelf height during the animation, which will update the
// destination bounds. Because this is in fixed rotation, We need to make sure the
@@ -451,6 +505,8 @@
mFinishTransaction = null;
callFinishCallback(wct);
}
+ // This is the end of transition on the Shell side so update the fixed rotation state.
+ mFixedRotationState = FIXED_ROTATION_UNDEFINED;
finishResizeForMenu(destinationBounds);
}
@@ -467,6 +523,7 @@
// mFinishCallback might be null with an outdated mCurrentPipTaskToken
// for example, when app crashes while in PiP and exit transition has not started
mCurrentPipTaskToken = null;
+ mFixedRotationState = FIXED_ROTATION_UNDEFINED;
if (mFinishCallback == null) return;
mFinishCallback.onTransitionFinished(null /* wct */);
mFinishCallback = null;
@@ -475,6 +532,9 @@
@Override
public void onFixedRotationStarted() {
+ if (mFixedRotationState == FIXED_ROTATION_UNDEFINED) {
+ mFixedRotationState = FIXED_ROTATION_CALLBACK;
+ }
fadeEnteredPipIfNeed(false /* show */);
}
@@ -656,7 +716,7 @@
// Check if it is fixed rotation.
final int rotationDelta;
- if (mInFixedRotation) {
+ if (mFixedRotationState == FIXED_ROTATION_TRANSITION) {
final int startRotation = pipChange.getStartRotation();
final int endRotation = mEndFixedRotation;
rotationDelta = deltaRotation(startRotation, endRotation);
@@ -873,11 +933,13 @@
final int startRotation = pipChange.getStartRotation();
// Check again in case some callers use startEnterAnimation directly so the flag was not
// set in startAnimation, e.g. from DefaultMixedHandler.
- if (!mInFixedRotation) {
+ if (mFixedRotationState != FIXED_ROTATION_TRANSITION) {
mEndFixedRotation = pipChange.getEndFixedRotation();
- mInFixedRotation = mEndFixedRotation != ROTATION_UNDEFINED;
+ mFixedRotationState = mEndFixedRotation != ROTATION_UNDEFINED
+ ? FIXED_ROTATION_TRANSITION : mFixedRotationState;
}
- final int endRotation = mInFixedRotation ? mEndFixedRotation : pipChange.getEndRotation();
+ final int endRotation = mFixedRotationState == FIXED_ROTATION_TRANSITION
+ ? mEndFixedRotation : pipChange.getEndRotation();
setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams,
taskInfo.topActivityInfo);
@@ -888,10 +950,15 @@
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
final Rect currentBounds = pipChange.getStartAbsBounds();
+
+ // Cache the start bounds for overlay manipulations as a part of finishCallback.
+ mInitBounds.set(currentBounds);
+
int rotationDelta = deltaRotation(startRotation, endRotation);
Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
taskInfo.pictureInPictureParams, currentBounds, destinationBounds);
- if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) {
+ if (rotationDelta != Surface.ROTATION_0
+ && mFixedRotationState == FIXED_ROTATION_TRANSITION) {
// Need to get the bounds of new rotation in old rotation for fixed rotation,
computeEnterPipRotatedBounds(rotationDelta, startRotation, endRotation, taskInfo,
destinationBounds, sourceHintRect);
@@ -955,10 +1022,12 @@
} else {
throw new RuntimeException("Unrecognized animation type: " + enterAnimationType);
}
+ mPipOrganizer.mPipOverlay = animator.getContentOverlayLeash();
animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration);
- if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) {
+ if (rotationDelta != Surface.ROTATION_0
+ && mFixedRotationState == FIXED_ROTATION_TRANSITION) {
// For fixed rotation, the animation destination bounds is in old rotation coordinates.
// Set the destination bounds to new coordinates after the animation is finished.
// ComputeRotatedBounds has changed the DisplayLayout without affecting the animation.
@@ -997,13 +1066,17 @@
@NonNull SurfaceControl leash, @Nullable Rect sourceHintRect,
@NonNull Rect destinationBounds,
@NonNull ActivityManager.RunningTaskInfo pipTaskInfo) {
- if (mInFixedRotation) {
+ if (mFixedRotationState == FIXED_ROTATION_TRANSITION) {
// If rotation changes when returning to home, the transition should contain both the
// entering PiP and the display change (PipController#startSwipePipToHome has updated
// the display layout to new rotation). So it is not expected to see fixed rotation.
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"%s: SwipePipToHome should not use fixed rotation %d", TAG, mEndFixedRotation);
}
+ Rect appBounds = pipTaskInfo.configuration.windowConfiguration.getAppBounds();
+ if (mFixedRotationState == FIXED_ROTATION_CALLBACK && appBounds != null) {
+ mInitBounds.set(appBounds);
+ }
final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mSwipePipToHomeOverlay;
if (swipePipToHomeOverlay != null) {
// Launcher fade in the overlay on top of the fullscreen Task. It is possible we
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 20c57fa..04911c0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -78,9 +78,9 @@
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
return;
}
- if (isInPipDirection(direction) && animator.getContentOverlayLeash() != null) {
- mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlayLeash(),
- animator::clearContentOverlay, true /* withStartDelay*/);
+ if (isInPipDirection(direction) && mPipOrganizer.mPipOverlay != null) {
+ mPipOrganizer.fadeOutAndRemoveOverlay(mPipOrganizer.mPipOverlay,
+ null /* callback */, true /* withStartDelay*/);
}
onFinishResize(taskInfo, animator.getDestinationBounds(), direction, tx);
sendOnPipTransitionFinished(direction);
@@ -90,9 +90,9 @@
public void onPipAnimationCancel(TaskInfo taskInfo,
PipAnimationController.PipTransitionAnimator animator) {
final int direction = animator.getTransitionDirection();
- if (isInPipDirection(direction) && animator.getContentOverlayLeash() != null) {
- mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlayLeash(),
- animator::clearContentOverlay, true /* withStartDelay */);
+ if (isInPipDirection(direction) && mPipOrganizer.mPipOverlay != null) {
+ mPipOrganizer.fadeOutAndRemoveOverlay(mPipOrganizer.mPipOverlay,
+ null /* callback */, true /* withStartDelay */);
}
sendOnPipTransitionCancelled(animator.getTransitionDirection());
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegateTest.kt
new file mode 100644
index 0000000..4b96251
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegateTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.saver.domain
+
+import android.content.SharedPreferences
+import android.testing.LeakCheck
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.utils.leaks.FakeDataSaverController
+import kotlin.coroutines.EmptyCoroutineContext
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+
+/** Test [DataSaverDialogDelegate]. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DataSaverDialogDelegateTest : SysuiTestCase() {
+
+ private val dataSaverController = FakeDataSaverController(LeakCheck())
+
+ private lateinit var sysuiDialogFactory: SystemUIDialog.Factory
+ private lateinit var sysuiDialog: SystemUIDialog
+ private lateinit var dataSaverDialogDelegate: DataSaverDialogDelegate
+
+ @Before
+ fun setup() {
+ sysuiDialog = mock<SystemUIDialog>()
+ sysuiDialogFactory = mock<SystemUIDialog.Factory>()
+
+ dataSaverDialogDelegate =
+ DataSaverDialogDelegate(
+ sysuiDialogFactory,
+ context,
+ EmptyCoroutineContext,
+ dataSaverController,
+ mock<SharedPreferences>()
+ )
+
+ whenever(sysuiDialogFactory.create(eq(dataSaverDialogDelegate), eq(context)))
+ .thenReturn(sysuiDialog)
+ }
+ @Test
+ fun delegateSetsDialogTitleCorrectly() {
+ val expectedResId = R.string.data_saver_enable_title
+
+ dataSaverDialogDelegate.onCreate(sysuiDialog, null)
+
+ verify(sysuiDialog).setTitle(eq(expectedResId))
+ }
+
+ @Test
+ fun delegateSetsDialogMessageCorrectly() {
+ val expectedResId = R.string.data_saver_description
+
+ dataSaverDialogDelegate.onCreate(sysuiDialog, null)
+
+ verify(sysuiDialog).setMessage(expectedResId)
+ }
+
+ @Test
+ fun delegateSetsDialogPositiveButtonCorrectly() {
+ val expectedResId = R.string.data_saver_enable_button
+
+ dataSaverDialogDelegate.onCreate(sysuiDialog, null)
+
+ verify(sysuiDialog).setPositiveButton(eq(expectedResId), any())
+ }
+
+ @Test
+ fun delegateSetsDialogCancelButtonCorrectly() {
+ val expectedResId = R.string.cancel
+
+ dataSaverDialogDelegate.onCreate(sysuiDialog, null)
+
+ verify(sysuiDialog).setNeutralButton(eq(expectedResId), eq(null))
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
new file mode 100644
index 0000000..d182412
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.saver.domain
+
+import android.widget.Switch
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
+import com.android.systemui.qs.tiles.impl.saver.qsDataSaverTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DataSaverTileMapperTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val dataSaverTileConfig = kosmos.qsDataSaverTileConfig
+
+ // Using lazy (versus =) to make sure we override the right context -- see b/311612168
+ private val mapper by lazy { DataSaverTileMapper(context.orCreateTestableResources.resources) }
+
+ @Test
+ fun activeStateMatchesEnabledModel() {
+ val inputModel = DataSaverTileModel(true)
+
+ val outputState = mapper.map(dataSaverTileConfig, inputModel)
+
+ val expectedState =
+ createDataSaverTileState(
+ QSTileState.ActivationState.ACTIVE,
+ R.drawable.qs_data_saver_icon_on
+ )
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ @Test
+ fun inactiveStateMatchesDisabledModel() {
+ val inputModel = DataSaverTileModel(false)
+
+ val outputState = mapper.map(dataSaverTileConfig, inputModel)
+
+ val expectedState =
+ createDataSaverTileState(
+ QSTileState.ActivationState.INACTIVE,
+ R.drawable.qs_data_saver_icon_off
+ )
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ private fun createDataSaverTileState(
+ activationState: QSTileState.ActivationState,
+ iconRes: Int
+ ): QSTileState {
+ val label = context.getString(R.string.data_saver)
+ val secondaryLabel =
+ if (activationState == QSTileState.ActivationState.ACTIVE)
+ context.resources.getStringArray(R.array.tile_states_saver)[2]
+ else if (activationState == QSTileState.ActivationState.INACTIVE)
+ context.resources.getStringArray(R.array.tile_states_saver)[1]
+ else context.resources.getStringArray(R.array.tile_states_saver)[0]
+
+ return QSTileState(
+ { Icon.Resource(iconRes, null) },
+ label,
+ activationState,
+ secondaryLabel,
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
+ label,
+ null,
+ QSTileState.SideViewIcon.None,
+ QSTileState.EnabledState.ENABLED,
+ Switch::class.qualifiedName
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt
new file mode 100644
index 0000000..819bd03
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.saver.domain.interactor
+
+import android.os.UserHandle
+import android.testing.LeakCheck
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
+import com.android.systemui.utils.leaks.FakeDataSaverController
+import com.google.common.truth.Truth
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DataSaverTileDataInteractorTest : SysuiTestCase() {
+ private val controller: FakeDataSaverController = FakeDataSaverController(LeakCheck())
+ private val underTest: DataSaverTileDataInteractor = DataSaverTileDataInteractor(controller)
+
+ @Test
+ fun isAvailableRegardlessOfController() = runTest {
+ controller.setDataSaverEnabled(false)
+
+ runCurrent()
+ val availability by collectLastValue(underTest.availability(TEST_USER))
+
+ Truth.assertThat(availability).isTrue()
+ }
+
+ @Test
+ fun dataMatchesController() = runTest {
+ controller.setDataSaverEnabled(false)
+ val flowValues: List<DataSaverTileModel> by
+ collectValues(underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest)))
+
+ runCurrent()
+ controller.setDataSaverEnabled(true)
+ runCurrent()
+ controller.setDataSaverEnabled(false)
+ runCurrent()
+
+ Truth.assertThat(flowValues.size).isEqualTo(3)
+ Truth.assertThat(flowValues.map { it.isEnabled })
+ .containsExactly(false, true, false)
+ .inOrder()
+ }
+
+ private companion object {
+ val TEST_USER = UserHandle.of(1)!!
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractorTest.kt
new file mode 100644
index 0000000..7091cb3
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractorTest.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.saver.domain.interactor
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.provider.Settings
+import android.testing.LeakCheck
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.actions.intentInputs
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.utils.leaks.FakeDataSaverController
+import com.google.common.truth.Truth.assertThat
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DataSaverTileUserActionInteractorTest : SysuiTestCase() {
+ private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler()
+ private val dataSaverController = FakeDataSaverController(LeakCheck())
+
+ private lateinit var userFileManager: UserFileManager
+ private lateinit var sharedPreferences: SharedPreferences
+ private lateinit var dialogFactory: SystemUIDialog.Factory
+ private lateinit var underTest: DataSaverTileUserActionInteractor
+
+ @Before
+ fun setup() {
+ userFileManager = mock<UserFileManager>()
+ sharedPreferences = mock<SharedPreferences>()
+ dialogFactory = mock<SystemUIDialog.Factory>()
+ whenever(
+ userFileManager.getSharedPreferences(
+ eq(DataSaverTileUserActionInteractor.PREFS),
+ eq(Context.MODE_PRIVATE),
+ eq(context.userId)
+ )
+ )
+ .thenReturn(sharedPreferences)
+
+ underTest =
+ DataSaverTileUserActionInteractor(
+ context,
+ EmptyCoroutineContext,
+ EmptyCoroutineContext,
+ dataSaverController,
+ qsTileIntentUserActionHandler,
+ mock<DialogLaunchAnimator>(),
+ dialogFactory,
+ userFileManager,
+ )
+ }
+
+ /** Since the dialog was shown before, we expect the click to enable the controller. */
+ @Test
+ fun handleClickToEnableDialogShownBefore() = runTest {
+ whenever(
+ sharedPreferences.getBoolean(
+ eq(DataSaverTileUserActionInteractor.DIALOG_SHOWN),
+ any()
+ )
+ )
+ .thenReturn(true)
+ val stateBeforeClick = false
+
+ underTest.handleInput(QSTileInputTestKtx.click(DataSaverTileModel(stateBeforeClick)))
+
+ assertThat(dataSaverController.isDataSaverEnabled).isEqualTo(!stateBeforeClick)
+ }
+
+ /**
+ * The first time the tile is clicked to turn on we expect (1) the enabled state to not change
+ * and (2) the dialog to be shown instead.
+ */
+ @Test
+ fun handleClickToEnableDialogNotShownBefore() = runTest {
+ whenever(
+ sharedPreferences.getBoolean(
+ eq(DataSaverTileUserActionInteractor.DIALOG_SHOWN),
+ any()
+ )
+ )
+ .thenReturn(false)
+ val mockDialog = mock<SystemUIDialog>()
+ whenever(dialogFactory.create(any(), any())).thenReturn(mockDialog)
+ val stateBeforeClick = false
+
+ val input = QSTileInputTestKtx.click(DataSaverTileModel(stateBeforeClick))
+ underTest.handleInput(input)
+
+ assertThat(dataSaverController.isDataSaverEnabled).isEqualTo(stateBeforeClick)
+ verify(mockDialog).show()
+ }
+
+ /** Disabling should flip the state, even if the dialog was not shown before. */
+ @Test
+ fun handleClickToDisableDialogNotShownBefore() = runTest {
+ whenever(
+ sharedPreferences.getBoolean(
+ eq(DataSaverTileUserActionInteractor.DIALOG_SHOWN),
+ any()
+ )
+ )
+ .thenReturn(false)
+ val enabledBeforeClick = true
+
+ underTest.handleInput(QSTileInputTestKtx.click(DataSaverTileModel(enabledBeforeClick)))
+
+ assertThat(dataSaverController.isDataSaverEnabled).isEqualTo(!enabledBeforeClick)
+ }
+
+ @Test
+ fun handleClickToDisableDialogShownBefore() = runTest {
+ whenever(
+ sharedPreferences.getBoolean(
+ eq(DataSaverTileUserActionInteractor.DIALOG_SHOWN),
+ any()
+ )
+ )
+ .thenReturn(true)
+ val enabledBeforeClick = true
+
+ underTest.handleInput(QSTileInputTestKtx.click(DataSaverTileModel(enabledBeforeClick)))
+
+ assertThat(dataSaverController.isDataSaverEnabled).isEqualTo(!enabledBeforeClick)
+ }
+
+ @Test
+ fun handleLongClickWhenEnabled() = runTest {
+ val enabledState = true
+
+ underTest.handleInput(QSTileInputTestKtx.longClick(DataSaverTileModel(enabledState)))
+
+ assertThat(qsTileIntentUserActionHandler.handledInputs).hasSize(1)
+ val intentInput = qsTileIntentUserActionHandler.intentInputs.last()
+ val actualIntentAction = intentInput.intent.action
+ val expectedIntentAction = Settings.ACTION_DATA_SAVER_SETTINGS
+ assertThat(actualIntentAction).isEqualTo(expectedIntentAction)
+ }
+
+ @Test
+ fun handleLongClickWhenDisabled() = runTest {
+ val enabledState = false
+
+ underTest.handleInput(QSTileInputTestKtx.longClick(DataSaverTileModel(enabledState)))
+
+ assertThat(qsTileIntentUserActionHandler.handledInputs).hasSize(1)
+ val intentInput = qsTileIntentUserActionHandler.intentInputs.last()
+ val actualIntentAction = intentInput.intent.action
+ val expectedIntentAction = Settings.ACTION_DATA_SAVER_SETTINGS
+ assertThat(actualIntentAction).isEqualTo(expectedIntentAction)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
new file mode 100644
index 0000000..fc42ba4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.saver.domain
+
+import android.content.Context
+import android.content.DialogInterface
+import android.content.SharedPreferences
+import android.os.Bundle
+import com.android.internal.R
+import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileUserActionInteractor
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.policy.DataSaverController
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+class DataSaverDialogDelegate(
+ private val sysuiDialogFactory: SystemUIDialog.Factory,
+ private val context: Context,
+ private val backgroundContext: CoroutineContext,
+ private val dataSaverController: DataSaverController,
+ private val sharedPreferences: SharedPreferences,
+) : SystemUIDialog.Delegate {
+ override fun createDialog(): SystemUIDialog {
+ return sysuiDialogFactory.create(this, context)
+ }
+
+ override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+ with(dialog) {
+ setTitle(R.string.data_saver_enable_title)
+ setMessage(R.string.data_saver_description)
+ setPositiveButton(R.string.data_saver_enable_button) { _: DialogInterface?, _ ->
+ CoroutineScope(backgroundContext).launch {
+ dataSaverController.setDataSaverEnabled(true)
+ }
+
+ sharedPreferences
+ .edit()
+ .putBoolean(DataSaverTileUserActionInteractor.DIALOG_SHOWN, true)
+ .apply()
+ }
+ setNeutralButton(R.string.cancel, null)
+ setShowForAllUsers(true)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
new file mode 100644
index 0000000..25b0913
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.saver.domain
+
+import android.content.res.Resources
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** Maps [DataSaverTileModel] to [QSTileState]. */
+class DataSaverTileMapper @Inject constructor(@Main private val resources: Resources) :
+ QSTileDataToStateMapper<DataSaverTileModel> {
+ override fun map(config: QSTileConfig, data: DataSaverTileModel): QSTileState =
+ QSTileState.build(resources, config.uiConfig) {
+ with(data) {
+ if (isEnabled) {
+ activationState = QSTileState.ActivationState.ACTIVE
+ icon = { Icon.Resource(R.drawable.qs_data_saver_icon_on, null) }
+ secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[2]
+ } else {
+ activationState = QSTileState.ActivationState.INACTIVE
+ icon = { Icon.Resource(R.drawable.qs_data_saver_icon_off, null) }
+ secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[1]
+ }
+ contentDescription = label
+ supportedActions =
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractor.kt
new file mode 100644
index 0000000..91e049b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractor.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.saver.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
+import com.android.systemui.statusbar.policy.DataSaverController
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+/** Observes data saver state changes providing the [DataSaverTileModel]. */
+class DataSaverTileDataInteractor
+@Inject
+constructor(
+ private val dataSaverController: DataSaverController,
+) : QSTileDataInteractor<DataSaverTileModel> {
+
+ override fun tileData(
+ user: UserHandle,
+ triggers: Flow<DataUpdateTrigger>
+ ): Flow<DataSaverTileModel> =
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val initialValue = dataSaverController.isDataSaverEnabled
+ trySend(DataSaverTileModel(initialValue))
+
+ val callback = DataSaverController.Listener { trySend(DataSaverTileModel(it)) }
+
+ dataSaverController.addCallback(callback)
+ awaitClose { dataSaverController.removeCallback(callback) }
+ }
+
+ override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
new file mode 100644
index 0000000..af74409
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.saver.domain.interactor
+
+import android.content.Context
+import android.content.Intent
+import android.provider.Settings
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.saver.domain.DataSaverDialogDelegate
+import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.policy.DataSaverController
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.withContext
+
+/** Handles data saver tile clicks. */
+class DataSaverTileUserActionInteractor
+@Inject
+constructor(
+ @Application private val context: Context,
+ @Main private val coroutineContext: CoroutineContext,
+ @Background private val backgroundContext: CoroutineContext,
+ private val dataSaverController: DataSaverController,
+ private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+ private val dialogLaunchAnimator: DialogLaunchAnimator,
+ private val systemUIDialogFactory: SystemUIDialog.Factory,
+ userFileManager: UserFileManager,
+) : QSTileUserActionInteractor<DataSaverTileModel> {
+ companion object {
+ private const val INTERACTION_JANK_TAG = "start_data_saver"
+ const val PREFS = "data_saver"
+ const val DIALOG_SHOWN = "data_saver_dialog_shown"
+ }
+
+ val sharedPreferences =
+ userFileManager.getSharedPreferences(PREFS, Context.MODE_PRIVATE, context.userId)
+
+ override suspend fun handleInput(input: QSTileInput<DataSaverTileModel>): Unit =
+ with(input) {
+ when (action) {
+ is QSTileUserAction.Click -> {
+ val wasEnabled: Boolean = data.isEnabled
+ if (wasEnabled || sharedPreferences.getBoolean(DIALOG_SHOWN, false)) {
+ withContext(backgroundContext) {
+ dataSaverController.setDataSaverEnabled(!wasEnabled)
+ }
+ return@with
+ }
+ // Show a dialog to confirm first. Dialogs shown by the DialogLaunchAnimator
+ // must be created and shown on the main thread, so we post it to the UI
+ // handler
+ withContext(coroutineContext) {
+ val dialogContext = action.view?.context ?: context
+ val dialogDelegate =
+ DataSaverDialogDelegate(
+ systemUIDialogFactory,
+ dialogContext,
+ backgroundContext,
+ dataSaverController,
+ sharedPreferences
+ )
+ val dialog = systemUIDialogFactory.create(dialogDelegate, dialogContext)
+
+ if (action.view != null) {
+ dialogLaunchAnimator.showFromView(
+ dialog,
+ action.view!!,
+ DialogCuj(
+ InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+ INTERACTION_JANK_TAG
+ )
+ )
+ } else {
+ dialog.show()
+ }
+ }
+ }
+ is QSTileUserAction.LongClick -> {
+ qsTileIntentUserActionHandler.handle(
+ action.view,
+ Intent(Settings.ACTION_DATA_SAVER_SETTINGS)
+ )
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/model/DataSaverTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/model/DataSaverTileModel.kt
new file mode 100644
index 0000000..040c7bf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/model/DataSaverTileModel.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.saver.domain.model
+
+/**
+ * data saver tile model.
+ *
+ * @param isEnabled is true when the data saver is enabled;
+ */
+@JvmInline value class DataSaverTileModel(val isEnabled: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
index a3adea0..642eacc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
@@ -35,6 +35,10 @@
import com.android.systemui.qs.tiles.impl.airplane.domain.interactor.AirplaneModeTileDataInteractor
import com.android.systemui.qs.tiles.impl.airplane.domain.interactor.AirplaneModeTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
+import com.android.systemui.qs.tiles.impl.saver.domain.DataSaverTileMapper
+import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileDataInteractor
+import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy
import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
@@ -85,6 +89,7 @@
companion object {
const val AIRPLANE_MODE_TILE_SPEC = "airplane"
+ const val DATA_SAVER_TILE_SPEC = "saver"
/** Inject InternetTile or InternetTileNewImpl into tileMap in QSModule */
@Provides
@@ -132,5 +137,36 @@
stateInteractor,
mapper,
)
+
+ @Provides
+ @IntoMap
+ @StringKey(DATA_SAVER_TILE_SPEC)
+ fun provideDataSaverTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+ QSTileConfig(
+ tileSpec = TileSpec.create(DATA_SAVER_TILE_SPEC),
+ uiConfig =
+ QSTileUIConfig.Resource(
+ iconRes = R.drawable.qs_data_saver_icon_off,
+ labelRes = R.string.data_saver,
+ ),
+ instanceId = uiEventLogger.getNewInstanceId(),
+ )
+
+ /** Inject DataSaverTile into tileViewModelMap in QSModule */
+ @Provides
+ @IntoMap
+ @StringKey(DATA_SAVER_TILE_SPEC)
+ fun provideDataSaverTileViewModel(
+ factory: QSTileViewModelFactory.Static<DataSaverTileModel>,
+ mapper: DataSaverTileMapper,
+ stateInteractor: DataSaverTileDataInteractor,
+ userActionInteractor: DataSaverTileUserActionInteractor
+ ): QSTileViewModel =
+ factory.create(
+ TileSpec.create(DATA_SAVER_TILE_SPEC),
+ userActionInteractor,
+ stateInteractor,
+ mapper,
+ )
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/saver/DataSaverTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/saver/DataSaverTileKosmos.kt
new file mode 100644
index 0000000..e9a394a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/saver/DataSaverTileKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.saver
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.qsEventLogger
+import com.android.systemui.statusbar.connectivity.ConnectivityModule
+
+val Kosmos.qsDataSaverTileConfig by
+ Kosmos.Fixture { ConnectivityModule.provideDataSaverTileConfig(qsEventLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeDataSaverController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
index 886722e..bade848 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
@@ -19,19 +19,38 @@
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DataSaverController.Listener;
+import java.util.ArrayList;
+import java.util.List;
+
public class FakeDataSaverController extends BaseLeakChecker<Listener> implements DataSaverController {
+ private boolean mIsEnabled = false;
+ private List<Listener> mListeners = new ArrayList<>();
+
public FakeDataSaverController(LeakCheck test) {
super(test, "datasaver");
}
@Override
public boolean isDataSaverEnabled() {
- return false;
+ return mIsEnabled;
}
@Override
public void setDataSaverEnabled(boolean enabled) {
+ mIsEnabled = enabled;
+ for (Listener listener: mListeners) {
+ listener.onDataSaverChanged(enabled);
+ }
+ }
+ @Override
+ public void addCallback(Listener listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public void removeCallback(Listener listener) {
+ mListeners.remove(listener);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 0a2a780..baae1d93 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -861,6 +861,7 @@
}
final class DetectingStateWithMultiFinger extends DetectingState {
+ private static final int TWO_FINGER_GESTURE_MAX_TAPS = 2;
// A flag set to true when two fingers have touched down.
// Used to indicate what next finger action should be.
private boolean mIsTwoFingerCountReached = false;
@@ -917,7 +918,8 @@
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
if (event.getPointerCount() == 2) {
- if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 2, event)) {
+ if (isMultiFingerMultiTapTriggered(
+ TWO_FINGER_GESTURE_MAX_TAPS - 1, event)) {
// 3tap and hold
afterLongTapTimeoutTransitionToDraggingState(event);
} else {
@@ -962,7 +964,8 @@
// (which is a rare combo to be used aside from magnification)
if (isMultiTapTriggered(2 /* taps */) && event.getPointerCount() == 1) {
transitionToViewportDraggingStateAndClear(event);
- } else if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 2, event)
+ } else if (isMultiFingerMultiTapTriggered(
+ TWO_FINGER_GESTURE_MAX_TAPS - 1, event)
&& event.getPointerCount() == 2) {
transitionToViewportDraggingStateAndClear(event);
} else if (isActivated() && event.getPointerCount() == 2) {
@@ -1009,7 +1012,7 @@
mDisplayId, event.getX(), event.getY())) {
transitionToDelegatingStateAndClear();
- } else if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 3, event)) {
+ } else if (isMultiFingerMultiTapTriggered(TWO_FINGER_GESTURE_MAX_TAPS, event)) {
// Placing multiple fingers before a single finger, because achieving a
// multi finger multi tap also means achieving a single finger triple tap
onTripleTap(event);
@@ -1051,7 +1054,7 @@
mIsTwoFingerCountReached = false;
}
- if (mDetectTwoFingerTripleTap && mCompletedTapCount > 2) {
+ if (mDetectTwoFingerTripleTap && mCompletedTapCount > TWO_FINGER_GESTURE_MAX_TAPS - 1) {
final boolean enabled = !isActivated();
mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
}
@@ -1075,7 +1078,7 @@
// Only log the 3tap and hold event
if (!shortcutTriggered) {
final boolean enabled = !isActivated();
- if (mCompletedTapCount == 2) {
+ if (mCompletedTapCount == TWO_FINGER_GESTURE_MAX_TAPS - 1) {
// Two finger triple tap and hold
mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
} else {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 73c267a..75d01f5 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -481,10 +481,10 @@
if (mDetectTwoFingerTripleTap) {
mGestureMatchers.add(new MultiFingerMultiTap(context, /* fingers= */ 2,
- /* taps= */ 3, MagnificationGestureMatcher.GESTURE_TRIPLE_TAP,
+ /* taps= */ 2, MagnificationGestureMatcher.GESTURE_TRIPLE_TAP,
null));
mGestureMatchers.add(new MultiFingerMultiTapAndHold(context, /* fingers= */ 2,
- /* taps= */ 3, MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD,
+ /* taps= */ 2, MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD,
null));
}
diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
index caafb42..fc8ad6bc 100644
--- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
+++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
@@ -35,6 +35,7 @@
import android.app.ForegroundServiceDelegationOptions;
import android.content.ComponentName;
import android.content.pm.ServiceInfo;
+import android.os.Trace;
import android.util.ArrayMap;
import android.util.IntArray;
import android.util.LongArray;
@@ -134,6 +135,11 @@
* call of the right type will also be associated and logged
*/
public void logForegroundServiceStart(int uid, int pid, ServiceRecord record) {
+ if (record.getComponentName() != null) {
+ final String traceTag = record.getComponentName().flattenToString() + ":" + uid;
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, traceTag,
+ "foregroundService", record.foregroundServiceType);
+ }
// initialize the UID stack
UidState uidState = mUids.get(uid);
if (uidState == null) {
@@ -205,6 +211,11 @@
// we need to log all the API end events and remove the start events
// then we remove the FGS from the various stacks
// and also clean up the start calls stack by UID
+ if (record.getComponentName() != null) {
+ final String traceTag = record.getComponentName().flattenToString() + ":" + uid;
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ traceTag, record.hashCode());
+ }
final IntArray apiTypes = convertFgsTypeToApiTypes(record.foregroundServiceType);
final UidState uidState = mUids.get(uid);
if (apiTypes.size() == 0) {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 3529b04..b1b1dba 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -122,11 +122,10 @@
/**
* Flag: This flag identifies secondary displays that should show system decorations, such as
- * status bar, navigation bar, home activity or IME.
+ * navigation bar, home activity or wallpaper.
* <p>Note that this flag doesn't work without {@link #FLAG_TRUSTED}</p>
* @hide
*/
- // TODO (b/114338689): Remove the flag and use IWindowManager#setShouldShowSystemDecors
public static final int FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 12;
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2d145c2..30e9f5b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5275,7 +5275,7 @@
return true;
}
}
- mSettings.appendAndPutEnabledInputMethodLocked(id, false);
+ mSettings.appendAndPutEnabledInputMethodLocked(id);
// Previous state was disabled.
return false;
} else {
@@ -5612,7 +5612,7 @@
if (enabled) {
if (!settings.getEnabledInputMethodListLocked().contains(
methodMap.get(imeId))) {
- settings.appendAndPutEnabledInputMethodLocked(imeId, false);
+ settings.appendAndPutEnabledInputMethodLocked(imeId);
}
} else {
settings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
@@ -6357,7 +6357,7 @@
}
}
if (!previouslyEnabled) {
- settings.appendAndPutEnabledInputMethodLocked(imeId, false);
+ settings.appendAndPutEnabledInputMethodLocked(imeId);
}
}
} else {
@@ -6501,7 +6501,7 @@
settings.putEnabledInputMethodsStr("");
nextEnabledImes.forEach(
imi -> settings.appendAndPutEnabledInputMethodLocked(
- imi.getId(), false));
+ imi.getId()));
// Reset selected IME.
settings.putSelectedInputMethod(nextIme);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index acf9a7f..2128356 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -362,10 +362,7 @@
return result;
}
- void appendAndPutEnabledInputMethodLocked(String id, boolean reloadInputMethodStr) {
- if (reloadInputMethodStr) {
- getEnabledInputMethodsStr();
- }
+ void appendAndPutEnabledInputMethodLocked(String id) {
if (TextUtils.isEmpty(mEnabledInputMethodsStrCache)) {
// Add in the newly enabled input method.
putEnabledInputMethodsStr(id);
diff --git a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
index 9fdeda4..8855666 100644
--- a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
+++ b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
@@ -16,14 +16,23 @@
package com.android.server.notification;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
+
import android.app.UiModeManager;
import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.hardware.display.ColorDisplayManager;
import android.os.Binder;
import android.os.PowerManager;
import android.service.notification.DeviceEffectsApplier;
import android.service.notification.ZenDeviceEffects;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.ConfigChangeOrigin;
+
+import com.android.internal.annotations.GuardedBy;
/** Default implementation for {@link DeviceEffectsApplier}. */
class DefaultDeviceEffectsApplier implements DeviceEffectsApplier {
@@ -34,13 +43,24 @@
private static final int SATURATION_LEVEL_FULL_COLOR = 100;
private static final float WALLPAPER_DIM_AMOUNT_DIMMED = 0.6f;
private static final float WALLPAPER_DIM_AMOUNT_NORMAL = 0f;
+ private static final IntentFilter SCREEN_OFF_INTENT_FILTER = new IntentFilter(
+ Intent.ACTION_SCREEN_OFF);
+ private final Context mContext;
private final ColorDisplayManager mColorDisplayManager;
private final PowerManager mPowerManager;
private final UiModeManager mUiModeManager;
private final WallpaperManager mWallpaperManager;
+ private final Object mRegisterReceiverLock = new Object();
+ @GuardedBy("mRegisterReceiverLock")
+ private boolean mIsScreenOffReceiverRegistered;
+
+ private ZenDeviceEffects mLastAppliedEffects = new ZenDeviceEffects.Builder().build();
+ private boolean mPendingNightMode;
+
DefaultDeviceEffectsApplier(Context context) {
+ mContext = context;
mColorDisplayManager = context.getSystemService(ColorDisplayManager.class);
mPowerManager = context.getSystemService(PowerManager.class);
mUiModeManager = context.getSystemService(UiModeManager.class);
@@ -48,24 +68,90 @@
}
@Override
- public void apply(ZenDeviceEffects effects) {
+ public void apply(ZenDeviceEffects effects, @ConfigChangeOrigin int origin) {
Binder.withCleanCallingIdentity(() -> {
- mPowerManager.suppressAmbientDisplay(SUPPRESS_AMBIENT_DISPLAY_TOKEN,
- effects.shouldSuppressAmbientDisplay());
-
- if (mColorDisplayManager != null) {
- mColorDisplayManager.setSaturationLevel(
- effects.shouldDisplayGrayscale() ? SATURATION_LEVEL_GRAYSCALE
- : SATURATION_LEVEL_FULL_COLOR);
+ if (mLastAppliedEffects.shouldSuppressAmbientDisplay()
+ != effects.shouldSuppressAmbientDisplay()) {
+ mPowerManager.suppressAmbientDisplay(SUPPRESS_AMBIENT_DISPLAY_TOKEN,
+ effects.shouldSuppressAmbientDisplay());
}
- if (mWallpaperManager != null) {
- mWallpaperManager.setWallpaperDimAmount(
- effects.shouldDimWallpaper() ? WALLPAPER_DIM_AMOUNT_DIMMED
- : WALLPAPER_DIM_AMOUNT_NORMAL);
+ if (mLastAppliedEffects.shouldDisplayGrayscale() != effects.shouldDisplayGrayscale()) {
+ if (mColorDisplayManager != null) {
+ mColorDisplayManager.setSaturationLevel(
+ effects.shouldDisplayGrayscale() ? SATURATION_LEVEL_GRAYSCALE
+ : SATURATION_LEVEL_FULL_COLOR);
+ }
}
- // TODO: b/308673343 - Apply dark theme (via UiModeManager) when screen is off.
+ if (mLastAppliedEffects.shouldDimWallpaper() != effects.shouldDimWallpaper()) {
+ if (mWallpaperManager != null) {
+ mWallpaperManager.setWallpaperDimAmount(
+ effects.shouldDimWallpaper() ? WALLPAPER_DIM_AMOUNT_DIMMED
+ : WALLPAPER_DIM_AMOUNT_NORMAL);
+ }
+ }
+
+ if (mLastAppliedEffects.shouldUseNightMode() != effects.shouldUseNightMode()) {
+ updateOrScheduleNightMode(effects.shouldUseNightMode(), origin);
+ }
});
+
+ mLastAppliedEffects = effects;
+ }
+
+ private void updateOrScheduleNightMode(boolean useNightMode, @ConfigChangeOrigin int origin) {
+ mPendingNightMode = useNightMode;
+
+ // Changing the theme can be disruptive for the user (Activities are likely recreated, may
+ // lose some state). Therefore we only apply the change immediately if the rule was
+ // activated manually, or we are initializing, or the screen is currently off/dreaming.
+ if (origin == ZenModeConfig.UPDATE_ORIGIN_INIT
+ || origin == ZenModeConfig.UPDATE_ORIGIN_INIT_USER
+ || origin == ZenModeConfig.UPDATE_ORIGIN_USER
+ || origin == ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
+ || !mPowerManager.isInteractive()) {
+ unregisterScreenOffReceiver();
+ updateNightModeImmediately(useNightMode);
+ } else {
+ registerScreenOffReceiver();
+ }
+ }
+
+ @GuardedBy("mRegisterReceiverLock")
+ private final BroadcastReceiver mNightModeWhenScreenOff = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ unregisterScreenOffReceiver();
+ updateNightModeImmediately(mPendingNightMode);
+ }
+ };
+
+ private void updateNightModeImmediately(boolean useNightMode) {
+ Binder.withCleanCallingIdentity(() -> {
+ // TODO: b/314285749 - Placeholder; use real APIs when available.
+ mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mUiModeManager.setNightModeActivatedForCustomMode(MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
+ useNightMode);
+ });
+ }
+
+ private void registerScreenOffReceiver() {
+ synchronized (mRegisterReceiverLock) {
+ if (!mIsScreenOffReceiverRegistered) {
+ mContext.registerReceiver(mNightModeWhenScreenOff, SCREEN_OFF_INTENT_FILTER,
+ Context.RECEIVER_NOT_EXPORTED);
+ mIsScreenOffReceiverRegistered = true;
+ }
+ }
+ }
+
+ private void unregisterScreenOffReceiver() {
+ synchronized (mRegisterReceiverLock) {
+ if (mIsScreenOffReceiverRegistered) {
+ mIsScreenOffReceiverRegistered = false;
+ mContext.unregisterReceiver(mNightModeWhenScreenOff);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 02845fb..c2a145d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5299,11 +5299,11 @@
public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
enforceSystemOrSystemUI("INotificationManager.setZenMode");
final int callingUid = Binder.getCallingUid();
- final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
final long identity = Binder.clearCallingIdentity();
try {
- mZenModeHelper.setManualZenMode(mode, conditionId, null, reason, callingUid,
- isSystemOrSystemUi);
+ mZenModeHelper.setManualZenMode(mode, conditionId,
+ ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, // Checked by enforce()
+ reason, /* caller= */ null, callingUid);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -5360,10 +5360,11 @@
}
return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule,
- "addAutomaticZenRule", Binder.getCallingUid(),
- // TODO: b/308670715: Distinguish FROM_APP from FROM_USER
- isCallerSystemOrSystemUi() ? ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI
- : ZenModeHelper.FROM_APP);
+ // TODO: b/308670715: Distinguish origin properly (e.g. USER if creating a rule
+ // manually in Settings).
+ isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
+ : ZenModeConfig.UPDATE_ORIGIN_APP,
+ "addAutomaticZenRule", Binder.getCallingUid());
}
@Override
@@ -5379,11 +5380,12 @@
Objects.requireNonNull(automaticZenRule.getConditionId(), "ConditionId is null");
enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
+ // TODO: b/308670715: Distinguish origin properly (e.g. USER if updating a rule
+ // manually in Settings).
return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
- "updateAutomaticZenRule", Binder.getCallingUid(),
- // TODO: b/308670715: Distinguish FROM_APP from FROM_USER
- isCallerSystemOrSystemUi() ? ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI
- : ZenModeHelper.FROM_APP);
+ isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
+ : ZenModeConfig.UPDATE_ORIGIN_APP,
+ "updateAutomaticZenRule", Binder.getCallingUid());
}
@Override
@@ -5392,8 +5394,12 @@
// Verify that they can modify zen rules.
enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
- return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule",
- Binder.getCallingUid(), isCallerSystemOrSystemUi());
+ // TODO: b/308670715: Distinguish origin properly (e.g. USER if removing a rule
+ // manually in Settings).
+ return mZenModeHelper.removeAutomaticZenRule(id,
+ isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
+ : ZenModeConfig.UPDATE_ORIGIN_APP,
+ "removeAutomaticZenRule", Binder.getCallingUid());
}
@Override
@@ -5402,8 +5408,9 @@
enforceSystemOrSystemUI("removeAutomaticZenRules");
return mZenModeHelper.removeAutomaticZenRules(packageName,
- packageName + "|removeAutomaticZenRules", Binder.getCallingUid(),
- isCallerSystemOrSystemUi());
+ isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
+ : ZenModeConfig.UPDATE_ORIGIN_APP,
+ packageName + "|removeAutomaticZenRules", Binder.getCallingUid());
}
@Override
@@ -5421,8 +5428,12 @@
enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState");
- mZenModeHelper.setAutomaticZenRuleState(id, condition, Binder.getCallingUid(),
- isCallerSystemOrSystemUi());
+ // TODO: b/308670715: Distinguish origin properly (e.g. USER if toggling a rule
+ // manually in Settings).
+ mZenModeHelper.setAutomaticZenRuleState(id, condition,
+ isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
+ : ZenModeConfig.UPDATE_ORIGIN_APP,
+ Binder.getCallingUid());
}
@Override
@@ -5440,8 +5451,11 @@
final long identity = Binder.clearCallingIdentity();
try {
- mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter",
- callingUid, isSystemOrSystemUi);
+ mZenModeHelper.setManualZenMode(zen, null,
+ isSystemOrSystemUi ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
+ : ZenModeConfig.UPDATE_ORIGIN_APP,
+ /* reason= */ "setInterruptionFilter", /* caller= */ pkg,
+ callingUid);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -5806,7 +5820,10 @@
} else {
ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion,
policy);
- mZenModeHelper.setNotificationPolicy(policy, callingUid, isSystemOrSystemUi);
+ mZenModeHelper.setNotificationPolicy(policy,
+ isSystemOrSystemUi ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
+ : ZenModeConfig.UPDATE_ORIGIN_APP,
+ callingUid);
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to set notification policy", e);
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 6a7eebb..fd86870 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -59,6 +59,7 @@
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.NotificationListenerService;
import android.service.notification.RankingHelperProto;
+import android.service.notification.ZenModeConfig;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
@@ -1862,12 +1863,16 @@
public void updateZenPolicy(boolean areChannelsBypassingDnd, int callingUid,
boolean fromSystemOrSystemUi) {
NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
- mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
- policy.priorityCategories, policy.priorityCallSenders,
- policy.priorityMessageSenders, policy.suppressedVisualEffects,
- (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
- : 0),
- policy.priorityConversationSenders), callingUid, fromSystemOrSystemUi);
+ mZenModeHelper.setNotificationPolicy(
+ new NotificationManager.Policy(
+ policy.priorityCategories, policy.priorityCallSenders,
+ policy.priorityMessageSenders, policy.suppressedVisualEffects,
+ (areChannelsBypassingDnd
+ ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND : 0),
+ policy.priorityConversationSenders),
+ fromSystemOrSystemUi ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
+ : ZenModeConfig.UPDATE_ORIGIN_APP,
+ callingUid);
}
// TODO: b/310620812 - rename to hasPriorityChannels() when modes_api is inlined.
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 6ecd799..86aa2d8 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -111,8 +111,10 @@
public void onServiceAdded(ComponentName component) {
if (DEBUG) Log.d(TAG, "onServiceAdded " + component);
final int callingUid = Binder.getCallingUid();
- mHelper.setConfig(mHelper.getConfig(), component, "zmc.onServiceAdded:" + component,
- callingUid, callingUid == Process.SYSTEM_UID);
+ mHelper.setConfig(mHelper.getConfig(), component,
+ callingUid == Process.SYSTEM_UID ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
+ : ZenModeConfig.UPDATE_ORIGIN_APP,
+ "zmc.onServiceAdded:" + component, callingUid);
}
@Override
@@ -121,8 +123,10 @@
ZenModeConfig config = mHelper.getConfig();
if (config == null) return;
final int callingUid = Binder.getCallingUid();
- mHelper.setAutomaticZenRuleState(id, condition, callingUid,
- callingUid == Process.SYSTEM_UID);
+ mHelper.setAutomaticZenRuleState(id, condition,
+ callingUid == Process.SYSTEM_UID ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
+ : ZenModeConfig.UPDATE_ORIGIN_APP,
+ callingUid);
}
// Only valid for CPS backed rules
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 218519f..7ec94c3 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (c) 2014, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,11 +24,16 @@
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import static android.service.notification.NotificationServiceProto.ROOT_CONFIG;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_APP;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_INIT;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_INIT_USER;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_RESTORE_BACKUP;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_USER;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import android.annotation.DrawableRes;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -79,6 +84,7 @@
import android.service.notification.DeviceEffectsApplier;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.ConfigChangeOrigin;
import android.service.notification.ZenModeConfig.ZenRule;
import android.service.notification.ZenModeProto;
import android.service.notification.ZenPolicy;
@@ -111,8 +117,6 @@
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -137,21 +141,6 @@
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
static final long SEND_ACTIVATION_AZR_STATUSES = 308673617L;
- /** A rule addition or update that is initiated by the System or SystemUI. */
- static final int FROM_SYSTEM_OR_SYSTEMUI = 1;
- /** A rule addition or update that is initiated by the user (through system settings). */
- static final int FROM_USER = 2;
- /** A rule addition or update that is initiated by an app (via NotificationManager APIs). */
- static final int FROM_APP = 3;
-
- @IntDef(prefix = { "FROM_" }, value = {
- FROM_SYSTEM_OR_SYSTEMUI,
- FROM_USER,
- FROM_APP
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface ChangeOrigin {}
-
// pkg|userId => uid
@VisibleForTesting protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
@@ -160,7 +149,7 @@
private final SettingsObserver mSettingsObserver;
private final AppOpsManager mAppOps;
private final NotificationManager mNotificationManager;
- private ZenModeConfig mDefaultConfig;
+ private final ZenModeConfig mDefaultConfig;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final ZenModeFiltering mFiltering;
private final RingerModeDelegate mRingerModeDelegate = new
@@ -275,9 +264,8 @@
// "update" config to itself, which will have no effect in the case where a config
// was read in via XML, but will initialize zen mode if nothing was read in and the
// config remains the default.
- updateConfigAndZenModeLocked(mConfig, "init", true /*setRingerMode*/,
- Process.SYSTEM_UID /* callingUid */, true /* is system */,
- false /* no broadcasts*/);
+ updateConfigAndZenModeLocked(mConfig, UPDATE_ORIGIN_INIT, "init",
+ true /*setRingerMode*/, Process.SYSTEM_UID /* callingUid */);
}
}
@@ -297,24 +285,20 @@
/**
* Set the {@link DeviceEffectsApplier} used to apply the consolidated effects.
*
- * <p>If effects were calculated previously (for example, when we loaded a {@link ZenModeConfig}
- * that includes activated rules), they will be applied immediately.
+ * <p>Previously calculated effects (as loaded from the user's {@link ZenModeConfig}) will be
+ * applied immediately.
*/
void setDeviceEffectsApplier(@NonNull DeviceEffectsApplier deviceEffectsApplier) {
if (!Flags.modesApi()) {
return;
}
- ZenDeviceEffects consolidatedDeviceEffects;
synchronized (mConfigLock) {
if (mDeviceEffectsApplier != null) {
throw new IllegalStateException("Already set up a DeviceEffectsApplier!");
}
mDeviceEffectsApplier = deviceEffectsApplier;
- consolidatedDeviceEffects = mConsolidatedDeviceEffects;
}
- if (consolidatedDeviceEffects.hasEffects()) {
- applyConsolidatedDeviceEffects();
- }
+ applyConsolidatedDeviceEffects(UPDATE_ORIGIN_INIT);
}
public void onUserSwitched(int user) {
@@ -353,7 +337,8 @@
config.user = user;
}
synchronized (mConfigLock) {
- setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
+ setConfigLocked(config, null, UPDATE_ORIGIN_INIT_USER, reason,
+ Process.SYSTEM_UID);
}
cleanUpZenRules();
}
@@ -366,9 +351,11 @@
boolean fromSystemOrSystemUi) {
final int newZen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
if (newZen != -1) {
- setManualZenMode(newZen, null, name != null ? name.getPackageName() : null,
- "listener:" + (name != null ? name.flattenToShortString() : null),
- callingUid, fromSystemOrSystemUi);
+ setManualZenMode(newZen, null,
+ fromSystemOrSystemUi ? UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI : UPDATE_ORIGIN_APP,
+ /* reason= */ "listener:" + (name != null ? name.flattenToShortString() : null),
+ /* caller= */ name != null ? name.getPackageName() : null,
+ callingUid);
}
}
@@ -428,7 +415,7 @@
}
public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule,
- String reason, int callingUid, @ChangeOrigin int origin) {
+ @ConfigChangeOrigin int origin, String reason, int callingUid) {
if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) {
PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
if (component == null) {
@@ -462,10 +449,9 @@
}
newConfig = mConfig.copy();
ZenRule rule = new ZenRule();
- populateZenRule(pkg, automaticZenRule, rule, true, origin);
+ populateZenRule(pkg, automaticZenRule, rule, origin, /* isNew= */ true);
newConfig.automaticRules.put(rule.id, rule);
- if (setConfigLocked(newConfig, reason, rule.component, true, callingUid,
- origin == FROM_SYSTEM_OR_SYSTEMUI)) {
+ if (setConfigLocked(newConfig, origin, reason, rule.component, true, callingUid)) {
return rule.id;
} else {
throw new AndroidRuntimeException("Could not create rule");
@@ -474,7 +460,7 @@
}
public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule,
- String reason, int callingUid, @ChangeOrigin int origin) {
+ @ConfigChangeOrigin int origin, String reason, int callingUid) {
ZenModeConfig newConfig;
synchronized (mConfigLock) {
if (mConfig == null) return false;
@@ -502,9 +488,9 @@
}
}
- populateZenRule(rule.pkg, automaticZenRule, rule, false, origin);
- return setConfigLocked(newConfig, reason, rule.component, true, callingUid,
- origin == FROM_SYSTEM_OR_SYSTEMUI);
+ populateZenRule(rule.pkg, automaticZenRule, rule, origin, /* isNew= */ false);
+ return setConfigLocked(newConfig, origin, reason,
+ rule.component, true, callingUid);
}
}
@@ -541,8 +527,7 @@
Condition deactivated = new Condition(rule.conditionId,
mContext.getString(R.string.zen_mode_implicit_deactivated),
Condition.STATE_FALSE);
- setAutomaticZenRuleState(rule.id, deactivated,
- callingUid, /* fromSystemOrSystemUi= */ false);
+ setAutomaticZenRuleState(rule.id, deactivated, UPDATE_ORIGIN_APP, callingUid);
}
} else {
// Either create a new rule with a default ZenPolicy, or update an existing rule's
@@ -558,9 +543,8 @@
rule.condition = new Condition(rule.conditionId,
mContext.getString(R.string.zen_mode_implicit_activated),
Condition.STATE_TRUE);
- setConfigLocked(newConfig, /* triggeringComponent= */ null,
- "applyGlobalZenModeAsImplicitZenRule",
- callingUid, /* fromSystemOrSystemUi= */ false);
+ setConfigLocked(newConfig, /* triggeringComponent= */ null, UPDATE_ORIGIN_APP,
+ "applyGlobalZenModeAsImplicitZenRule", callingUid);
}
}
}
@@ -595,9 +579,8 @@
}
// TODO: b/308673679 - Keep user customization of this rule!
rule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy);
- setConfigLocked(newConfig, /* triggeringComponent= */ null,
- "applyGlobalPolicyAsImplicitZenRule",
- callingUid, /* fromSystemOrSystemUi= */ false);
+ setConfigLocked(newConfig, /* triggeringComponent= */ null, UPDATE_ORIGIN_APP,
+ "applyGlobalPolicyAsImplicitZenRule", callingUid);
}
}
@@ -669,8 +652,8 @@
return "implicit_" + forPackage;
}
- public boolean removeAutomaticZenRule(String id, String reason, int callingUid,
- boolean fromSystemOrSystemUi) {
+ boolean removeAutomaticZenRule(String id, @ConfigChangeOrigin int origin, String reason,
+ int callingUid) {
ZenModeConfig newConfig;
synchronized (mConfigLock) {
if (mConfig == null) return false;
@@ -695,13 +678,12 @@
}
dispatchOnAutomaticRuleStatusChanged(
mConfig.user, ruleToRemove.getPkg(), id, AUTOMATIC_RULE_STATUS_REMOVED);
- return setConfigLocked(newConfig, reason, null, true, callingUid,
- fromSystemOrSystemUi);
+ return setConfigLocked(newConfig, origin, reason, null, true, callingUid);
}
}
- public boolean removeAutomaticZenRules(String packageName, String reason, int callingUid,
- boolean fromSystemOrSystemUi) {
+ boolean removeAutomaticZenRules(String packageName, @ConfigChangeOrigin int origin,
+ String reason, int callingUid) {
ZenModeConfig newConfig;
synchronized (mConfigLock) {
if (mConfig == null) return false;
@@ -712,13 +694,12 @@
newConfig.automaticRules.removeAt(i);
}
}
- return setConfigLocked(newConfig, reason, null, true, callingUid,
- fromSystemOrSystemUi);
+ return setConfigLocked(newConfig, origin, reason, null, true, callingUid);
}
}
- public void setAutomaticZenRuleState(String id, Condition condition, int callingUid,
- boolean fromSystemOrSystemUi) {
+ void setAutomaticZenRuleState(String id, Condition condition, @ConfigChangeOrigin int origin,
+ int callingUid) {
ZenModeConfig newConfig;
synchronized (mConfigLock) {
if (mConfig == null) return;
@@ -726,13 +707,12 @@
newConfig = mConfig.copy();
ArrayList<ZenRule> rules = new ArrayList<>();
rules.add(newConfig.automaticRules.get(id));
- setAutomaticZenRuleStateLocked(newConfig, rules, condition, callingUid,
- fromSystemOrSystemUi);
+ setAutomaticZenRuleStateLocked(newConfig, rules, condition, origin, callingUid);
}
}
- public void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition, int callingUid,
- boolean fromSystemOrSystemUi) {
+ void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition,
+ @ConfigChangeOrigin int origin, int callingUid) {
ZenModeConfig newConfig;
synchronized (mConfigLock) {
if (mConfig == null) return;
@@ -740,20 +720,23 @@
setAutomaticZenRuleStateLocked(newConfig,
findMatchingRules(newConfig, ruleDefinition, condition),
- condition, callingUid, fromSystemOrSystemUi);
+ condition, origin, callingUid);
}
}
@GuardedBy("mConfigLock")
private void setAutomaticZenRuleStateLocked(ZenModeConfig config, List<ZenRule> rules,
- Condition condition, int callingUid, boolean fromSystemOrSystemUi) {
+ Condition condition, @ConfigChangeOrigin int origin, int callingUid) {
if (rules == null || rules.isEmpty()) return;
+ if (Flags.modesApi() && condition.source == Condition.SOURCE_USER_ACTION) {
+ origin = UPDATE_ORIGIN_USER; // Although coming from app, it's actually a user action.
+ }
+
for (ZenRule rule : rules) {
rule.condition = condition;
updateSnoozing(rule);
- setConfigLocked(config, rule.component, "conditionChanged", callingUid,
- fromSystemOrSystemUi);
+ setConfigLocked(config, rule.component, origin, "conditionChanged", callingUid);
}
}
@@ -857,7 +840,7 @@
// update default rule (if locale changed, name of rule will change)
currRule.name = defaultRule.name;
updateAutomaticZenRule(defaultRule.id, zenRuleToAutomaticZenRule(currRule),
- "locale changed", callingUid, FROM_SYSTEM_OR_SYSTEMUI);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "locale changed", callingUid);
}
}
}
@@ -899,13 +882,12 @@
return null;
}
- @VisibleForTesting
void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
- boolean isNew, @ChangeOrigin int origin) {
- // TODO: b/308671593,b/311406021 - Handle origins more precisely:
- // - FROM_USER can override anything and updates bitmask of user-modified fields;
- // - FROM_SYSTEM_OR_SYSTEMUI can override anything and preserves bitmask;
- // - FROM_APP can only update if not user-modified.
+ @ConfigChangeOrigin int origin, boolean isNew) {
+ // TODO: b/308671593,b/311406021 - Handle origins more precisely:
+ // - USER can override anything and updates bitmask of user-modified fields;
+ // - SYSTEM_OR_SYSTEMUI can override anything and preserves bitmask;
+ // - APP can only update if not user-modified.
if (rule.enabled != automaticZenRule.isEnabled()) {
rule.snoozing = false;
}
@@ -951,12 +933,12 @@
*/
@Nullable
private static ZenDeviceEffects fixZenDeviceEffects(@Nullable ZenDeviceEffects oldEffects,
- @Nullable ZenDeviceEffects newEffects, @ChangeOrigin int origin) {
+ @Nullable ZenDeviceEffects newEffects, @ConfigChangeOrigin int origin) {
// TODO: b/308671593,b/311406021 - Handle origins more precisely:
- // - FROM_USER can override anything and updates bitmask of user-modified fields;
- // - FROM_SYSTEM_OR_SYSTEMUI can override anything and preserves bitmask;
- // - FROM_APP can only update if not user-modified.
- if (origin == FROM_SYSTEM_OR_SYSTEMUI || origin == FROM_USER) {
+ // - USER can override anything and updates bitmask of user-modified fields;
+ // - SYSTEM_OR_SYSTEMUI can override anything and preserves bitmask;
+ // - APP can only update if not user-modified.
+ if (origin != UPDATE_ORIGIN_APP) {
return newEffects;
}
@@ -1033,16 +1015,16 @@
: AUTOMATIC_RULE_STATUS_DISABLED);
}
- public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason,
- int callingUid, boolean fromSystemOrSystemUi) {
- setManualZenMode(zenMode, conditionId, reason, caller, true /*setRingerMode*/, callingUid,
- fromSystemOrSystemUi);
+ void setManualZenMode(int zenMode, Uri conditionId, @ConfigChangeOrigin int origin,
+ String reason, @Nullable String caller, int callingUid) {
+ setManualZenMode(zenMode, conditionId, origin, reason, caller, true /*setRingerMode*/,
+ callingUid);
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.SHOW_ZEN_SETTINGS_SUGGESTION, 0);
}
- private void setManualZenMode(int zenMode, Uri conditionId, String reason, String caller,
- boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
+ private void setManualZenMode(int zenMode, Uri conditionId, @ConfigChangeOrigin int origin,
+ String reason, @Nullable String caller, boolean setRingerMode, int callingUid) {
ZenModeConfig newConfig;
synchronized (mConfigLock) {
if (mConfig == null) return;
@@ -1069,8 +1051,7 @@
}
newConfig.manualRule = newRule;
}
- setConfigLocked(newConfig, reason, null, setRingerMode, callingUid,
- fromSystemOrSystemUi);
+ setConfigLocked(newConfig, origin, reason, null, setRingerMode, callingUid);
}
}
@@ -1199,7 +1180,9 @@
}
if (DEBUG) Log.d(TAG, reason);
synchronized (mConfigLock) {
- setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
+ setConfigLocked(config, null,
+ forRestore ? UPDATE_ORIGIN_RESTORE_BACKUP : UPDATE_ORIGIN_INIT, reason,
+ Process.SYSTEM_UID);
}
}
}
@@ -1233,13 +1216,13 @@
/**
* Sets the global notification policy used for priority only do not disturb
*/
- public void setNotificationPolicy(Policy policy, int callingUid, boolean fromSystemOrSystemUi) {
+ public void setNotificationPolicy(Policy policy, @ConfigChangeOrigin int origin,
+ int callingUid) {
synchronized (mConfigLock) {
if (policy == null || mConfig == null) return;
final ZenModeConfig newConfig = mConfig.copy();
newConfig.applyNotificationPolicy(policy);
- setConfigLocked(newConfig, null, "setNotificationPolicy", callingUid,
- fromSystemOrSystemUi);
+ setConfigLocked(newConfig, null, origin, "setNotificationPolicy", callingUid);
}
}
@@ -1264,8 +1247,8 @@
}
}
}
- setConfigLocked(newConfig, null, "cleanUpZenRules", Process.SYSTEM_UID,
- true);
+ setConfigLocked(newConfig, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "cleanUpZenRules",
+ Process.SYSTEM_UID);
}
}
@@ -1287,22 +1270,22 @@
@GuardedBy("mConfigLock")
private boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
- String reason, int callingUid, boolean fromSystemOrSystemUi) {
- return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/,
- callingUid, fromSystemOrSystemUi);
+ @ConfigChangeOrigin int origin, String reason, int callingUid) {
+ return setConfigLocked(config, origin, reason, triggeringComponent, true /*setRingerMode*/,
+ callingUid);
}
- public void setConfig(ZenModeConfig config, ComponentName triggeringComponent, String reason,
- int callingUid, boolean fromSystemOrSystemUi) {
+ void setConfig(ZenModeConfig config, ComponentName triggeringComponent,
+ @ConfigChangeOrigin int origin, String reason, int callingUid) {
synchronized (mConfigLock) {
- setConfigLocked(config, triggeringComponent, reason, callingUid, fromSystemOrSystemUi);
+ setConfigLocked(config, triggeringComponent, origin, reason, callingUid);
}
}
@GuardedBy("mConfigLock")
- private boolean setConfigLocked(ZenModeConfig config, String reason,
- ComponentName triggeringComponent, boolean setRingerMode, int callingUid,
- boolean fromSystemOrSystemUi) {
+ private boolean setConfigLocked(ZenModeConfig config, @ConfigChangeOrigin int origin,
+ String reason, ComponentName triggeringComponent, boolean setRingerMode,
+ int callingUid) {
final long identity = Binder.clearCallingIdentity();
try {
if (config == null || !config.isValid()) {
@@ -1332,8 +1315,7 @@
if (policyChanged) {
dispatchOnPolicyChanged();
}
- updateConfigAndZenModeLocked(config, reason, setRingerMode, callingUid,
- fromSystemOrSystemUi, true);
+ updateConfigAndZenModeLocked(config, origin, reason, setRingerMode, callingUid);
mConditions.evaluateConfig(config, triggeringComponent, true /*processSubscriptions*/);
return true;
} catch (SecurityException e) {
@@ -1349,17 +1331,16 @@
* If logging is enabled, will also request logging of the outcome of this change if needed.
*/
@GuardedBy("mConfigLock")
- private void updateConfigAndZenModeLocked(ZenModeConfig config, String reason,
- boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi,
- boolean sendBroadcasts) {
+ private void updateConfigAndZenModeLocked(ZenModeConfig config, @ConfigChangeOrigin int origin,
+ String reason, boolean setRingerMode, int callingUid) {
final boolean logZenModeEvents = mFlagResolver.isEnabled(
SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS);
// Store (a copy of) all config and zen mode info prior to any changes taking effect
ZenModeEventLogger.ZenModeInfo prevInfo = new ZenModeEventLogger.ZenModeInfo(
mZenMode, mConfig, mConsolidatedPolicy);
if (!config.equals(mConfig)) {
- // schedule broadcasts
- if (Flags.modesApi() && sendBroadcasts) {
+ // Schedule broadcasts. Cannot be sent during boot, though.
+ if (Flags.modesApi() && origin != UPDATE_ORIGIN_INIT) {
for (ZenRule rule : config.automaticRules.values()) {
ZenRule original = mConfig.automaticRules.get(rule.id);
if (original != null) {
@@ -1377,15 +1358,19 @@
mConfig = config;
dispatchOnConfigChanged();
- updateAndApplyConsolidatedPolicyAndDeviceEffects(reason);
+ updateAndApplyConsolidatedPolicyAndDeviceEffects(origin, reason);
}
final String val = Integer.toString(config.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
- evaluateZenModeLocked(reason, setRingerMode);
+ evaluateZenModeLocked(origin, reason, setRingerMode);
// After all changes have occurred, log if requested
if (logZenModeEvents) {
ZenModeEventLogger.ZenModeInfo newInfo = new ZenModeEventLogger.ZenModeInfo(
mZenMode, mConfig, mConsolidatedPolicy);
+ boolean fromSystemOrSystemUi = origin == UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
+ || origin == UPDATE_ORIGIN_INIT
+ || origin == UPDATE_ORIGIN_INIT_USER
+ || origin == UPDATE_ORIGIN_RESTORE_BACKUP;
mZenModeEventLogger.maybeLogZenChange(prevInfo, newInfo, callingUid,
fromSystemOrSystemUi);
}
@@ -1416,7 +1401,8 @@
@VisibleForTesting
@GuardedBy("mConfigLock")
- protected void evaluateZenModeLocked(String reason, boolean setRingerMode) {
+ protected void evaluateZenModeLocked(@ConfigChangeOrigin int origin, String reason,
+ boolean setRingerMode) {
if (DEBUG) Log.d(TAG, "evaluateZenMode");
if (mConfig == null) return;
final int policyHashBefore = mConsolidatedPolicy == null ? 0
@@ -1426,7 +1412,7 @@
ZenLog.traceSetZenMode(zen, reason);
mZenMode = zen;
setZenModeSetting(mZenMode);
- updateAndApplyConsolidatedPolicyAndDeviceEffects(reason);
+ updateAndApplyConsolidatedPolicyAndDeviceEffects(origin, reason);
boolean shouldApplyToRinger = setRingerMode && (zen != zenBefore || (
zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
&& policyHashBefore != mConsolidatedPolicy.hashCode()));
@@ -1468,6 +1454,7 @@
}
}
+ @GuardedBy("mConfigLock")
private void applyCustomPolicy(ZenPolicy policy, ZenRule rule) {
if (rule.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
policy.apply(new ZenPolicy.Builder()
@@ -1487,7 +1474,9 @@
}
}
- private void updateAndApplyConsolidatedPolicyAndDeviceEffects(String reason) {
+ @GuardedBy("mConfigLock")
+ private void updateAndApplyConsolidatedPolicyAndDeviceEffects(@ConfigChangeOrigin int origin,
+ String reason) {
synchronized (mConfigLock) {
if (mConfig == null) return;
ZenPolicy policy = new ZenPolicy();
@@ -1519,13 +1508,13 @@
ZenDeviceEffects deviceEffects = deviceEffectsBuilder.build();
if (!deviceEffects.equals(mConsolidatedDeviceEffects)) {
mConsolidatedDeviceEffects = deviceEffects;
- mHandler.postApplyDeviceEffects();
+ mHandler.postApplyDeviceEffects(origin);
}
}
}
}
- private void applyConsolidatedDeviceEffects() {
+ private void applyConsolidatedDeviceEffects(@ConfigChangeOrigin int source) {
if (!Flags.modesApi()) {
return;
}
@@ -1536,7 +1525,7 @@
effects = mConsolidatedDeviceEffects;
}
if (applier != null) {
- applier.apply(effects);
+ applier.apply(effects, source);
}
}
@@ -1801,8 +1790,8 @@
}
@Override
- public int onSetRingerModeInternal(int ringerModeOld, int ringerModeNew, String caller,
- int ringerModeExternal, VolumePolicy policy) {
+ public int onSetRingerModeInternal(int ringerModeOld, int ringerModeNew,
+ @Nullable String caller, int ringerModeExternal, VolumePolicy policy) {
final boolean isChange = ringerModeOld != ringerModeNew;
int ringerModeExternalOut = ringerModeNew;
@@ -1839,8 +1828,9 @@
}
if (newZen != -1) {
- setManualZenMode(newZen, null, "ringerModeInternal", null,
- false /*setRingerMode*/, Process.SYSTEM_UID, true);
+ setManualZenMode(newZen, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+ "ringerModeInternal", /* caller= */ null, /* setRingerMode= */ false,
+ Process.SYSTEM_UID);
}
if (isChange || newZen != -1 || ringerModeExternal != ringerModeExternalOut) {
ZenLog.traceSetRingerModeInternal(ringerModeOld, ringerModeNew, caller,
@@ -1856,8 +1846,8 @@
}
@Override
- public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller,
- int ringerModeInternal, VolumePolicy policy) {
+ public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew,
+ @Nullable String caller, int ringerModeInternal, VolumePolicy policy) {
int ringerModeInternalOut = ringerModeNew;
final boolean isChange = ringerModeOld != ringerModeNew;
final boolean isVibrate = ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE;
@@ -1883,8 +1873,8 @@
break;
}
if (newZen != -1) {
- setManualZenMode(newZen, null, "ringerModeExternal", caller,
- false /*setRingerMode*/, Process.SYSTEM_UID, true);
+ setManualZenMode(newZen, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+ "ringerModeExternal", caller, false /*setRingerMode*/, Process.SYSTEM_UID);
}
ZenLog.traceSetRingerModeExternal(ringerModeOld, ringerModeNew, caller,
@@ -2024,11 +2014,9 @@
}
try {
final Resources res = mPm.getResourcesForApplication(packageName);
- final String fullName = res.getResourceName(resId);
-
- return fullName;
+ return res.getResourceName(resId);
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
- Log.e(TAG, "Resource name for ID=" + resId + " not found in package " + packageName
+ Slog.e(TAG, "Resource name for ID=" + resId + " not found in package " + packageName
+ ". Resource IDs may change when the application is upgraded, and the system"
+ " may not be able to find the correct resource.");
return null;
@@ -2149,9 +2137,9 @@
sendMessage(obtainMessage(MSG_RINGER_AUDIO, shouldApplyToRinger));
}
- private void postApplyDeviceEffects() {
+ private void postApplyDeviceEffects(@ConfigChangeOrigin int origin) {
removeMessages(MSG_APPLY_EFFECTS);
- sendEmptyMessage(MSG_APPLY_EFFECTS);
+ sendMessage(obtainMessage(MSG_APPLY_EFFECTS, origin, 0));
}
@Override
@@ -2168,7 +2156,8 @@
updateRingerAndAudio(shouldApplyToRinger);
break;
case MSG_APPLY_EFFECTS:
- applyConsolidatedDeviceEffects();
+ @ConfigChangeOrigin int origin = msg.arg1;
+ applyConsolidatedDeviceEffects(origin);
break;
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 4c4dafa..6f27507 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1856,7 +1856,7 @@
logExternalInputEvent(
FrameworkStatsLog
.EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__TUNED,
- sessionState.inputId, sessionState);
+ sessionActualInputId, sessionState);
}
mOnScreenInputId = sessionActualInputId;
mOnScreenSessionState = sessionState;
@@ -3119,20 +3119,20 @@
@GuardedBy("mLock")
private void logExternalInputEvent(int eventType, String inputId, SessionState sessionState) {
UserState userState = getOrCreateUserStateLocked(sessionState.userId);
- // Try finding the actual input id in inputMap. If not found, find the input id as is.
- String inputIdForLogging = getSessionActualInputId(sessionState);
- TvInputState tvInputState = userState.inputMap.get(inputIdForLogging);
+ TvInputState tvInputState = userState.inputMap.get(inputId);
if (tvInputState == null) {
- inputIdForLogging = inputId;
- tvInputState = userState.inputMap.get(inputIdForLogging);
+ Slog.w(TAG, "Cannot find input state for input id " + inputId);
+ // If input id is not found, try to find the input id of this sessionState.
+ inputId = sessionState.inputId;
+ tvInputState = userState.inputMap.get(inputId);
}
if (tvInputState == null) {
- Slog.w(TAG, "Cannot find input state for input id " + inputIdForLogging);
+ Slog.w(TAG, "Cannot find input state for sessionState.inputId " + inputId);
return;
}
TvInputInfo tvInputInfo = tvInputState.info;
if (tvInputInfo == null) {
- Slog.w(TAG, "TvInputInfo is null for input id " + inputIdForLogging);
+ Slog.w(TAG, "TvInputInfo is null for input id " + inputId);
return;
}
int inputState = tvInputState.state;
@@ -3675,7 +3675,7 @@
logExternalInputEvent(
FrameworkStatsLog
.EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__TUNED,
- mSessionState.inputId, mSessionState);
+ sessionActualInputId, mSessionState);
}
mOnScreenInputId = sessionActualInputId;
mOnScreenSessionState = mSessionState;
diff --git a/services/core/java/com/android/server/utils/AnrTimer.java b/services/core/java/com/android/server/utils/AnrTimer.java
index 2b6dffb..7b5192c 100644
--- a/services/core/java/com/android/server/utils/AnrTimer.java
+++ b/services/core/java/com/android/server/utils/AnrTimer.java
@@ -16,37 +16,22 @@
package com.android.server.utils;
-import static android.text.TextUtils.formatSimple;
-
-import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.Handler;
-import android.os.Looper;
import android.os.Message;
-import android.os.Process;
import android.os.SystemClock;
import android.os.Trace;
-import android.text.TextUtils;
import android.text.format.TimeMigrationUtils;
-import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.Log;
-import android.util.MathUtils;
-import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.Keep;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.util.RingBuffer;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* This class managers AnrTimers. An AnrTimer is a substitute for a delayed Message. In legacy
@@ -102,12 +87,6 @@
private static final long TRACE_TAG = Trace.TRACE_TAG_ACTIVITY_MANAGER;
/**
- * Enable tracing from the time a timer expires until it is accepted or discarded. This is
- * used to diagnose long latencies in the client.
- */
- private static final boolean ENABLE_TRACING = false;
-
- /**
* Return true if the feature is enabled. By default, the value is take from the Flags class
* but it can be changed for local testing.
*/
@@ -116,22 +95,13 @@
}
/**
- * The status of an ANR timer. TIMER_INVALID status is returned when an error is detected.
+ * This class allows test code to provide instance-specific overrides.
*/
- private static final int TIMER_INVALID = 0;
- private static final int TIMER_RUNNING = 1;
- private static final int TIMER_EXPIRED = 2;
-
- @IntDef(prefix = { "TIMER_" }, value = {
- TIMER_INVALID, TIMER_RUNNING, TIMER_EXPIRED
- })
- private @interface TimerStatus {}
-
- /**
- * A static list of all known AnrTimer instances, used for dumping and testing.
- */
- @GuardedBy("sAnrTimerList")
- private static final ArrayList<WeakReference<AnrTimer>> sAnrTimerList = new ArrayList<>();
+ static class Injector {
+ boolean anrTimerServiceEnabled() {
+ return AnrTimer.anrTimerServiceEnabled();
+ }
+ }
/**
* An error is defined by its issue, the operation that detected the error, the tag of the
@@ -161,6 +131,22 @@
this.arg = arg;
this.timestamp = SystemClock.elapsedRealtime();
}
+
+ /**
+ * Dump a single error to the output stream.
+ */
+ private void dump(IndentingPrintWriter ipw, int seq) {
+ ipw.format("%2d: op:%s tag:%s issue:%s arg:%s\n", seq, operation, tag, issue, arg);
+
+ final long offset = System.currentTimeMillis() - SystemClock.elapsedRealtime();
+ final long etime = offset + timestamp;
+ ipw.println(" date:" + TimeMigrationUtils.formatMillisWithFixedFormat(etime));
+ ipw.increaseIndent();
+ for (int i = 0; i < stack.length; i++) {
+ ipw.println(" " + stack[i].toString());
+ }
+ ipw.decreaseIndent();
+ }
}
/**
@@ -171,132 +157,10 @@
@GuardedBy("sErrors")
private static final RingBuffer<Error> sErrors = new RingBuffer<>(Error.class, 20);
- /**
- * A record of a single anr timer. The pid and uid are retained for reference but they do not
- * participate in the equality tests. A {@link Timer} is bound to its parent {@link AnrTimer}
- * through the owner field. Access to timer fields is guarded by the mLock of the owner.
- */
- private static class Timer {
- /** The AnrTimer that is managing this Timer. */
- final AnrTimer owner;
-
- /** The argument that uniquely identifies the Timer in the context of its current owner. */
- final Object arg;
- /** The pid of the process being tracked by this Timer. */
- final int pid;
- /** The uid of the process being tracked by this Timer as reported by the kernel. */
- final int uid;
- /** The original timeout. */
- final long timeoutMs;
-
- /** The status of the Timer. */
- @GuardedBy("owner.mLock")
- @TimerStatus
- int status;
-
- /** The absolute time the timer was startd */
- final long startedMs;
-
- /** Fields used by the native timer service. */
-
- /** The timer ID: used to exchange information with the native service. */
- int timerId;
-
- /** Fields used by the legacy timer service. */
-
- /**
- * The process's cpu delay time when the timer starts . It is meaningful only if
- * extendable is true. The cpu delay is cumulative, so the incremental delay that occurs
- * during a timer is the delay at the end of the timer minus this value. Units are in
- * milliseconds.
- */
- @GuardedBy("owner.mLock")
- long initialCpuDelayMs;
-
- /** True if the timer has been extended. */
- @GuardedBy("owner.mLock")
- boolean extended;
-
- /**
- * Fetch a new Timer. This is private. Clients should get a new timer using the obtain()
- * method.
- */
- private Timer(int pid, int uid, @Nullable Object arg, long timeoutMs,
- @NonNull AnrTimer service) {
- this.arg = arg;
- this.pid = pid;
- this.uid = uid;
- this.timerId = 0;
- this.timeoutMs = timeoutMs;
- this.startedMs = now();
- this.owner = service;
- this.initialCpuDelayMs = 0;
- this.extended = false;
- this.status = TIMER_INVALID;
- }
-
- /** Get a timer. This implementation constructs a new timer. */
- static Timer obtain(int pid, int uid, @Nullable Object arg, long timeout,
- @NonNull AnrTimer service) {
- return new Timer(pid, uid, arg, timeout, service);
- }
-
- /** Release a timer. This implementation simply drops the timer. */
- void release() {
- }
-
- /** Return the age of the timer. This is used for debugging. */
- long age() {
- return now() - startedMs;
- }
-
- /**
- * The hash code is generated from the owner and the argument. By definition, the
- * combination must be unique for the lifetime of an in-use Timer.
- */
- @Override
- public int hashCode() {
- return Objects.hash(owner, arg);
- }
-
- /**
- * The equality check compares the owner and the argument. By definition, the combination
- * must be unique for the lifetime of an in-use Timer.
- */
- @Override
- public boolean equals(Object r) {
- if (r instanceof Timer) {
- Timer t = (Timer) r;
- return Objects.equals(owner, t.owner) && Objects.equals(arg, t.arg);
- }
- return false;
- }
-
- @Override
- public String toString() {
- final int myStatus;
- synchronized (owner.mLock) {
- myStatus = status;
- }
- return "timerId=" + timerId + " pid=" + pid + " uid=" + uid
- + " " + statusString(myStatus) + " " + owner.mLabel;
- }
- }
-
/** A lock for the AnrTimer instance. */
private final Object mLock = new Object();
/**
- * The map from client argument to the associated timer.
- */
- @GuardedBy("mLock")
- private final ArrayMap<V, Timer> mTimerMap = new ArrayMap<>();
-
- /** The highwater mark of started, but not closed, timers. */
- @GuardedBy("mLock")
- private int mMaxStarted = 0;
-
- /**
* The total number of timers started.
*/
@GuardedBy("mLock")
@@ -309,176 +173,6 @@
private int mTotalErrors = 0;
/**
- * The total number of timers that have expired.
- */
- @GuardedBy("mLock")
- private int mTotalExpired = 0;
-
- /**
- * A TimerService that generates a timeout event <n> milliseconds in the future. See the
- * class documentation for an explanation of the operations.
- */
- private abstract class TimerService {
- /** Start a timer. The timeout must be initialized. */
- abstract boolean start(@NonNull Timer timer);
-
- abstract void cancel(@NonNull Timer timer);
-
- abstract void accept(@NonNull Timer timer);
-
- abstract void discard(@NonNull Timer timer);
- }
-
- /**
- * A class to assist testing. All methods are null by default but can be overridden as
- * necessary for a test.
- */
- @VisibleForTesting
- static class Injector {
- private final Handler mReferenceHandler;
-
- Injector(@NonNull Handler handler) {
- mReferenceHandler = handler;
- }
-
- /**
- * Return a handler for the given Callback, based on the reference handler. The handler
- * might be mocked, in which case it does not have a valid Looper. In this case, use the
- * main Looper.
- */
- @NonNull
- Handler newHandler(@NonNull Handler.Callback callback) {
- Looper looper = mReferenceHandler.getLooper();
- if (looper == null) looper = Looper.getMainLooper();
- return new Handler(looper, callback);
- }
-
- /**
- * Return a CpuTracker. The default behavior is to create a new CpuTracker but this changes
- * for unit tests.
- **/
- @NonNull
- CpuTracker newTracker() {
- return new CpuTracker();
- }
-
- /** Return true if the feature is enabled. */
- boolean isFeatureEnabled() {
- return anrTimerServiceEnabled();
- }
- }
-
- /**
- * A helper class to measure CPU delays. Given a process ID, this class will return the
- * cumulative CPU delay for the PID, since process inception. This class is defined to assist
- * testing.
- */
- @VisibleForTesting
- static class CpuTracker {
- /**
- * The parameter to ProcessCpuTracker indicates that statistics should be collected on a
- * single process and not on the collection of threads associated with that process.
- */
- private final ProcessCpuTracker mCpu = new ProcessCpuTracker(false);
-
- /** A simple wrapper to fetch the delay. This method can be overridden for testing. */
- long delay(int pid) {
- return mCpu.getCpuDelayTimeForPid(pid);
- }
- }
-
- /**
- * The "user-space" implementation of the timer service. This service uses its own message
- * handler to create timeouts.
- */
- private class HandlerTimerService extends TimerService {
- /** The lock for this handler */
- private final Object mLock = new Object();
-
- /** The message handler for scheduling future events. */
- private final Handler mHandler;
-
- /** The interface to fetch process statistics that might extend an ANR timeout. */
- private final CpuTracker mCpu;
-
- /** Create a HandlerTimerService that directly uses the supplied handler and tracker. */
- @VisibleForTesting
- HandlerTimerService(@NonNull Injector injector) {
- mHandler = injector.newHandler(this::expires);
- mCpu = injector.newTracker();
- }
-
- /** Post a message with the specified timeout. The timer is not modified. */
- private void post(@NonNull Timer t, long timeoutMillis) {
- final Message msg = mHandler.obtainMessage();
- msg.obj = t;
- mHandler.sendMessageDelayed(msg, timeoutMillis);
- }
-
- /**
- * The local expiration handler first attempts to compute a timer extension. If the timer
- * should be extended, it is rescheduled in the future (granting more time to the
- * associated process). If the timer should not be extended then the timeout is delivered
- * to the client.
- *
- * A process is extended to account for the time the process was swapped out and was not
- * runnable through no fault of its own. A timer can only be extended once and only if
- * the AnrTimer permits extensions. Finally, a timer will never be extended by more than
- * the original timeout, so the total timeout will never be more than twice the originally
- * configured timeout.
- */
- private boolean expires(Message msg) {
- Timer t = (Timer) msg.obj;
- synchronized (mLock) {
- long extension = 0;
- if (mExtend && !t.extended) {
- extension = mCpu.delay(t.pid) - t.initialCpuDelayMs;
- if (extension < 0) extension = 0;
- if (extension > t.timeoutMs) extension = t.timeoutMs;
- t.extended = true;
- }
- if (extension > 0) {
- post(t, extension);
- } else {
- onExpiredLocked(t);
- }
- }
- return true;
- }
-
- @GuardedBy("mLock")
- @Override
- boolean start(@NonNull Timer t) {
- if (mExtend) {
- t.initialCpuDelayMs = mCpu.delay(t.pid);
- }
- post(t, t.timeoutMs);
- return true;
- }
-
- @Override
- void cancel(@NonNull Timer t) {
- mHandler.removeMessages(0, t);
- }
-
- @Override
- void accept(@NonNull Timer t) {
- // Nothing to do.
- }
-
- @Override
- void discard(@NonNull Timer t) {
- // Nothing to do.
- }
-
- /** The string identifies this subclass of AnrTimerService as being based on handlers. */
- @Override
- public String toString() {
- return "handler";
- }
- }
-
- /**
* The handler for messages sent from this instance.
*/
private final Handler mHandler;
@@ -499,18 +193,6 @@
private final boolean mExtend;
/**
- * The timer service to use for this AnrTimer.
- */
- private final TimerService mTimerService;
-
- /**
- * Whether or not canceling a non-existent timer is an error. Clients often cancel freely
- * preemptively, without knowing if the timer was ever started. Keeping this variable true
- * means that such behavior is not an error.
- */
- private final boolean mLenientCancel = true;
-
- /**
* The top-level switch for the feature enabled or disabled.
*/
private final FeatureSwitch mFeature;
@@ -528,40 +210,34 @@
* AnrTimer may extend the individual timer rather than immediately delivering the timeout to
* the client. The extension policy is not part of the instance.
*
- * This method accepts an {@link #Injector} to tune behavior for testing. This method should
- * not be called directly by regular clients.
- *
* @param handler The handler to which the expiration message will be delivered.
* @param what The "what" parameter for the expiration message.
* @param label A name for this instance.
* @param extend A flag to indicate if expired timers can be granted extensions.
- * @param injector An {@link #Injector} to tune behavior for testing.
+ * @param injector An injector to provide overrides for testing.
*/
@VisibleForTesting
AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend,
- @NonNull Injector injector) {
+ @NonNull Injector injector) {
mHandler = handler;
mWhat = what;
mLabel = label;
mExtend = extend;
- boolean enabled = injector.isFeatureEnabled();
- if (!enabled) {
- mFeature = new FeatureDisabled();
- mTimerService = null;
- } else {
- mFeature = new FeatureEnabled();
- mTimerService = new HandlerTimerService(injector);
-
- synchronized (sAnrTimerList) {
- sAnrTimerList.add(new WeakReference(this));
- }
- }
- Log.i(TAG, formatSimple("created %s label: \"%s\"", mTimerService, label));
+ mFeature = new FeatureDisabled();
}
/**
- * Create an AnrTimer instance with the default {@link #Injector}. See {@link AnrTimer(Handler,
- * int, String, boolean, Injector} for a functional description.
+ * Create one AnrTimer instance. The instance is given a handler and a "what". Individual
+ * timers are started with {@link #start}. If a timer expires, then a {@link Message} is sent
+ * immediately to the handler with {@link Message.what} set to what and {@link Message.obj} set
+ * to the timer key.
+ *
+ * AnrTimer instances have a label, which must be unique. The label is used for reporting and
+ * debug.
+ *
+ * If an individual timer expires internally, and the "extend" parameter is true, then the
+ * AnrTimer may extend the individual timer rather than immediately delivering the timeout to
+ * the client. The extension policy is not part of the instance.
*
* @param handler The handler to which the expiration message will be delivered.
* @param what The "what" parameter for the expiration message.
@@ -569,7 +245,7 @@
* @param extend A flag to indicate if expired timers can be granted extensions.
*/
public AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend) {
- this(handler, what, label, extend, new Injector(handler));
+ this(handler, what, label, extend, new Injector());
}
/**
@@ -596,105 +272,17 @@
}
/**
- * Start a trace on the timer. The trace is laid down in the AnrTimerTrack.
- */
- private void traceBegin(Timer t, String what) {
- if (ENABLE_TRACING) {
- final String label = formatSimple("%s(%d,%d,%s)", what, t.pid, t.uid, mLabel);
- final int cookie = t.hashCode();
- Trace.asyncTraceForTrackBegin(TRACE_TAG, TRACK, label, cookie);
- }
- }
-
- /**
- * End a trace on the timer.
- */
- private void traceEnd(Timer t) {
- if (ENABLE_TRACING) {
- final int cookie = t.hashCode();
- Trace.asyncTraceForTrackEnd(TRACE_TAG, TRACK, cookie);
- }
- }
-
- /**
- * Return the string representation for a timer status.
- */
- private static String statusString(int s) {
- switch (s) {
- case TIMER_INVALID: return "invalid";
- case TIMER_RUNNING: return "running";
- case TIMER_EXPIRED: return "expired";
- }
- return formatSimple("unknown: %d", s);
- }
-
- /**
- * Delete the timer associated with arg from the maps and return it. Return null if the timer
- * was not found.
- */
- @GuardedBy("mLock")
- private Timer removeLocked(V arg) {
- Timer timer = mTimerMap.remove(arg);
- return timer;
- }
-
- /**
- * Return the number of timers currently running.
- */
- @VisibleForTesting
- static int sizeOfTimerList() {
- synchronized (sAnrTimerList) {
- int totalTimers = 0;
- for (int i = 0; i < sAnrTimerList.size(); i++) {
- AnrTimer client = sAnrTimerList.get(i).get();
- if (client != null) totalTimers += client.mTimerMap.size();
- }
- return totalTimers;
- }
- }
-
- /**
- * Clear out all existing timers. This will lead to unexpected behavior if used carelessly.
- * It is available only for testing. It returns the number of times that were actually
- * erased.
- */
- @VisibleForTesting
- static int resetTimerListForHermeticTest() {
- synchronized (sAnrTimerList) {
- int mapLen = 0;
- for (int i = 0; i < sAnrTimerList.size(); i++) {
- AnrTimer client = sAnrTimerList.get(i).get();
- if (client != null) {
- mapLen += client.mTimerMap.size();
- client.mTimerMap.clear();
- }
- }
- if (mapLen > 0) {
- Log.w(TAG, formatSimple("erasing timer list: clearing %d timers", mapLen));
- }
- return mapLen;
- }
- }
-
- /**
- * Generate a log message for a timer.
- */
- private void report(@NonNull Timer timer, @NonNull String msg) {
- Log.i(TAG, msg + " " + timer + " " + Objects.toString(timer.arg));
- }
-
- /**
* The FeatureSwitch class provides a quick switch between feature-enabled behavior and
* feature-disabled behavior.
*/
private abstract class FeatureSwitch {
- abstract boolean start(@NonNull V arg, int pid, int uid, long timeoutMs);
+ abstract void start(@NonNull V arg, int pid, int uid, long timeoutMs);
- abstract boolean cancel(@NonNull V arg);
+ abstract void cancel(@NonNull V arg);
- abstract boolean accept(@NonNull V arg);
+ abstract void accept(@NonNull V arg);
- abstract boolean discard(@NonNull V arg);
+ abstract void discard(@NonNull V arg);
abstract boolean enabled();
}
@@ -706,29 +294,25 @@
private class FeatureDisabled extends FeatureSwitch {
/** Start a timer by sending a message to the client's handler. */
@Override
- boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) {
+ void start(@NonNull V arg, int pid, int uid, long timeoutMs) {
final Message msg = mHandler.obtainMessage(mWhat, arg);
mHandler.sendMessageDelayed(msg, timeoutMs);
- return true;
}
/** Cancel a timer by removing the message from the client's handler. */
@Override
- boolean cancel(@NonNull V arg) {
+ void cancel(@NonNull V arg) {
mHandler.removeMessages(mWhat, arg);
- return true;
}
/** accept() is a no-op when the feature is disabled. */
@Override
- boolean accept(@NonNull V arg) {
- return true;
+ void accept(@NonNull V arg) {
}
/** discard() is a no-op when the feature is disabled. */
@Override
- boolean discard(@NonNull V arg) {
- return true;
+ void discard(@NonNull V arg) {
}
/** The feature is not enabled. */
@@ -739,113 +323,6 @@
}
/**
- * The FeatureEnabled class enables the AnrTimer logic. It is used when the AnrTimer service
- * is enabled via Flags.anrTimerServiceEnabled.
- */
- private class FeatureEnabled extends FeatureSwitch {
-
- /**
- * Start a timer.
- */
- @Override
- boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) {
- final Timer timer = Timer.obtain(pid, uid, arg, timeoutMs, AnrTimer.this);
- synchronized (mLock) {
- Timer old = mTimerMap.get(arg);
- // There is an existing timer. If the timer was running, then cancel the running
- // timer and restart it. If the timer was expired record a protocol error and
- // discard the expired timer.
- if (old != null) {
- if (old.status == TIMER_EXPIRED) {
- restartedLocked(old.status, arg);
- discard(arg);
- } else {
- cancel(arg);
- }
- }
- if (mTimerService.start(timer)) {
- timer.status = TIMER_RUNNING;
- mTimerMap.put(arg, timer);
- mTotalStarted++;
- mMaxStarted = Math.max(mMaxStarted, mTimerMap.size());
- if (DEBUG) report(timer, "start");
- return true;
- } else {
- Log.e(TAG, "AnrTimer.start failed");
- return false;
- }
- }
- }
-
- /**
- * Cancel a timer. Return false if the timer was not found.
- */
- @Override
- boolean cancel(@NonNull V arg) {
- synchronized (mLock) {
- Timer timer = removeLocked(arg);
- if (timer == null) {
- if (!mLenientCancel) notFoundLocked("cancel", arg);
- return false;
- }
- mTimerService.cancel(timer);
- // There may be an expiration message in flight. Cancel it.
- mHandler.removeMessages(mWhat, arg);
- if (DEBUG) report(timer, "cancel");
- timer.release();
- return true;
- }
- }
-
- /**
- * Accept a timer in the framework-level handler. The timeout has been accepted and the
- * timeout handler is executing. Return false if the timer was not found.
- */
- @Override
- boolean accept(@NonNull V arg) {
- synchronized (mLock) {
- Timer timer = removeLocked(arg);
- if (timer == null) {
- notFoundLocked("accept", arg);
- return false;
- }
- mTimerService.accept(timer);
- traceEnd(timer);
- if (DEBUG) report(timer, "accept");
- timer.release();
- return true;
- }
- }
-
- /**
- * Discard a timer in the framework-level handler. For whatever reason, the timer is no
- * longer interesting. No statistics are collected. Return false if the time was not
- * found.
- */
- @Override
- boolean discard(@NonNull V arg) {
- synchronized (mLock) {
- Timer timer = removeLocked(arg);
- if (timer == null) {
- notFoundLocked("discard", arg);
- return false;
- }
- mTimerService.discard(timer);
- traceEnd(timer);
- if (DEBUG) report(timer, "discard");
- timer.release();
- return true;
- }
- }
-
- /** The feature is enabled. */
- @Override
- boolean enabled() {
- return true;
- }
- }
-
- /**
* Start a timer associated with arg. The same object must be used to cancel, accept, or
* discard a timer later. If a timer already exists with the same arg, then the existing timer
* is canceled and a new timer is created.
@@ -854,32 +331,27 @@
* @param pid The Linux process ID of the target being timed.
* @param uid The Linux user ID of the target being timed.
* @param timeoutMs The timer timeout, in milliseconds.
- * @return true if the timer was successfully created.
*/
- public boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) {
- return mFeature.start(arg, pid, uid, timeoutMs);
+ public void start(@NonNull V arg, int pid, int uid, long timeoutMs) {
+ mFeature.start(arg, pid, uid, timeoutMs);
}
/**
* Cancel the running timer associated with arg. The timer is forgotten. If the timer has
* expired, the call is treated as a discard. No errors are reported if the timer does not
* exist or if the timer has expired.
- *
- * @return true if the timer was found and was running.
*/
- public boolean cancel(@NonNull V arg) {
- return mFeature.cancel(arg);
+ public void cancel(@NonNull V arg) {
+ mFeature.cancel(arg);
}
/**
* Accept the expired timer associated with arg. This indicates that the caller considers the
* timer expiration to be a true ANR. (See {@link #discard} for an alternate response.) It is
* an error to accept a running timer, however the running timer will be canceled.
- *
- * @return true if the timer was found and was expired.
*/
- public boolean accept(@NonNull V arg) {
- return mFeature.accept(arg);
+ public void accept(@NonNull V arg) {
+ mFeature.accept(arg);
}
/**
@@ -889,24 +361,9 @@
* such a process could be stopped at a breakpoint and its failure to respond would not be an
* error. It is an error to discard a running timer, however the running timer will be
* canceled.
- *
- * @return true if the timer was found and was expired.
*/
- public boolean discard(@NonNull V arg) {
- return mFeature.discard(arg);
- }
-
- /**
- * The notifier that a timer has fired. The timer is not modified.
- */
- @GuardedBy("mLock")
- private void onExpiredLocked(@NonNull Timer timer) {
- if (DEBUG) report(timer, "expire");
- traceBegin(timer, "expired");
- mHandler.sendMessage(Message.obtain(mHandler, mWhat, timer.arg));
- synchronized (mLock) {
- mTotalExpired++;
- }
+ public void discard(@NonNull V arg) {
+ mFeature.discard(arg);
}
/**
@@ -916,9 +373,7 @@
synchronized (mLock) {
pw.format("timer: %s\n", mLabel);
pw.increaseIndent();
- pw.format("started=%d maxStarted=%d running=%d expired=%d error=%d\n",
- mTotalStarted, mMaxStarted, mTimerMap.size(),
- mTotalExpired, mTotalErrors);
+ pw.format("started=%d errors=%d\n", mTotalStarted, mTotalErrors);
pw.decreaseIndent();
}
}
@@ -931,10 +386,20 @@
}
/**
- * The current time in milliseconds.
+ * Dump all errors to the output stream.
*/
- private static long now() {
- return SystemClock.uptimeMillis();
+ private static void dumpErrors(IndentingPrintWriter ipw) {
+ Error errors[];
+ synchronized (sErrors) {
+ if (sErrors.size() == 0) return;
+ errors = sErrors.toArray();
+ }
+ ipw.println("Errors");
+ ipw.increaseIndent();
+ for (int i = 0; i < errors.length; i++) {
+ if (errors[i] != null) errors[i].dump(ipw, i);
+ }
+ ipw.decreaseIndent();
}
/**
@@ -966,60 +431,12 @@
}
/**
- * Log an error about a timer that is started when there is an existing timer.
- */
- @GuardedBy("mLock")
- private void restartedLocked(@TimerStatus int status, Object arg) {
- recordErrorLocked("start", status == TIMER_EXPIRED ? "autoDiscard" : "autoCancel", arg);
- }
-
- /**
- * Dump a single error to the output stream.
- */
- private static void dump(IndentingPrintWriter ipw, int seq, Error err) {
- ipw.format("%2d: op:%s tag:%s issue:%s arg:%s\n", seq, err.operation, err.tag,
- err.issue, err.arg);
-
- final long offset = System.currentTimeMillis() - SystemClock.elapsedRealtime();
- final long etime = offset + err.timestamp;
- ipw.println(" date:" + TimeMigrationUtils.formatMillisWithFixedFormat(etime));
- ipw.increaseIndent();
- for (int i = 0; i < err.stack.length; i++) {
- ipw.println(" " + err.stack[i].toString());
- }
- ipw.decreaseIndent();
- }
-
- /**
- * Dump all errors to the output stream.
- */
- private static void dumpErrors(IndentingPrintWriter ipw) {
- Error errors[];
- synchronized (sErrors) {
- if (sErrors.size() == 0) return;
- errors = sErrors.toArray();
- }
- ipw.println("Errors");
- ipw.increaseIndent();
- for (int i = 0; i < errors.length; i++) {
- if (errors[i] != null) dump(ipw, i, errors[i]);
- }
- ipw.decreaseIndent();
- }
-
- /**
* Dumpsys output.
*/
public static void dump(@NonNull PrintWriter pw, boolean verbose) {
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
ipw.println("AnrTimer statistics");
ipw.increaseIndent();
- synchronized (sAnrTimerList) {
- for (int i = 0; i < sAnrTimerList.size(); i++) {
- AnrTimer client = sAnrTimerList.get(i).get();
- if (client != null) client.dump(ipw);
- }
- }
if (verbose) dumpErrors(ipw);
ipw.format("AnrTimerEnd\n");
ipw.decreaseIndent();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1861b2e..3f20624 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1649,12 +1649,13 @@
mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
- void sendNewConfiguration() {
+ /** Returns {@code true} if the display configuration is changed. */
+ boolean sendNewConfiguration() {
if (!isReady()) {
- return;
+ return false;
}
if (mRemoteDisplayChangeController.isWaitingForRemoteDisplayChange()) {
- return;
+ return false;
}
final Transition.ReadyCondition displayConfig = mTransitionController.isCollecting()
@@ -1669,7 +1670,7 @@
displayConfig.meet();
}
if (configUpdated) {
- return;
+ return true;
}
// The display configuration doesn't change. If there is a launching transformed app, that
@@ -1687,6 +1688,7 @@
setLayoutNeeded();
mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
+ return false;
}
@Override
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index 23c135a..03574029 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -26,6 +26,7 @@
import android.view.Display.Mode;
import android.view.DisplayInfo;
import android.view.Surface;
+import android.view.SurfaceControl;
import android.view.SurfaceControl.RefreshRateRange;
import java.util.HashMap;
@@ -191,26 +192,35 @@
public static class FrameRateVote {
float mRefreshRate;
@Surface.FrameRateCompatibility int mCompatibility;
+ @SurfaceControl.FrameRateSelectionStrategy int mSelectionStrategy;
- FrameRateVote(float refreshRate, @Surface.FrameRateCompatibility int compatibility) {
- update(refreshRate, compatibility);
+
+
+ FrameRateVote(float refreshRate, @Surface.FrameRateCompatibility int compatibility,
+ @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy) {
+ update(refreshRate, compatibility, selectionStrategy);
}
FrameRateVote() {
reset();
}
- boolean update(float refreshRate, @Surface.FrameRateCompatibility int compatibility) {
- if (!refreshRateEquals(refreshRate) || mCompatibility != compatibility) {
+ boolean update(float refreshRate, @Surface.FrameRateCompatibility int compatibility,
+ @SurfaceControl.FrameRateSelectionStrategy int selectionStrategy) {
+ if (!refreshRateEquals(refreshRate)
+ || mCompatibility != compatibility
+ || mSelectionStrategy != selectionStrategy) {
mRefreshRate = refreshRate;
mCompatibility = compatibility;
+ mSelectionStrategy = selectionStrategy;
return true;
}
return false;
}
boolean reset() {
- return update(0, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+ return update(0, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_PROPAGATE);
}
@Override
@@ -221,17 +231,20 @@
FrameRateVote other = (FrameRateVote) o;
return refreshRateEquals(other.mRefreshRate)
- && mCompatibility == other.mCompatibility;
+ && mCompatibility == other.mCompatibility
+ && mSelectionStrategy == other.mSelectionStrategy;
}
@Override
public int hashCode() {
- return Objects.hash(mRefreshRate, mCompatibility);
+ return Objects.hash(mRefreshRate, mCompatibility, mSelectionStrategy);
+
}
@Override
public String toString() {
- return "mRefreshRate=" + mRefreshRate + ", mCompatibility=" + mCompatibility;
+ return "mRefreshRate=" + mRefreshRate + ", mCompatibility=" + mCompatibility
+ + ", mSelectionStrategy=" + mSelectionStrategy;
}
private boolean refreshRateEquals(float refreshRate) {
@@ -265,7 +278,8 @@
for (Display.Mode mode : mDisplayInfo.supportedModes) {
if (preferredModeId == mode.getModeId()) {
return w.mFrameRateVote.update(mode.getRefreshRate(),
- Surface.FRAME_RATE_COMPATIBILITY_EXACT);
+ Surface.FRAME_RATE_COMPATIBILITY_EXACT,
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
}
}
}
@@ -273,7 +287,8 @@
if (w.mAttrs.preferredRefreshRate > 0) {
return w.mFrameRateVote.update(w.mAttrs.preferredRefreshRate,
- Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+ Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
}
// If the app didn't set a preferred mode id or refresh rate, but it is part of the deny
@@ -282,7 +297,8 @@
final String packageName = w.getOwningPackage();
if (mHighRefreshRateDenylist.isDenylisted(packageName)) {
return w.mFrameRateVote.update(mLowRefreshRateMode.getRefreshRate(),
- Surface.FRAME_RATE_COMPATIBILITY_EXACT);
+ Surface.FRAME_RATE_COMPATIBILITY_EXACT,
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
}
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 76c4a0e..f020bfa 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -2868,8 +2868,7 @@
final WindowContainer<?> wc = mParticipants.valueAt(i);
final DisplayContent dc = wc.asDisplayContent();
if (dc == null || !mChanges.get(dc).hasChanged()) continue;
- final int originalSeq = dc.getConfiguration().seq;
- dc.sendNewConfiguration();
+ final boolean changed = dc.sendNewConfiguration();
// Set to ready if no other change controls the ready state. But if there is, such as
// if an activity is pausing, it will call setReady(ar, false) and wait for the next
// resumed activity. Then do not set to ready because the transition only contains
@@ -2877,7 +2876,7 @@
if (!mReadyTrackerOld.mUsed) {
setReady(dc, true);
}
- if (originalSeq == dc.getConfiguration().seq) continue;
+ if (!changed) continue;
// If the update is deferred, sendNewConfiguration won't deliver new configuration to
// clients, then it is the caller's responsibility to deliver the changes.
if (mController.mAtm.mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9c4ad5c..4e9d23c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -178,6 +178,7 @@
import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
+import static com.android.window.flags.Flags.explicitRefreshRateHints;
import static com.android.window.flags.Flags.secureWindowState;
import static com.android.window.flags.Flags.surfaceTrustedOverlay;
@@ -5207,9 +5208,13 @@
boolean voteChanged = refreshRatePolicy.updateFrameRateVote(this);
if (voteChanged) {
- getPendingTransaction().setFrameRate(
- mSurfaceControl, mFrameRateVote.mRefreshRate,
- mFrameRateVote.mCompatibility, Surface.CHANGE_FRAME_RATE_ALWAYS);
+ getPendingTransaction()
+ .setFrameRate(mSurfaceControl, mFrameRateVote.mRefreshRate,
+ mFrameRateVote.mCompatibility, Surface.CHANGE_FRAME_RATE_ALWAYS);
+ if (explicitRefreshRateHints()) {
+ getPendingTransaction().setFrameRateSelectionStrategy(mSurfaceControl,
+ mFrameRateVote.mSelectionStrategy);
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index a9967f6..71d64cf 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -532,12 +532,11 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
- public void testTwoFingerTripleTap_StateIsIdle_shouldInActivated() {
+ public void testTwoFingerDoubleTap_StateIsIdle_shouldInActivated() {
goFromStateIdleTo(STATE_IDLE);
twoFingerTap();
twoFingerTap();
- twoFingerTap();
assertIn(STATE_ACTIVATED);
verify(mMockMagnificationLogger, never()).logMagnificationTripleTap(anyBoolean());
@@ -546,13 +545,12 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
- public void testTwoFingerTripleTap_StateIsActivated_shouldInIdle() {
+ public void testTwoFingerDoubleTap_StateIsActivated_shouldInIdle() {
goFromStateIdleTo(STATE_ACTIVATED);
reset(mMockMagnificationLogger);
twoFingerTap();
twoFingerTap();
- twoFingerTap();
assertIn(STATE_IDLE);
verify(mMockMagnificationLogger, never()).logMagnificationTripleTap(anyBoolean());
@@ -561,11 +559,10 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
- public void testTwoFingerTripleTapAndHold_StateIsIdle_shouldZoomsImmediately() {
+ public void testTwoFingerDoubleTapAndHold_StateIsIdle_shouldZoomsImmediately() {
goFromStateIdleTo(STATE_IDLE);
twoFingerTap();
- twoFingerTap();
twoFingerTapAndHold();
assertIn(STATE_NON_ACTIVATED_ZOOMED_TMP);
@@ -575,11 +572,10 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
- public void testTwoFingerTripleSwipeAndHold_StateIsIdle_shouldZoomsImmediately() {
+ public void testTwoFingerDoubleSwipeAndHold_StateIsIdle_shouldZoomsImmediately() {
goFromStateIdleTo(STATE_IDLE);
twoFingerTap();
- twoFingerTap();
twoFingerSwipeAndHold();
assertIn(STATE_NON_ACTIVATED_ZOOMED_TMP);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index a3b67ae..c99e040 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -72,15 +72,15 @@
public static final int STATE_SHOW_MAGNIFIER_TRIPLE_TAP = 4;
public static final int STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD = 5;
public static final int STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD = 6;
- public static final int STATE_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP = 7;
- public static final int STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD = 8;
- public static final int STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD = 9;
+ public static final int STATE_SHOW_MAGNIFIER_TWO_FINGER_DOUBLE_TAP = 7;
+ public static final int STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_DOUBLE_TAP_AND_HOLD = 8;
+ public static final int STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_DOUBLE_TAP_AND_HOLD = 9;
//TODO: Test it after can injecting Handler to GestureMatcher is available.
public static final int FIRST_STATE = STATE_IDLE;
public static final int LAST_STATE = STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD;
public static final int LAST_STATE_WITH_MULTI_FINGER =
- STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD;
+ STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_DOUBLE_TAP_AND_HOLD;
// Co-prime x and y, to potentially catch x-y-swapped errors
public static final float DEFAULT_TAP_X = 301;
@@ -257,15 +257,15 @@
break;
case STATE_SHOW_MAGNIFIER_SHORTCUT:
case STATE_SHOW_MAGNIFIER_TRIPLE_TAP:
- case STATE_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP:
+ case STATE_SHOW_MAGNIFIER_TWO_FINGER_DOUBLE_TAP:
check(isWindowMagnifierEnabled(DISPLAY_0), state);
check(mWindowMagnificationGestureHandler.mCurrentState
== mWindowMagnificationGestureHandler.mDetectingState, state);
break;
case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD:
case STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD:
- case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD:
- case STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD: {
+ case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_DOUBLE_TAP_AND_HOLD:
+ case STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_DOUBLE_TAP_AND_HOLD: {
check(isWindowMagnifierEnabled(DISPLAY_0), state);
check(mWindowMagnificationGestureHandler.mCurrentState
== mWindowMagnificationGestureHandler.mViewportDraggingState, state);
@@ -337,8 +337,7 @@
tapAndHold();
}
break;
- case STATE_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP: {
- twoFingerTap();
+ case STATE_SHOW_MAGNIFIER_TWO_FINGER_DOUBLE_TAP: {
twoFingerTap();
twoFingerTap();
// Wait for two-finger tap gesture completed.
@@ -346,17 +345,15 @@
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
break;
- case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD: {
- twoFingerTap();
+ case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_DOUBLE_TAP_AND_HOLD: {
twoFingerTap();
twoFingerTapAndHold();
}
break;
- case STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD: {
+ case STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_DOUBLE_TAP_AND_HOLD: {
// enabled then perform two finger triple tap and hold gesture
goFromStateIdleTo(STATE_SHOW_MAGNIFIER_SHORTCUT);
twoFingerTap();
- twoFingerTap();
twoFingerTapAndHold();
}
break;
@@ -394,16 +391,15 @@
}
break;
case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD:
- case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD:
+ case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_DOUBLE_TAP_AND_HOLD:
send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
break;
case STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD:
- case STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD:
+ case STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_DOUBLE_TAP_AND_HOLD:
send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
returnToNormalFrom(STATE_SHOW_MAGNIFIER_SHORTCUT);
break;
- case STATE_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP: {
- twoFingerTap();
+ case STATE_SHOW_MAGNIFIER_TWO_FINGER_DOUBLE_TAP: {
twoFingerTap();
twoFingerTap();
// Wait for two-finger tap gesture completed.
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index cc3c880..f1c1dc3 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -266,18 +266,22 @@
.adoptShellPermissionIdentity(Manifest.permission.BLUETOOTH_PRIVILEGED);
// no metadata set
- assertTrue(mFakeBtDevice.setMetadata(BluetoothDevice.METADATA_DEVICE_TYPE,
- DEVICE_TYPE_DEFAULT.getBytes()));
- assertFalse(
- mAudioDeviceBroker.isBluetoothAudioDeviceCategoryFixed(
- mFakeBtDevice.getAddress()));
+ if (mFakeBtDevice.setMetadata(BluetoothDevice.METADATA_DEVICE_TYPE,
+ DEVICE_TYPE_DEFAULT.getBytes())) {
+ assertFalse(mAudioDeviceBroker.isBluetoothAudioDeviceCategoryFixed(
+ mFakeBtDevice.getAddress()));
+ }
// metadata set
- assertTrue(mFakeBtDevice.setMetadata(BluetoothDevice.METADATA_DEVICE_TYPE,
- DEVICE_TYPE_HEADSET.getBytes()));
- assertTrue(mAudioDeviceBroker.isBluetoothAudioDeviceCategoryFixed(
- mFakeBtDevice.getAddress()));
+ if (mFakeBtDevice.setMetadata(BluetoothDevice.METADATA_DEVICE_TYPE,
+ DEVICE_TYPE_HEADSET.getBytes())) {
+ assertTrue(mAudioDeviceBroker.isBluetoothAudioDeviceCategoryFixed(
+ mFakeBtDevice.getAddress()));
+ }
} finally {
+ // reset the metadata device type
+ mFakeBtDevice.setMetadata(BluetoothDevice.METADATA_DEVICE_TYPE,
+ DEVICE_TYPE_DEFAULT.getBytes());
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.dropShellPermissionIdentity();
}
@@ -304,25 +308,30 @@
.adoptShellPermissionIdentity(Manifest.permission.BLUETOOTH_PRIVILEGED);
// no metadata set
- assertTrue(mFakeBtDevice.setMetadata(BluetoothDevice.METADATA_DEVICE_TYPE,
- DEVICE_TYPE_DEFAULT.getBytes()));
- assertEquals(AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER,
- mAudioDeviceBroker.getAndUpdateBtAdiDeviceStateCategoryForAddress(
- mFakeBtDevice.getAddress()));
- verify(mMockAudioService,
- timeout(MAX_MESSAGE_HANDLING_DELAY_MS).times(0)).onUpdatedAdiDeviceState(
- eq(devState));
+ if (mFakeBtDevice.setMetadata(BluetoothDevice.METADATA_DEVICE_TYPE,
+ DEVICE_TYPE_DEFAULT.getBytes())) {
+ assertEquals(AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER,
+ mAudioDeviceBroker.getAndUpdateBtAdiDeviceStateCategoryForAddress(
+ mFakeBtDevice.getAddress()));
+ verify(mMockAudioService,
+ timeout(MAX_MESSAGE_HANDLING_DELAY_MS).times(0)).onUpdatedAdiDeviceState(
+ eq(devState));
+ }
// metadata set
- assertTrue(mFakeBtDevice.setMetadata(BluetoothDevice.METADATA_DEVICE_TYPE,
- DEVICE_TYPE_HEADSET.getBytes()));
- assertEquals(AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES,
- mAudioDeviceBroker.getAndUpdateBtAdiDeviceStateCategoryForAddress(
- mFakeBtDevice.getAddress()));
- verify(mMockAudioService,
- timeout(MAX_MESSAGE_HANDLING_DELAY_MS)).onUpdatedAdiDeviceState(
- any());
+ if (mFakeBtDevice.setMetadata(BluetoothDevice.METADATA_DEVICE_TYPE,
+ DEVICE_TYPE_HEADSET.getBytes())) {
+ assertEquals(AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES,
+ mAudioDeviceBroker.getAndUpdateBtAdiDeviceStateCategoryForAddress(
+ mFakeBtDevice.getAddress()));
+ verify(mMockAudioService,
+ timeout(MAX_MESSAGE_HANDLING_DELAY_MS)).onUpdatedAdiDeviceState(
+ any());
+ }
} finally {
+ // reset the metadata device type
+ mFakeBtDevice.setMetadata(BluetoothDevice.METADATA_DEVICE_TYPE,
+ DEVICE_TYPE_DEFAULT.getBytes());
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.dropShellPermissionIdentity();
}
diff --git a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
index 330dbb8..861d14a 100644
--- a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
@@ -30,13 +30,19 @@
import com.android.internal.annotations.GuardedBy;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@SmallTest
@Presubmit
+@RunWith(Parameterized.class)
public class AnrTimerTest {
// The commonly used message timeout key.
@@ -106,6 +112,16 @@
}
/**
+ * Force AnrTimer to use the test parameter for the feature flag.
+ */
+ class TestInjector extends AnrTimer.Injector {
+ @Override
+ boolean anrTimerServiceEnabled() {
+ return mEnabled;
+ }
+ }
+
+ /**
* An instrumented AnrTimer.
*/
private static class TestAnrTimer extends AnrTimer<TestArg> {
@@ -137,6 +153,17 @@
assertEquals(actual.what, MSG_TIMEOUT);
}
+ @Parameters(name = "featureEnabled={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] { {false}, {true} });
+ }
+
+ /** True if the feature is enabled. */
+ private boolean mEnabled;
+
+ public AnrTimerTest(boolean featureEnabled) {
+ mEnabled = featureEnabled;
+ }
/**
* Verify that a simple expiration succeeds. The timer is started for 10ms. The test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
index 5febd02..8936dc6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -16,13 +16,28 @@
package com.android.server.notification;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_APP;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_USER;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
import android.app.UiModeManager;
import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.hardware.display.ColorDisplayManager;
import android.os.PowerManager;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -36,6 +51,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -55,7 +71,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = new TestableContext(InstrumentationRegistry.getContext(), null);
+ mContext = spy(new TestableContext(InstrumentationRegistry.getContext(), null));
mContext.addMockSystemService(PowerManager.class, mPowerManager);
mContext.addMockSystemService(ColorDisplayManager.class, mColorDisplayManager);
mContext.addMockSystemService(UiModeManager.class, mUiModeManager);
@@ -74,25 +90,33 @@
.setShouldDisplayGrayscale(true)
.setShouldUseNightMode(true)
.build();
- mApplier.apply(effects);
+ mApplier.apply(effects, UPDATE_ORIGIN_USER);
verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(true));
verify(mColorDisplayManager).setSaturationLevel(eq(0));
verify(mWallpaperManager).setWallpaperDimAmount(eq(0.6f));
- verifyZeroInteractions(mUiModeManager); // Coming later; adding now so test fails then. :)
+ verify(mUiModeManager).setNightModeActivatedForCustomMode(
+ eq(MODE_NIGHT_CUSTOM_TYPE_BEDTIME), eq(true));
}
@Test
- public void apply_removesEffects() {
+ public void apply_removesPreviouslyAppliedEffects() {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ ZenDeviceEffects previousEffects = new ZenDeviceEffects.Builder()
+ .setShouldSuppressAmbientDisplay(true)
+ .setShouldDimWallpaper(true)
+ .build();
+ mApplier.apply(previousEffects, UPDATE_ORIGIN_USER);
+ verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(true));
+ verify(mWallpaperManager).setWallpaperDimAmount(eq(0.6f));
+
ZenDeviceEffects noEffects = new ZenDeviceEffects.Builder().build();
- mApplier.apply(noEffects);
+ mApplier.apply(noEffects, UPDATE_ORIGIN_USER);
verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(false));
- verify(mColorDisplayManager).setSaturationLevel(eq(100));
verify(mWallpaperManager).setWallpaperDimAmount(eq(0.0f));
- verifyZeroInteractions(mUiModeManager);
+ verifyZeroInteractions(mColorDisplayManager, mUiModeManager);
}
@Test
@@ -107,9 +131,104 @@
.setShouldDisplayGrayscale(true)
.setShouldUseNightMode(true)
.build();
- mApplier.apply(effects);
+ mApplier.apply(effects, UPDATE_ORIGIN_USER);
verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(true));
// (And no crash from missing services).
}
+
+ @Test
+ public void apply_someEffects_onlyThoseEffectsApplied() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+ ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .setShouldDisplayGrayscale(true)
+ .build();
+ mApplier.apply(effects, UPDATE_ORIGIN_USER);
+
+ verify(mColorDisplayManager).setSaturationLevel(eq(0));
+ verify(mWallpaperManager).setWallpaperDimAmount(eq(0.6f));
+
+ verify(mPowerManager, never()).suppressAmbientDisplay(anyString(), anyBoolean());
+ verify(mUiModeManager, never()).setNightModeActivatedForCustomMode(anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void apply_onlyEffectDeltaApplied() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+ mApplier.apply(new ZenDeviceEffects.Builder().setShouldDimWallpaper(true).build(),
+ UPDATE_ORIGIN_USER);
+ verify(mWallpaperManager).setWallpaperDimAmount(eq(0.6f));
+
+ // Apply a second effect and remove the first one.
+ mApplier.apply(new ZenDeviceEffects.Builder().setShouldDisplayGrayscale(true).build(),
+ UPDATE_ORIGIN_USER);
+
+ // Wallpaper dimming was undone, Grayscale was applied, nothing else was touched.
+ verify(mWallpaperManager).setWallpaperDimAmount(eq(0.0f));
+ verify(mColorDisplayManager).setSaturationLevel(eq(0));
+ verifyZeroInteractions(mPowerManager);
+ verifyZeroInteractions(mUiModeManager);
+ }
+
+ @Test
+ public void apply_darkThemeFromApp_appliedOnScreenOff() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ ArgumentCaptor<IntentFilter> intentFilterCaptor =
+ ArgumentCaptor.forClass(IntentFilter.class);
+
+ when(mPowerManager.isInteractive()).thenReturn(true);
+
+ mApplier.apply(new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(),
+ UPDATE_ORIGIN_APP);
+
+ // Effect was not yet applied, but a broadcast receiver was registered.
+ verifyZeroInteractions(mUiModeManager);
+ verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(),
+ intentFilterCaptor.capture(), anyInt());
+ assertThat(intentFilterCaptor.getValue().getAction(0)).isEqualTo(Intent.ACTION_SCREEN_OFF);
+ BroadcastReceiver screenOffReceiver = broadcastReceiverCaptor.getValue();
+
+ // Now the "screen off" event comes.
+ screenOffReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
+
+ // So the effect is applied, and we stopped listening for this event.
+ verify(mUiModeManager).setNightModeActivatedForCustomMode(
+ eq(MODE_NIGHT_CUSTOM_TYPE_BEDTIME), eq(true));
+ verify(mContext).unregisterReceiver(eq(screenOffReceiver));
+ }
+
+ @Test
+ public void apply_darkThemeFromAppWithScreenOff_appliedImmediately() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+ when(mPowerManager.isInteractive()).thenReturn(false);
+
+ mApplier.apply(new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(),
+ UPDATE_ORIGIN_APP);
+
+ // Effect was applied, and no broadcast receiver was registered.
+ verify(mUiModeManager).setNightModeActivatedForCustomMode(
+ eq(MODE_NIGHT_CUSTOM_TYPE_BEDTIME), eq(true));
+ verify(mContext, never()).registerReceiver(any(), any(), anyInt());
+ }
+
+ @Test
+ public void testDeviceEffects_darkThemeFromUser_appliedImmediately() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+ when(mPowerManager.isInteractive()).thenReturn(true);
+
+ mApplier.apply(new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(),
+ UPDATE_ORIGIN_USER);
+
+ // Effect was applied, and no broadcast receiver was registered.
+ verify(mUiModeManager).setNightModeActivatedForCustomMode(
+ eq(MODE_NIGHT_CUSTOM_TYPE_BEDTIME), eq(true));
+ verify(mContext, never()).registerReceiver(any(), any(), anyInt());
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ee08fd2..8bb11a4 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -220,6 +220,7 @@
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
+import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
@@ -8929,8 +8930,8 @@
mBinderService.addAutomaticZenRule(rule, "com.android.settings");
// verify that zen mode helper gets passed in a package name of "android"
- verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString(),
- anyInt(), eq(ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI)); // system call
+ verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule),
+ eq(ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI), anyString(), anyInt());
}
@Test
@@ -8951,8 +8952,8 @@
mBinderService.addAutomaticZenRule(rule, "com.android.settings");
// verify that zen mode helper gets passed in a package name of "android"
- verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString(),
- anyInt(), eq(ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI)); // system call
+ verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule),
+ eq(ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI), anyString(), anyInt());
}
@Test
@@ -8972,8 +8973,8 @@
// verify that zen mode helper gets passed in the package name from the arg, not the owner
verify(mockZenModeHelper).addAutomaticZenRule(
- eq("another.package"), eq(rule), anyString(), anyInt(),
- eq(ZenModeHelper.FROM_APP)); // doesn't count as a system/systemui call
+ eq("another.package"), eq(rule), eq(ZenModeConfig.UPDATE_ORIGIN_APP),
+ anyString(), anyInt()); // doesn't count as a system/systemui call
}
@Test
@@ -13185,7 +13186,7 @@
NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
mBinderService.setNotificationPolicy("package", policy);
- verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyBoolean());
+ verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
}
@Test
@@ -13207,7 +13208,7 @@
NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
mBinderService.setNotificationPolicy("package", policy);
- verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyBoolean());
+ verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
}
@Test
@@ -13223,7 +13224,7 @@
NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
mBinderService.setNotificationPolicy("package", policy);
- verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyBoolean());
+ verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
}
@Test
@@ -13271,7 +13272,8 @@
mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
- eq("package"), anyString(), anyInt(), anyBoolean());
+ eq(ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI), anyString(), eq("package"),
+ anyInt());
}
@Test
@@ -13280,6 +13282,7 @@
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
mService.mZenModeHelper = zenModeHelper;
+ mService.setCallerIsNormalPackage();
when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
.thenReturn(true);
when(mCompanionMgr.getAssociations(anyString(), anyInt()))
@@ -13292,7 +13295,7 @@
mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
- eq("package"), anyString(), anyInt(), anyBoolean());
+ eq(ZenModeConfig.UPDATE_ORIGIN_APP), anyString(), eq("package"), anyInt());
}
@Test
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 fe21103..5ddac03 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -67,7 +67,6 @@
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -2370,7 +2369,7 @@
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
uid, false);
assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
// create notification channel that can bypass dnd
@@ -2380,18 +2379,18 @@
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
uid, false);
assertTrue(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
// delete channels
mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel.getId(), uid, false);
assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
- verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel2.getId(), uid, false);
assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
}
@@ -2407,7 +2406,7 @@
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
uid, false);
assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
// Recreate a channel & now the app has dnd access granted and can set the bypass dnd field
@@ -2417,7 +2416,7 @@
uid, false);
assertTrue(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
}
@@ -2433,7 +2432,7 @@
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
uid, false);
assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
// create notification channel that can bypass dnd, using local app level settings
@@ -2443,18 +2442,18 @@
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
uid, false);
assertTrue(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
// delete channels
mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel.getId(), uid, false);
assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
- verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel2.getId(), uid, false);
assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
}
@@ -2481,8 +2480,7 @@
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
uid, false);
assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(),
- anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
}
@@ -2504,7 +2502,7 @@
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
uid, false);
assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
}
@@ -2526,7 +2524,7 @@
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
uid, false);
assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
}
@@ -2542,7 +2540,7 @@
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
uid, false);
assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
// update channel so it CAN bypass dnd:
@@ -2550,7 +2548,7 @@
channel.setBypassDnd(true);
mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true, SYSTEM_UID, true);
assertTrue(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
// update channel so it can't bypass dnd:
@@ -2558,7 +2556,7 @@
channel.setBypassDnd(false);
mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true, SYSTEM_UID, true);
assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
}
@@ -2571,7 +2569,7 @@
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper.syncChannelsBypassingDnd();
assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
}
@@ -2581,7 +2579,7 @@
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean());
+ verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyInt());
resetZenModeHelper();
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index ef6fced..646ee3f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -45,6 +45,12 @@
import static android.provider.Settings.Global.ZEN_MODE_OFF;
import static android.service.notification.Condition.STATE_FALSE;
import static android.service.notification.Condition.STATE_TRUE;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_APP;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_INIT;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_INIT_USER;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_UNKNOWN;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_USER;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
@@ -53,9 +59,6 @@
import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG;
import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW;
import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW;
-import static com.android.server.notification.ZenModeHelper.FROM_APP;
-import static com.android.server.notification.ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI;
-import static com.android.server.notification.ZenModeHelper.FROM_USER;
import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
import static com.google.common.truth.Truth.assertThat;
@@ -305,8 +308,8 @@
mZenModeHelper.writeXml(serializer, false, version, UserHandle.USER_ALL);
serializer.endDocument();
serializer.flush();
- mZenModeHelper.setConfig(new ZenModeConfig(), null, "writing xml", Process.SYSTEM_UID,
- true);
+ mZenModeHelper.setConfig(new ZenModeConfig(), null, UPDATE_ORIGIN_INIT, "writing xml",
+ Process.SYSTEM_UID);
return baos;
}
@@ -321,7 +324,8 @@
serializer.flush();
ZenModeConfig newConfig = new ZenModeConfig();
newConfig.user = userId;
- mZenModeHelper.setConfig(newConfig, null, "writing xml", Process.SYSTEM_UID, true);
+ mZenModeHelper.setConfig(newConfig, null, UPDATE_ORIGIN_INIT, "writing xml",
+ Process.SYSTEM_UID);
return baos;
}
@@ -838,7 +842,7 @@
mZenModeHelper.mConfig = null; // will evaluate config to zen mode off
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenModeLocked("test", true);
+ mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -865,7 +869,7 @@
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenModeLocked("test", true);
+ mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -892,7 +896,7 @@
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenModeLocked("test", true);
+ mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -905,8 +909,8 @@
setupZenConfig();
// Turn manual zen mode on
- mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null,
- "test", CUSTOM_PKG_UID, false);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP,
+ null, "test", CUSTOM_PKG_UID);
// audio manager shouldn't do anything until the handler processes its messages
verify(mAudioManager, never()).updateRingerModeAffectedStreamsInternal();
@@ -1185,7 +1189,8 @@
List<StatsEvent> events = new LinkedList<>();
mZenModeHelper.pullRules(events);
- mZenModeHelper.removeAutomaticZenRule(CUSTOM_RULE_ID, "test", CUSTOM_PKG_UID, false);
+ mZenModeHelper.removeAutomaticZenRule(CUSTOM_RULE_ID, UPDATE_ORIGIN_APP, "test",
+ CUSTOM_PKG_UID);
assertTrue(-1
== mZenModeHelper.mRulesUidCache.getOrDefault(CUSTOM_PKG_NAME + "|" + 0, -1));
}
@@ -1240,12 +1245,14 @@
config10.user = 10;
config10.allowAlarms = true;
config10.allowMedia = true;
- mZenModeHelper.setConfig(config10, null, "writeXml", Process.SYSTEM_UID, true);
+ mZenModeHelper.setConfig(config10, null, UPDATE_ORIGIN_INIT, "writeXml",
+ Process.SYSTEM_UID);
ZenModeConfig config11 = mZenModeHelper.mConfig.copy();
config11.user = 11;
config11.allowAlarms = false;
config11.allowMedia = false;
- mZenModeHelper.setConfig(config11, null, "writeXml", Process.SYSTEM_UID, true);
+ mZenModeHelper.setConfig(config11, null, UPDATE_ORIGIN_INIT, "writeXml",
+ Process.SYSTEM_UID);
// Backup user 10 and reset values.
ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, 10);
@@ -1849,8 +1856,8 @@
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
// We need the package name to be something that's not "android" so there aren't any
// existing rules under that package.
- String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
- CUSTOM_PKG_UID, FROM_APP);
+ String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, UPDATE_ORIGIN_APP,
+ "test", CUSTOM_PKG_UID);
assertNotNull(id);
}
try {
@@ -1860,8 +1867,8 @@
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
- CUSTOM_PKG_UID, FROM_APP);
+ String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, UPDATE_ORIGIN_APP,
+ "test", CUSTOM_PKG_UID);
fail("allowed too many rules to be created");
} catch (IllegalArgumentException e) {
// yay
@@ -1881,8 +1888,8 @@
ZenModeConfig.toScheduleConditionId(si),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
- CUSTOM_PKG_UID, FROM_APP);
+ String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, UPDATE_ORIGIN_APP,
+ "test", CUSTOM_PKG_UID);
assertNotNull(id);
}
try {
@@ -1892,8 +1899,8 @@
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
- CUSTOM_PKG_UID, FROM_APP);
+ String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, UPDATE_ORIGIN_APP,
+ "test", CUSTOM_PKG_UID);
fail("allowed too many rules to be created");
} catch (IllegalArgumentException e) {
// yay
@@ -1913,8 +1920,8 @@
ZenModeConfig.toScheduleConditionId(si),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
- CUSTOM_PKG_UID, FROM_APP);
+ String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, UPDATE_ORIGIN_APP,
+ "test", CUSTOM_PKG_UID);
assertNotNull(id);
}
try {
@@ -1924,8 +1931,8 @@
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
- CUSTOM_PKG_UID, FROM_APP);
+ String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, UPDATE_ORIGIN_APP,
+ "test", CUSTOM_PKG_UID);
fail("allowed too many rules to be created");
} catch (IllegalArgumentException e) {
// yay
@@ -1940,8 +1947,8 @@
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test",
- Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ String id = mZenModeHelper.addAutomaticZenRule("android", zenRule,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
assertTrue(id != null);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
@@ -1961,8 +1968,8 @@
new ComponentName("android", "ScheduleConditionProvider"),
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test",
- Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ String id = mZenModeHelper.addAutomaticZenRule("android", zenRule,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
assertTrue(id != null);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
@@ -1985,11 +1992,12 @@
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
- CUSTOM_PKG_UID, FROM_APP);
+ String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, UPDATE_ORIGIN_APP, "test",
+ CUSTOM_PKG_UID);
mZenModeHelper.setAutomaticZenRuleState(zenRule.getConditionId(),
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- CUSTOM_PKG_UID, false);
+ UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
assertEquals(STATE_TRUE, ruleInConfig.condition.state);
@@ -2004,8 +2012,8 @@
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
- CUSTOM_PKG_UID, FROM_APP);
+ String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, UPDATE_ORIGIN_APP, "test",
+ CUSTOM_PKG_UID);
AutomaticZenRule zenRule2 = new AutomaticZenRule("NEW",
null,
@@ -2014,7 +2022,7 @@
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- mZenModeHelper.updateAutomaticZenRule(id, zenRule2, "", CUSTOM_PKG_UID, FROM_APP);
+ mZenModeHelper.updateAutomaticZenRule(id, zenRule2, UPDATE_ORIGIN_APP, "", CUSTOM_PKG_UID);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
assertEquals("NEW", ruleInConfig.name);
@@ -2029,15 +2037,15 @@
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
- CUSTOM_PKG_UID, FROM_APP);
+ String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, UPDATE_ORIGIN_APP, "test",
+ CUSTOM_PKG_UID);
assertTrue(id != null);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
assertTrue(ruleInConfig != null);
assertEquals(zenRule.getName(), ruleInConfig.name);
- mZenModeHelper.removeAutomaticZenRule(id, "test", CUSTOM_PKG_UID, false);
+ mZenModeHelper.removeAutomaticZenRule(id, UPDATE_ORIGIN_APP, "test", CUSTOM_PKG_UID);
assertNull(mZenModeHelper.mConfig.automaticRules.get(id));
}
@@ -2049,16 +2057,16 @@
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
- CUSTOM_PKG_UID, FROM_APP);
+ String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, UPDATE_ORIGIN_APP, "test",
+ CUSTOM_PKG_UID);
assertTrue(id != null);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
assertTrue(ruleInConfig != null);
assertEquals(zenRule.getName(), ruleInConfig.name);
- mZenModeHelper.removeAutomaticZenRules(mContext.getPackageName(), "test",
- CUSTOM_PKG_UID, false);
+ mZenModeHelper.removeAutomaticZenRules(mContext.getPackageName(), UPDATE_ORIGIN_APP, "test",
+ CUSTOM_PKG_UID);
assertNull(mZenModeHelper.mConfig.automaticRules.get(id));
}
@@ -2073,17 +2081,18 @@
new ComponentName("android", "ScheduleConditionProvider"),
sharedUri,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test",
- Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ String id = mZenModeHelper.addAutomaticZenRule("android", zenRule,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
new ComponentName("android", "ScheduleConditionProvider"),
sharedUri,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id2 = mZenModeHelper.addAutomaticZenRule("android", zenRule2, "test",
- Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ String id2 = mZenModeHelper.addAutomaticZenRule("android", zenRule2,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
Condition condition = new Condition(sharedUri, "", STATE_TRUE);
- mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition, Process.SYSTEM_UID, true);
+ mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
if (rule.id.equals(id)) {
@@ -2097,7 +2106,8 @@
}
condition = new Condition(sharedUri, "", STATE_FALSE);
- mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition, Process.SYSTEM_UID, true);
+ mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
if (rule.id.equals(id)) {
@@ -2133,7 +2143,7 @@
.setOwner(OWNER)
.setDeviceEffects(zde)
.build(),
- "reasons", 0, FROM_APP);
+ UPDATE_ORIGIN_APP, "reasons", 0);
AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
assertThat(savedRule.getDeviceEffects()).isEqualTo(
@@ -2167,7 +2177,7 @@
.setOwner(OWNER)
.setDeviceEffects(zde)
.build(),
- "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reasons", 0);
AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
assertThat(savedRule.getDeviceEffects()).isEqualTo(zde);
@@ -2195,7 +2205,8 @@
.setOwner(OWNER)
.setDeviceEffects(zde)
.build(),
- "reasons", 0, FROM_USER);
+ UPDATE_ORIGIN_USER,
+ "reasons", 0);
AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
assertThat(savedRule.getDeviceEffects()).isEqualTo(zde);
@@ -2212,7 +2223,7 @@
.setOwner(OWNER)
.setDeviceEffects(original)
.build(),
- "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reasons", 0);
ZenDeviceEffects updateFromApp = new ZenDeviceEffects.Builder()
.setShouldUseNightMode(true) // Good
@@ -2223,7 +2234,7 @@
.setOwner(OWNER)
.setDeviceEffects(updateFromApp)
.build(),
- "reasons", 0, FROM_APP);
+ UPDATE_ORIGIN_APP, "reasons", 0);
AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
assertThat(savedRule.getDeviceEffects()).isEqualTo(
@@ -2244,7 +2255,7 @@
.setOwner(OWNER)
.setDeviceEffects(original)
.build(),
- "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reasons", 0);
ZenDeviceEffects updateFromSystem = new ZenDeviceEffects.Builder()
.setShouldUseNightMode(true) // Good
@@ -2254,7 +2265,7 @@
new AutomaticZenRule.Builder("Rule", CONDITION_ID)
.setDeviceEffects(updateFromSystem)
.build(),
- "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reasons", 0);
AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromSystem);
@@ -2271,7 +2282,7 @@
.setOwner(OWNER)
.setDeviceEffects(original)
.build(),
- "reasons", 0, FROM_SYSTEM_OR_SYSTEMUI);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reasons", 0);
ZenDeviceEffects updateFromUser = new ZenDeviceEffects.Builder()
.setShouldUseNightMode(true) // Good
@@ -2281,7 +2292,7 @@
new AutomaticZenRule.Builder("Rule", CONDITION_ID)
.setDeviceEffects(updateFromUser)
.build(),
- "reasons", 0, FROM_USER);
+ UPDATE_ORIGIN_USER, "reasons", 0);
AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromUser);
@@ -2293,16 +2304,16 @@
setupZenConfig();
// note that caller=null because that's how it comes in from NMS.setZenMode
- mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "",
- Process.SYSTEM_UID, true);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null, Process.SYSTEM_UID);
// confirm that setting zen mode via setManualZenMode changed the zen mode correctly
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeHelper.mZenMode);
assertEquals(true, mZenModeHelper.mConfig.manualRule.allowManualInvocation);
// and also that it works to turn it back off again
- mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
- Process.SYSTEM_UID, true);
+ mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+ "", null, Process.SYSTEM_UID);
assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
}
@@ -2312,15 +2323,15 @@
setupZenConfig();
// note that caller=null because that's how it comes in from NMS.setZenMode
- mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "",
- Process.SYSTEM_UID, true);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null, Process.SYSTEM_UID);
// confirm that setting zen mode via setManualZenMode changed the zen mode correctly
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeHelper.mZenMode);
// and also that it works to turn it back off again
- mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, null, "",
- Process.SYSTEM_UID, true);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
+ null, Process.SYSTEM_UID);
assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode);
}
@@ -2332,13 +2343,13 @@
// Turn zen mode on (to important_interruptions)
// Need to additionally call the looper in order to finish the post-apply-config process
- mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "",
- Process.SYSTEM_UID, true);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null, Process.SYSTEM_UID);
// Now turn zen mode off, but via a different package UID -- this should get registered as
// "not an action by the user" because some other app is changing zen mode
- mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, null, "", CUSTOM_PKG_UID,
- false);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_APP, "", null,
+ CUSTOM_PKG_UID);
// In total, this should be 2 loggable changes
assertEquals(2, mZenModeEventLogger.numLoggedChanges());
@@ -2397,17 +2408,18 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
- "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
// Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE
mZenModeHelper.setAutomaticZenRuleState(id,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+ Process.SYSTEM_UID);
// Event 2: "User" turns off the automatic rule (sets it to not enabled)
zenRule.setEnabled(false);
- mZenModeHelper.updateAutomaticZenRule(id, zenRule, "", Process.SYSTEM_UID,
- FROM_SYSTEM_OR_SYSTEMUI);
+ mZenModeHelper.updateAutomaticZenRule(id, zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
+ Process.SYSTEM_UID);
// Add a new system rule
AutomaticZenRule systemRule = new AutomaticZenRule("systemRule",
@@ -2417,15 +2429,16 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String systemId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), systemRule,
- "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
// Event 3: turn on the system rule
mZenModeHelper.setAutomaticZenRuleState(systemId,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Event 4: "User" deletes the rule
- mZenModeHelper.removeAutomaticZenRule(systemId, "", Process.SYSTEM_UID, true);
+ mZenModeHelper.removeAutomaticZenRule(systemId, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
+ Process.SYSTEM_UID);
// In total, this represents 4 events
assertEquals(4, mZenModeEventLogger.numLoggedChanges());
@@ -2486,26 +2499,26 @@
setupZenConfig();
// First just turn zen mode on
- mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "",
- Process.SYSTEM_UID, true);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null, Process.SYSTEM_UID);
// Now change the policy slightly; want to confirm that this'll be reflected in the logs
ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
newConfig.allowAlarms = true;
newConfig.allowRepeatCallers = false;
- mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), Process.SYSTEM_UID,
- true);
+ mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Turn zen mode off; we want to make sure policy changes do not get logged when zen mode
// is off.
- mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, null, "",
- Process.SYSTEM_UID, true);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
+ null, Process.SYSTEM_UID);
// Change the policy again
newConfig.allowMessages = false;
newConfig.allowRepeatCallers = true;
- mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), Process.SYSTEM_UID,
- true);
+ mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Total events: we only expect ones for turning on, changing policy, and turning off
assertEquals(3, mZenModeEventLogger.numLoggedChanges());
@@ -2548,8 +2561,8 @@
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
- Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
// Rule 2, same as rule 1
AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
@@ -2558,8 +2571,8 @@
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, "test",
- Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
// Rule 3, has stricter settings than the default settings
ZenModeConfig ruleConfig = mZenModeHelper.mConfig.copy();
@@ -2572,28 +2585,28 @@
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
ruleConfig.toZenPolicy(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id3 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule3, "test",
- Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ String id3 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule3,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
// First: turn on rule 1
mZenModeHelper.setAutomaticZenRuleState(id,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Second: turn on rule 2
mZenModeHelper.setAutomaticZenRuleState(id2,
new Condition(zenRule2.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Third: turn on rule 3
mZenModeHelper.setAutomaticZenRuleState(id3,
new Condition(zenRule3.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Fourth: Turn *off* rule 2
mZenModeHelper.setAutomaticZenRuleState(id2,
new Condition(zenRule2.getConditionId(), "", STATE_FALSE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// This should result in a total of four events
assertEquals(4, mZenModeEventLogger.numLoggedChanges());
@@ -2649,7 +2662,7 @@
// Artificially turn zen mode "on". Re-evaluating zen mode should cause it to turn back off
// given that we don't have any zen rules active.
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelper.evaluateZenModeLocked("test", true);
+ mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true);
// Check that the change actually took: zen mode should be off now
assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode);
@@ -2672,8 +2685,8 @@
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
- Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
// Rule 2, same as rule 1 but owned by the system
AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
@@ -2682,37 +2695,37 @@
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, "test",
- Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
// Turn on rule 1; call looks like it's from the system. Because setting a condition is
// typically an automatic (non-user-initiated) action, expect the calling UID to be
// re-evaluated to the one associat.d with CUSTOM_PKG_NAME.
mZenModeHelper.setAutomaticZenRuleState(id,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Second: turn on rule 2. This is a system-owned rule and the UID should not be modified
// (nor even looked up; the mock PackageManager won't handle "android" as input).
mZenModeHelper.setAutomaticZenRuleState(id2,
new Condition(zenRule2.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Disable rule 1. Because this looks like a user action, the UID should not be modified
// from the system-provided one.
zenRule.setEnabled(false);
- mZenModeHelper.updateAutomaticZenRule(id, zenRule, "", Process.SYSTEM_UID,
- FROM_SYSTEM_OR_SYSTEMUI);
+ mZenModeHelper.updateAutomaticZenRule(id, zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
+ Process.SYSTEM_UID);
// Add a manual rule. Any manual rule changes should not get calling uids reassigned.
- mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "",
- CUSTOM_PKG_UID, false);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP,
+ "", null, CUSTOM_PKG_UID);
// Change rule 2's condition, but from some other UID. Since it doesn't look like it's from
// the system, we keep the UID info.
mZenModeHelper.setAutomaticZenRuleState(id2,
new Condition(zenRule2.getConditionId(), "", STATE_FALSE),
- 12345, false);
+ UPDATE_ORIGIN_APP, 12345);
// That was 5 events total
assertEquals(5, mZenModeEventLogger.numLoggedChanges());
@@ -2767,26 +2780,26 @@
// Turn on zen mode with a manual rule with an enabler set. This should *not* count
// as a user action, and *should* get its UID reassigned.
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
- CUSTOM_PKG_NAME, "", Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", CUSTOM_PKG_NAME, Process.SYSTEM_UID);
// Now change apps bypassing to true
ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
newConfig.areChannelsBypassingDnd = true;
- mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), Process.SYSTEM_UID,
- true);
+ mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// and then back to false, all without changing anything else
newConfig.areChannelsBypassingDnd = false;
- mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), Process.SYSTEM_UID,
- true);
+ mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Turn off manual mode, call from a package: don't reset UID even though enabler is set
- mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null,
- CUSTOM_PKG_NAME, "", 12345, false);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_APP, "",
+ CUSTOM_PKG_NAME, 12345);
// And likewise when turning it back on again
- mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
- CUSTOM_PKG_NAME, "", 12345, false);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP,
+ "", CUSTOM_PKG_NAME, 12345);
// These are 5 events in total.
assertEquals(5, mZenModeEventLogger.numLoggedChanges());
@@ -2831,15 +2844,15 @@
setupZenConfig();
// First just turn zen mode on
- mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "",
- Process.SYSTEM_UID, true);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null, Process.SYSTEM_UID);
// Now change only the channels part of the policy; want to confirm that this'll be
// reflected in the logs
ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
newConfig.allowPriorityChannels = false;
- mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), Process.SYSTEM_UID,
- true);
+ mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Total events: one for turning on, one for changing policy
assertThat(mZenModeEventLogger.numLoggedChanges()).isEqualTo(2);
@@ -2881,13 +2894,13 @@
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
- Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
// enable the rule
mZenModeHelper.setAutomaticZenRuleState(id,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// inspect the consolidated policy. Based on setupZenConfig() values.
assertFalse(mZenModeHelper.mConsolidatedPolicy.allowAlarms());
@@ -2924,13 +2937,13 @@
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
customPolicy,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
- Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
// enable the rule; this will update the consolidated policy
mZenModeHelper.setAutomaticZenRuleState(id,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// since this is the only active rule, the consolidated policy should match the custom
// policy for every field specified, and take default values for unspecified things
@@ -2960,13 +2973,13 @@
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
- Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
// enable rule 1
mZenModeHelper.setAutomaticZenRuleState(id,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// custom policy for rule 2
ZenPolicy customPolicy = new ZenPolicy.Builder()
@@ -2985,12 +2998,12 @@
customPolicy,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
- "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
// enable rule 2; this will update the consolidated policy
mZenModeHelper.setAutomaticZenRuleState(id2,
new Condition(zenRule2.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// now both rules should be on, and the consolidated policy should reflect the most
// restrictive option of each of the two
@@ -3022,13 +3035,13 @@
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
customPolicy,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
- Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
// enable the rule; this will update the consolidated policy
mZenModeHelper.setAutomaticZenRuleState(id,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// confirm that channels make it through
assertTrue(mZenModeHelper.mConsolidatedPolicy.allowPriorityChannels());
@@ -3045,12 +3058,12 @@
strictPolicy,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
- "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
// enable rule 2; this will update the consolidated policy
mZenModeHelper.setAutomaticZenRuleState(id2,
new Condition(zenRule2.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// rule 2 should override rule 1
assertFalse(mZenModeHelper.mConsolidatedPolicy.allowPriorityChannels());
@@ -3121,7 +3134,7 @@
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
- mZenModeHelper.populateZenRule(OWNER.getPackageName(), azr, rule, true, FROM_APP);
+ mZenModeHelper.populateZenRule(OWNER.getPackageName(), azr, rule, UPDATE_ORIGIN_APP, true);
assertEquals(NAME, rule.name);
assertEquals(OWNER, rule.component);
@@ -3149,7 +3162,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
CountDownLatch latch = new CountDownLatch(1);
final int[] actualStatus = new int[1];
@@ -3165,8 +3178,8 @@
mZenModeHelper.addCallback(callback);
zenRule.setEnabled(false);
- mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID,
- FROM_SYSTEM_OR_SYSTEMUI);
+ mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+ "", Process.SYSTEM_UID);
assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
assertEquals(AUTOMATIC_RULE_STATUS_DISABLED, actualStatus[0]);
@@ -3184,7 +3197,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, false);
final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
CountDownLatch latch = new CountDownLatch(1);
final int[] actualStatus = new int[1];
@@ -3200,8 +3213,8 @@
mZenModeHelper.addCallback(callback);
zenRule.setEnabled(true);
- mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID,
- FROM_SYSTEM_OR_SYSTEMUI);
+ mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+ "", Process.SYSTEM_UID);
assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
assertEquals(AUTOMATIC_RULE_STATUS_ENABLED, actualStatus[0]);
@@ -3220,7 +3233,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
CountDownLatch latch = new CountDownLatch(1);
final int[] actualStatus = new int[1];
@@ -3237,7 +3250,7 @@
mZenModeHelper.setAutomaticZenRuleState(createdId,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
assertEquals(AUTOMATIC_RULE_STATUS_ACTIVATED, actualStatus[0]);
@@ -3256,7 +3269,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
CountDownLatch latch = new CountDownLatch(1);
final int[] actualStatus = new int[2];
@@ -3274,10 +3287,10 @@
mZenModeHelper.setAutomaticZenRuleState(createdId,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
- mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
- Process.SYSTEM_UID, true);
+ mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+ null, "", Process.SYSTEM_UID);
assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
assertEquals(AUTOMATIC_RULE_STATUS_DEACTIVATED, actualStatus[1]);
@@ -3296,7 +3309,7 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
CountDownLatch latch = new CountDownLatch(1);
final int[] actualStatus = new int[2];
@@ -3314,11 +3327,11 @@
mZenModeHelper.setAutomaticZenRuleState(createdId,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
mZenModeHelper.setAutomaticZenRuleState(createdId,
new Condition(zenRule.getConditionId(), "", STATE_FALSE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
assertEquals(AUTOMATIC_RULE_STATUS_DEACTIVATED, actualStatus[1]);
@@ -3336,21 +3349,21 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
final String createdId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- zenRule, "test", Process.SYSTEM_UID, FROM_SYSTEM_OR_SYSTEMUI);
+ zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
// Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE
mZenModeHelper.setAutomaticZenRuleState(createdId,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- Process.SYSTEM_UID, true);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Event 2: Snooze rule by turning off DND
- mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
- Process.SYSTEM_UID, true);
+ mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+ "", null, Process.SYSTEM_UID);
// Event 3: "User" turns off the automatic rule (sets it to not enabled)
zenRule.setEnabled(false);
- mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, "", Process.SYSTEM_UID,
- FROM_SYSTEM_OR_SYSTEMUI);
+ mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+ "", Process.SYSTEM_UID);
assertEquals(false, mZenModeHelper.mConfig.automaticRules.get(createdId).snoozing);
}
@@ -3359,18 +3372,20 @@
public void testDeviceEffects_applied() {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
+ verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT));
ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
.setShouldSuppressAmbientDisplay(true)
.setShouldDimWallpaper(true)
.build();
String ruleId = addRuleWithEffects(effects);
- verify(mDeviceEffectsApplier, never()).apply(any());
+ verifyNoMoreInteractions(mDeviceEffectsApplier);
- mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, CUSTOM_PKG_UID, false);
+ mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
mTestableLooper.processAllMessages();
- verify(mDeviceEffectsApplier).apply(eq(effects));
+ verify(mDeviceEffectsApplier).apply(eq(effects), eq(UPDATE_ORIGIN_APP));
}
@Test
@@ -3380,31 +3395,66 @@
ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build();
String ruleId = addRuleWithEffects(zde);
- mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, CUSTOM_PKG_UID, false);
+ mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
mTestableLooper.processAllMessages();
- verify(mDeviceEffectsApplier).apply(eq(zde));
+ verify(mDeviceEffectsApplier).apply(eq(zde), eq(UPDATE_ORIGIN_APP));
- mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_FALSE, CUSTOM_PKG_UID, false);
+ mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_FALSE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
mTestableLooper.processAllMessages();
- verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS));
+ verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_APP));
}
@Test
+ public void testDeviceEffects_changeToConsolidatedEffects_applied() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
+ verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT));
+
+ String ruleId = addRuleWithEffects(
+ new ZenDeviceEffects.Builder().setShouldDisplayGrayscale(true).build());
+ mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
+ mTestableLooper.processAllMessages();
+ verify(mDeviceEffectsApplier).apply(
+ eq(new ZenDeviceEffects.Builder()
+ .setShouldDisplayGrayscale(true)
+ .build()),
+ eq(UPDATE_ORIGIN_APP));
+
+ // Now create and activate a second rule that adds more effects.
+ String secondRuleId = addRuleWithEffects(
+ new ZenDeviceEffects.Builder().setShouldDimWallpaper(true).build());
+ mZenModeHelper.setAutomaticZenRuleState(secondRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
+ mTestableLooper.processAllMessages();
+
+ verify(mDeviceEffectsApplier).apply(
+ eq(new ZenDeviceEffects.Builder()
+ .setShouldDisplayGrayscale(true)
+ .setShouldDimWallpaper(true)
+ .build()),
+ eq(UPDATE_ORIGIN_APP));
+ }
+ @Test
public void testDeviceEffects_noChangeToConsolidatedEffects_notApplied() {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
+ verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT));
ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build();
String ruleId = addRuleWithEffects(zde);
- mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, CUSTOM_PKG_UID, false);
+ mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
mTestableLooper.processAllMessages();
- verify(mDeviceEffectsApplier).apply(eq(zde));
+ verify(mDeviceEffectsApplier).apply(eq(zde), eq(UPDATE_ORIGIN_APP));
// Now create and activate a second rule that doesn't add any more effects.
String secondRuleId = addRuleWithEffects(zde);
- mZenModeHelper.setAutomaticZenRuleState(secondRuleId, CONDITION_TRUE, CUSTOM_PKG_UID,
- false);
+ mZenModeHelper.setAutomaticZenRuleState(secondRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
mTestableLooper.processAllMessages();
verifyNoMoreInteractions(mDeviceEffectsApplier);
@@ -3416,21 +3466,50 @@
ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build();
String ruleId = addRuleWithEffects(zde);
- verify(mDeviceEffectsApplier, never()).apply(any());
+ verify(mDeviceEffectsApplier, never()).apply(any(), anyInt());
- mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, CUSTOM_PKG_UID, false);
+ mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
mTestableLooper.processAllMessages();
- verify(mDeviceEffectsApplier, never()).apply(any());
+ verify(mDeviceEffectsApplier, never()).apply(any(), anyInt());
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
- verify(mDeviceEffectsApplier).apply(eq(zde));
+ verify(mDeviceEffectsApplier).apply(eq(zde), eq(UPDATE_ORIGIN_INIT));
+ }
+
+ @Test
+ public void testDeviceEffects_onUserSwitch_appliedImmediately() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
+ verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT));
+
+ // Initialize default configurations (default rules) for both users.
+ mZenModeHelper.onUserSwitched(1);
+ mZenModeHelper.onUserSwitched(2);
+
+ // Current user is now 2. Tweak a rule for user 1 so it's active and has effects.
+ ZenRule user1Rule = mZenModeHelper.mConfigs.get(1).automaticRules.valueAt(0);
+ user1Rule.enabled = true;
+ user1Rule.condition = new Condition(user1Rule.conditionId, "on", STATE_TRUE);
+ user1Rule.zenDeviceEffects = new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .setShouldUseNightMode(true)
+ .build();
+ verifyNoMoreInteractions(mDeviceEffectsApplier);
+
+ mZenModeHelper.onUserSwitched(1);
+ mTestableLooper.processAllMessages();
+
+ verify(mDeviceEffectsApplier).apply(eq(user1Rule.zenDeviceEffects),
+ eq(UPDATE_ORIGIN_INIT_USER));
}
private String addRuleWithEffects(ZenDeviceEffects effects) {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
.setDeviceEffects(effects)
.build();
- return mZenModeHelper.addAutomaticZenRule("pkg", rule, "", CUSTOM_PKG_UID, FROM_APP);
+ return mZenModeHelper.addAutomaticZenRule("pkg", rule, UPDATE_ORIGIN_APP, "",
+ CUSTOM_PKG_UID);
}
@Test
@@ -3455,7 +3534,7 @@
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
ZEN_MODE_IMPORTANT_INTERRUPTIONS);
- mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, "test", "test", 0, true);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_APP, "test", "test", 0);
assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
@@ -3505,7 +3584,7 @@
assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isFalse();
- mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, "test", "test", 0, true);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_APP, "test", "test", 0);
assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isTrue();
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
diff --git a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
index c0a90b2..e77c14a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
@@ -19,10 +19,13 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.window.flags.Flags.explicitRefreshRateHints;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -62,9 +65,11 @@
private static final FrameRateVote FRAME_RATE_VOTE_NONE = new FrameRateVote();
private static final FrameRateVote FRAME_RATE_VOTE_60_EXACT =
- new FrameRateVote(60, Surface.FRAME_RATE_COMPATIBILITY_EXACT);
+ new FrameRateVote(60, Surface.FRAME_RATE_COMPATIBILITY_EXACT,
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
private static final FrameRateVote FRAME_RATE_VOTE_60_PREFERRED =
- new FrameRateVote(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+ new FrameRateVote(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
WindowState createWindow(String name) {
WindowState window = createWindow(null, TYPE_APPLICATION, name);
@@ -110,6 +115,8 @@
any(SurfaceControl.class), anyInt());
verify(appWindow.getPendingTransaction(), never()).setFrameRate(
any(SurfaceControl.class), anyInt(), anyInt(), anyInt());
+ verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy(
+ any(SurfaceControl.class), anyInt());
}
@Test
@@ -140,6 +147,8 @@
appWindow.getSurfaceControl(), 1);
verify(appWindow.getPendingTransaction(), never()).setFrameRate(
any(SurfaceControl.class), anyInt(), anyInt(), anyInt());
+ verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy(
+ any(SurfaceControl.class), anyInt());
}
@Test
@@ -175,8 +184,17 @@
appWindow.getSurfaceControl(), 0);
verify(appWindow.getPendingTransaction(), times(2)).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), 1);
- verify(appWindow.getPendingTransaction(), never()).setFrameRate(
- any(SurfaceControl.class), anyInt(), anyInt(), anyInt());
+ verify(appWindow.getPendingTransaction(), times(1)).setFrameRate(
+ eq(appWindow.getSurfaceControl()), anyFloat(),
+ eq(Surface.FRAME_RATE_COMPATIBILITY_EXACT), eq(Surface.CHANGE_FRAME_RATE_ALWAYS));
+ if (explicitRefreshRateHints()) {
+ verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy(
+ appWindow.getSurfaceControl(),
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
+ } else {
+ verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy(
+ any(SurfaceControl.class), anyInt());
+ }
}
@Test
@@ -202,8 +220,17 @@
appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), 2);
- verify(appWindow.getPendingTransaction(), never()).setFrameRate(
- any(SurfaceControl.class), anyInt(), anyInt(), anyInt());
+ verify(appWindow.getPendingTransaction(), times(1)).setFrameRate(
+ eq(appWindow.getSurfaceControl()), anyFloat(),
+ eq(Surface.FRAME_RATE_COMPATIBILITY_EXACT), eq(Surface.CHANGE_FRAME_RATE_ALWAYS));
+ if (explicitRefreshRateHints()) {
+ verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy(
+ appWindow.getSurfaceControl(),
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
+ } else {
+ verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy(
+ any(SurfaceControl.class), anyInt());
+ }
}
@Test
@@ -229,6 +256,8 @@
appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
verify(appWindow.getPendingTransaction(), never()).setFrameRate(
any(SurfaceControl.class), anyInt(), anyInt(), anyInt());
+ verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy(
+ any(SurfaceControl.class), anyInt());
}
@Test
@@ -256,6 +285,14 @@
verify(appWindow.getPendingTransaction(), times(1)).setFrameRate(
appWindow.getSurfaceControl(), 60,
Surface.FRAME_RATE_COMPATIBILITY_EXACT, Surface.CHANGE_FRAME_RATE_ALWAYS);
+ if (explicitRefreshRateHints()) {
+ verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy(
+ appWindow.getSurfaceControl(),
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
+ } else {
+ verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy(
+ any(SurfaceControl.class), anyInt());
+ }
}
@Test
@@ -283,6 +320,8 @@
appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
verify(appWindow.getPendingTransaction(), never()).setFrameRate(
any(SurfaceControl.class), anyInt(), anyInt(), anyInt());
+ verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy(
+ any(SurfaceControl.class), anyInt());
}
@Test
@@ -310,5 +349,13 @@
verify(appWindow.getPendingTransaction(), times(1)).setFrameRate(
appWindow.getSurfaceControl(), 60,
Surface.FRAME_RATE_COMPATIBILITY_DEFAULT, Surface.CHANGE_FRAME_RATE_ALWAYS);
+ if (explicitRefreshRateHints()) {
+ verify(appWindow.getPendingTransaction(), times(1)).setFrameRateSelectionStrategy(
+ appWindow.getSurfaceControl(),
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
+ } else {
+ verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionStrategy(
+ any(SurfaceControl.class), anyInt());
+ }
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index 49a8886..c9a83b0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN;
import static android.view.SurfaceControl.RefreshRateRange.FLOAT_TOLERANCE;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -69,15 +70,20 @@
private static final FrameRateVote FRAME_RATE_VOTE_NONE = new FrameRateVote();
private static final FrameRateVote FRAME_RATE_VOTE_DENY_LIST =
- new FrameRateVote(LOW_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_EXACT);
+ new FrameRateVote(LOW_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_EXACT,
+ FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
private static final FrameRateVote FRAME_RATE_VOTE_LOW_EXACT =
- new FrameRateVote(LOW_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_EXACT);
+ new FrameRateVote(LOW_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_EXACT,
+ FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
private static final FrameRateVote FRAME_RATE_VOTE_HI_EXACT =
- new FrameRateVote(HI_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_EXACT);
+ new FrameRateVote(HI_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_EXACT,
+ FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
private static final FrameRateVote FRAME_RATE_VOTE_LOW_PREFERRED =
- new FrameRateVote(LOW_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+ new FrameRateVote(LOW_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
+ FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
private static final FrameRateVote FRAME_RATE_VOTE_HI_PREFERRED =
- new FrameRateVote(HI_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+ new FrameRateVote(HI_REFRESH_RATE, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT,
+ FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
// Parcel and Unparcel the LayoutParams in the window state to test the path the object
// travels from the app's process to system server