Merge "Frozen-aware AppOpsService op noted callback" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 8bd4367..e6c2b3b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -34179,7 +34179,6 @@
method public android.os.StrictMode.VmPolicy build();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectActivityLeaks();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectAll();
- method @FlaggedApi("com.android.window.flags.bal_strict_mode") @NonNull public android.os.StrictMode.VmPolicy.Builder detectBlockedBackgroundActivityLaunch();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectCleartextNetwork();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectContentUriWithoutPermission();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectCredentialProtectedWhileLocked();
@@ -34192,7 +34191,6 @@
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectNonSdkApiUsage();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectUnsafeIntentLaunch();
method @NonNull public android.os.StrictMode.VmPolicy.Builder detectUntaggedSockets();
- method @FlaggedApi("com.android.window.flags.bal_strict_mode") @NonNull public android.os.StrictMode.VmPolicy.Builder ignoreBlockedBackgroundActivityLaunch();
method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
@@ -51080,6 +51078,7 @@
method @NonNull public android.view.DisplayShape getShape();
method @Deprecated public void getSize(android.graphics.Point);
method public int getState();
+ method @FlaggedApi("com.android.server.display.feature.flags.enable_get_suggested_frame_rate") public float getSuggestedFrameRate(int);
method public android.view.Display.Mode[] getSupportedModes();
method @Deprecated public float[] getSupportedRefreshRates();
method @Deprecated public int getWidth();
@@ -51097,6 +51096,8 @@
field public static final int FLAG_ROUND = 16; // 0x10
field public static final int FLAG_SECURE = 2; // 0x2
field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
+ field @FlaggedApi("com.android.server.display.feature.flags.enable_get_suggested_frame_rate") public static final int FRAME_RATE_CATEGORY_HIGH = 1; // 0x1
+ field @FlaggedApi("com.android.server.display.feature.flags.enable_get_suggested_frame_rate") public static final int FRAME_RATE_CATEGORY_NORMAL = 0; // 0x0
field public static final int INVALID_DISPLAY = -1; // 0xffffffff
field public static final int STATE_DOZE = 3; // 0x3
field public static final int STATE_DOZE_SUSPEND = 4; // 0x4
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 003104a..0a05144 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -359,23 +359,6 @@
in RemoteCallback navigationObserver, in BackAnimationAdapter adaptor);
/**
- * registers a callback to be invoked when a background activity launch is aborted.
- *
- * @param observer callback to be registered.
- * @return true if the callback was successfully registered, false otherwise.
- * @hide
- */
- boolean registerBackgroundActivityStartCallback(in IBinder binder);
-
- /**
- * unregisters a callback to be invoked when a background activity launch is aborted.
- *
- * @param observer callback to be registered.
- * @hide
- */
- void unregisterBackgroundActivityStartCallback(in IBinder binder);
-
- /**
* registers a callback to be invoked when the screen is captured.
*
* @param observer callback to be registered.
diff --git a/core/java/android/app/IBackgroundActivityLaunchCallback.aidl b/core/java/android/app/IBackgroundActivityLaunchCallback.aidl
deleted file mode 100644
index 6dfb518..0000000
--- a/core/java/android/app/IBackgroundActivityLaunchCallback.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-* Copyright 2024, The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-package android.app;
-
-/**
- * Callback to find out when a background activity launch is aborted.
- * @hide
- */
-oneway interface IBackgroundActivityLaunchCallback
-{
- void onBackgroundActivityLaunchAborted(in String message);
-}
diff --git a/core/java/android/app/backup/NotificationLoggingConstants.java b/core/java/android/app/backup/NotificationLoggingConstants.java
new file mode 100644
index 0000000..add4562
--- /dev/null
+++ b/core/java/android/app/backup/NotificationLoggingConstants.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.backup;
+
+/**
+ * @hide
+ */
+public class NotificationLoggingConstants {
+
+ // Key under which the payload blob is stored
+ public static final String KEY_NOTIFICATIONS = "notifications";
+
+ @BackupRestoreEventLogger.BackupRestoreDataType
+ public static final String DATA_TYPE_ZEN_CONFIG = KEY_NOTIFICATIONS + ":zen_config";
+ @BackupRestoreEventLogger.BackupRestoreDataType
+ public static final String DATA_TYPE_ZEN_RULES = KEY_NOTIFICATIONS + ":zen_rules";
+
+ @BackupRestoreEventLogger.BackupRestoreError
+ public static final String ERROR_XML_PARSING = KEY_NOTIFICATIONS + ":invalid_xml_parsing";
+}
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 60a9e05..81dc46e 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -20,22 +20,17 @@
import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH;
import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__INTERNAL_NON_EXPORTED_COMPONENT_MATCH;
import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH;
-import static com.android.window.flags.Flags.balStrictMode;
import android.animation.ValueAnimator;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
import android.app.ActivityThread;
import android.app.IActivityManager;
-import android.app.IBackgroundActivityLaunchCallback;
import android.app.IUnsafeIntentStrictModeCallback;
-import android.app.PendingIntent;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
@@ -50,7 +45,6 @@
import android.net.TrafficStats;
import android.net.Uri;
import android.os.storage.IStorageManager;
-import android.os.strictmode.BackgroundActivityLaunchViolation;
import android.os.strictmode.CleartextNetworkViolation;
import android.os.strictmode.ContentUriWithoutPermissionViolation;
import android.os.strictmode.CredentialProtectedWhileLockedViolation;
@@ -88,7 +82,6 @@
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.HexDump;
import com.android.internal.util.Preconditions;
-import com.android.window.flags.Flags;
import dalvik.system.BlockGuard;
import dalvik.system.CloseGuard;
@@ -273,7 +266,6 @@
DETECT_VM_IMPLICIT_DIRECT_BOOT,
DETECT_VM_INCORRECT_CONTEXT_USE,
DETECT_VM_UNSAFE_INTENT_LAUNCH,
- DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED,
PENALTY_GATHER,
PENALTY_LOG,
PENALTY_DIALOG,
@@ -317,8 +309,6 @@
private static final int DETECT_VM_INCORRECT_CONTEXT_USE = 1 << 12;
/** @hide */
private static final int DETECT_VM_UNSAFE_INTENT_LAUNCH = 1 << 13;
- /** @hide */
- private static final int DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED = 1 << 14;
/** @hide */
private static final int DETECT_VM_ALL = 0x0000ffff;
@@ -912,9 +902,6 @@
if (targetSdk >= Build.VERSION_CODES.S) {
detectUnsafeIntentLaunch();
}
- if (balStrictMode() && targetSdk > Build.VERSION_CODES.VANILLA_ICE_CREAM) {
- detectBlockedBackgroundActivityLaunch();
- }
// TODO: Decide whether to detect non SDK API usage beyond a certain API level.
// TODO: enable detectImplicitDirectBoot() once system is less noisy
@@ -1153,39 +1140,6 @@
}
/**
- * Detects when your app is blocked from launching a background activity or a
- * PendingIntent created by your app cannot be launched.
- * <p>
- * Starting an activity requires <a
- * href="https://developer.android.com/guide/components/activities/background-starts
- * ">specific permissions</a> which may depend on the state at runtime and especially
- * in case of {@link android.app.PendingIntent} starts on the collaborating app.
- * If the activity start is blocked methods like {@link Context#startActivity(Intent)}
- * or {@link PendingIntent#send()} have no way to return that information. Instead you
- * can use this strct mode feature to detect blocked starts.
- * <p>
- * Note that in some cases blocked starts may be unavoidable, e.g. when the user clicks
- * the home button while the app tries to start a new activity.
- */
- @SuppressWarnings("BuilderSetStyle")
- @FlaggedApi(Flags.FLAG_BAL_STRICT_MODE)
- public @NonNull Builder detectBlockedBackgroundActivityLaunch() {
- return enable(DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED);
- }
-
- /**
- * Stops detecting whether your app is blocked from launching a background activity or
- * a PendingIntent created by your app cannot be launched.
- * <p>
- * This disables the effect of {@link #detectBlockedBackgroundActivityLaunch()}.
- */
- @SuppressWarnings("BuilderSetStyle")
- @FlaggedApi(Flags.FLAG_BAL_STRICT_MODE)
- public @NonNull Builder ignoreBlockedBackgroundActivityLaunch() {
- return disable(DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED);
- }
-
- /**
* Crashes the whole process on violation. This penalty runs at the end of all enabled
* penalties so you'll still get your logging or other violations before the process
* dies.
@@ -2179,25 +2133,10 @@
registerIntentMatchingRestrictionCallback();
}
- if ((sVmPolicy.mask & DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED) != 0) {
- registerBackgroundActivityLaunchCallback();
- }
-
setBlockGuardVmPolicy(sVmPolicy.mask);
}
}
- private static void registerBackgroundActivityLaunchCallback() {
- try {
- ActivityTaskManager.getService().registerBackgroundActivityStartCallback(
- new BackgroundActivityLaunchCallback());
- } catch (DeadObjectException e) {
- // ignore
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException handling StrictMode violation", e);
- }
- }
-
private static final class UnsafeIntentStrictModeCallback
extends IUnsafeIntentStrictModeCallback.Stub {
@Override
@@ -2222,16 +2161,6 @@
}
}
- private static final class BackgroundActivityLaunchCallback
- extends IBackgroundActivityLaunchCallback.Stub {
- @Override
- public void onBackgroundActivityLaunchAborted(String message) {
- if (StrictMode.vmBackgroundActivityLaunchEnabled()) {
- StrictMode.onBackgroundActivityLaunchAborted(message);
- }
- }
- }
-
/** Gets the current VM policy. */
public static VmPolicy getVmPolicy() {
synchronized (StrictMode.class) {
@@ -2307,11 +2236,6 @@
}
/** @hide */
- public static boolean vmBackgroundActivityLaunchEnabled() {
- return (sVmPolicy.mask & DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED) != 0;
- }
-
- /** @hide */
public static void onSqliteObjectLeaked(String message, Throwable originStack) {
onVmPolicyViolation(new SqliteObjectLeakedViolation(message, originStack));
}
@@ -2478,11 +2402,6 @@
onVmPolicyViolation(new UnsafeIntentLaunchViolation(intent, msg + intent));
}
- /** @hide */
- public static void onBackgroundActivityLaunchAborted(String message) {
- onVmPolicyViolation(new BackgroundActivityLaunchViolation(message));
- }
-
/** Assume locked until we hear otherwise */
private static volatile boolean sCeStorageUnlocked = false;
diff --git a/core/java/android/os/strictmode/BackgroundActivityLaunchViolation.java b/core/java/android/os/strictmode/BackgroundActivityLaunchViolation.java
deleted file mode 100644
index aef52c6..0000000
--- a/core/java/android/os/strictmode/BackgroundActivityLaunchViolation.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os.strictmode;
-
-import android.annotation.NonNull;
-import android.app.Activity;
-
-/**
- * Violation raised when your app is blocked from launching an {@link Activity}
- * (from the background).
- * <p>
- * This occurs when the app:
- * <ul>
- * <li>Does not have sufficient privileges to launch the Activity.</li>
- * <li>Has not explicitly opted-in to launch the Activity.</li>
- * </ul>
- * Violations may affect the functionality of your app and should be addressed to ensure
- * proper behavior.
- * @hide
- */
-public class BackgroundActivityLaunchViolation extends Violation {
-
- /** @hide */
- public BackgroundActivityLaunchViolation(@NonNull String message) {
- super(message);
- }
-}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index e173255..24328eb 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -24,12 +24,12 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_RULES;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
import static android.service.notification.ZenAdapters.peopleTypeToPrioritySenders;
import static android.service.notification.ZenAdapters.prioritySendersToPeopleType;
import static android.service.notification.ZenAdapters.zenPolicyConversationSendersToNotificationPolicy;
-import static android.service.notification.ZenModeConfig.EventInfo.REPLY_YES_OR_MAYBE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_ALARMS;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_CALLS;
@@ -56,6 +56,7 @@
import android.app.Flags;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
+import android.app.backup.BackupRestoreEventLogger;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -957,8 +958,9 @@
}
}
- public static ZenModeConfig readXml(TypedXmlPullParser parser)
- throws XmlPullParserException, IOException {
+ public static ZenModeConfig readXml(TypedXmlPullParser parser,
+ @Nullable BackupRestoreEventLogger logger) throws XmlPullParserException, IOException {
+ int readRuleCount = 0;
int type = parser.getEventType();
if (type != XmlPullParser.START_TAG) return null;
String tag = parser.getName();
@@ -1048,6 +1050,8 @@
readManualRule = true;
if (rt.manualRule.zenPolicy == null) {
readManualRuleWithoutPolicy = true;
+ } else {
+ readRuleCount++;
}
} else if (AUTOMATIC_TAG.equals(tag)
|| (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag))) {
@@ -1062,6 +1066,7 @@
}
} else if (AUTOMATIC_TAG.equals(tag)) {
rt.automaticRules.put(id, automaticRule);
+ readRuleCount++;
}
}
} else if (STATE_TAG.equals(tag)) {
@@ -1085,8 +1090,17 @@
}
rt.manualRule.condition = new Condition(rt.manualRule.conditionId, "",
Condition.STATE_TRUE);
+ readRuleCount++;
}
}
+
+ if (!Flags.modesUi()){
+ readRuleCount++;
+ }
+
+ if (logger != null) {
+ logger.logItemsRestored(DATA_TYPE_ZEN_RULES, readRuleCount);
+ }
return rt;
}
}
@@ -1110,8 +1124,9 @@
* @throws IOException
*/
- public void writeXml(TypedXmlSerializer out, Integer version, boolean forBackup)
- throws IOException {
+ public void writeXml(TypedXmlSerializer out, Integer version, boolean forBackup,
+ @Nullable BackupRestoreEventLogger logger) throws IOException {
+ int writtenRuleCount = 0;
int xmlVersion = getCurrentXmlVersion();
out.startTag(null, ZEN_TAG);
out.attribute(null, ZEN_ATT_VERSION, version == null
@@ -1147,6 +1162,7 @@
writeRuleXml(manualRule, out, forBackup);
out.endTag(null, MANUAL_TAG);
}
+ writtenRuleCount++;
final int N = automaticRules.size();
for (int i = 0; i < N; i++) {
final String id = automaticRules.keyAt(i);
@@ -1155,6 +1171,7 @@
out.attribute(null, RULE_ATT_ID, id);
writeRuleXml(automaticRule, out, forBackup);
out.endTag(null, AUTOMATIC_TAG);
+ writtenRuleCount++;
}
if (Flags.modesApi() && !forBackup) {
for (int i = 0; i < deletedRules.size(); i++) {
@@ -1171,6 +1188,9 @@
out.endTag(null, STATE_TAG);
out.endTag(null, ZEN_TAG);
+ if (logger != null) {
+ logger.logItemsBackedUp(DATA_TYPE_ZEN_RULES, writtenRuleCount);
+ }
}
@NonNull
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index e940e55bd..910e644 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -22,6 +22,7 @@
import static com.android.server.display.feature.flags.Flags.FLAG_HIGHEST_HDR_SDR_RATIO_API;
import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_HAS_ARR_SUPPORT;
+import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE;
import android.Manifest;
import android.annotation.FlaggedApi;
@@ -1278,6 +1279,60 @@
}
}
+ @FlaggedApi(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE)
+ public static final int FRAME_RATE_CATEGORY_NORMAL = 0;
+ @FlaggedApi(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE)
+ public static final int FRAME_RATE_CATEGORY_HIGH = 1;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"FRAME_RATE_CATEGORY_"},
+ value = {
+ FRAME_RATE_CATEGORY_NORMAL,
+ FRAME_RATE_CATEGORY_HIGH
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrameRateCategory {}
+
+ /**
+ * <p> Gets the display-defined frame rate given a descriptive frame rate category. </p>
+ *
+ * <p> For example, an animation that does not require fast render rates can use
+ * the {@link #FRAME_RATE_CATEGORY_NORMAL} to get the suggested frame rate.
+ * The suggested frame rate then can be used in the
+ * {@link Surface.FrameRateParams.Builder#setDesiredRateRange} for desiredMinRate.
+ *
+ * <pre>{@code
+ * float desiredMinRate = display.getSuggestedFrameRate(FRAME_RATE_CATEGORY_NORMAL);
+ * Surface.FrameRateParams params = new Surface.FrameRateParams.Builder().
+ * setDesiredRateRange(desiredMinRate, Float.MAX).build();
+ * surface.setFrameRate(params);
+ * }</pre>
+ * </p>
+ *
+ * @param category either {@link #FRAME_RATE_CATEGORY_NORMAL}
+ * or {@link #FRAME_RATE_CATEGORY_HIGH}
+ *
+ * @see Surface#setFrameRate(Surface.FrameRateParams)
+ * @see SurfaceControl.Transaction#setFrameRate(SurfaceControl, Surface.FrameRateParams)
+ * @throws IllegalArgumentException when category is not {@link #FRAME_RATE_CATEGORY_NORMAL}
+ * or {@link #FRAME_RATE_CATEGORY_HIGH}
+ */
+ @FlaggedApi(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE)
+ public float getSuggestedFrameRate(@FrameRateCategory int category) {
+ synchronized (mLock) {
+ updateDisplayInfoLocked();
+ if (category == FRAME_RATE_CATEGORY_HIGH) {
+ return mDisplayInfo.frameRateCategoryRate.getHigh();
+ } else if (category == FRAME_RATE_CATEGORY_NORMAL) {
+ return mDisplayInfo.frameRateCategoryRate.getNormal();
+ } else {
+ throw new IllegalArgumentException("Invalid FrameRateCategory provided");
+ }
+ }
+ }
+
/**
* <p> Returns true if the connected display can be switched into a mode with minimal
* post processing. </p>
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 26fce90..8f112f3 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -204,6 +204,12 @@
public boolean hasArrSupport;
/**
+ * Represents frame rate for the FrameRateCategory Normal and High.
+ * @see android.view.Display#getSuggestedFrameRate(int) for more details.
+ */
+ public FrameRateCategoryRate frameRateCategoryRate;
+
+ /**
* The default display mode.
*/
public int defaultModeId;
@@ -443,6 +449,7 @@
&& modeId == other.modeId
&& renderFrameRate == other.renderFrameRate
&& hasArrSupport == other.hasArrSupport
+ && Objects.equals(frameRateCategoryRate, other.frameRateCategoryRate)
&& defaultModeId == other.defaultModeId
&& userPreferredModeId == other.userPreferredModeId
&& Arrays.equals(supportedModes, other.supportedModes)
@@ -505,6 +512,7 @@
modeId = other.modeId;
renderFrameRate = other.renderFrameRate;
hasArrSupport = other.hasArrSupport;
+ frameRateCategoryRate = other.frameRateCategoryRate;
defaultModeId = other.defaultModeId;
userPreferredModeId = other.userPreferredModeId;
supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length);
@@ -562,6 +570,8 @@
modeId = source.readInt();
renderFrameRate = source.readFloat();
hasArrSupport = source.readBoolean();
+ frameRateCategoryRate = source.readParcelable(null,
+ android.view.FrameRateCategoryRate.class);
defaultModeId = source.readInt();
userPreferredModeId = source.readInt();
int nModes = source.readInt();
@@ -636,6 +646,7 @@
dest.writeInt(modeId);
dest.writeFloat(renderFrameRate);
dest.writeBoolean(hasArrSupport);
+ dest.writeParcelable(frameRateCategoryRate, flags);
dest.writeInt(defaultModeId);
dest.writeInt(userPreferredModeId);
dest.writeInt(supportedModes.length);
@@ -883,6 +894,8 @@
sb.append(renderFrameRate);
sb.append(", hasArrSupport ");
sb.append(hasArrSupport);
+ sb.append(", frameRateCategoryRate ");
+ sb.append(frameRateCategoryRate);
sb.append(", defaultMode ");
sb.append(defaultModeId);
sb.append(", userPreferredModeId ");
diff --git a/core/java/android/view/FrameRateCategoryRate.java b/core/java/android/view/FrameRateCategoryRate.java
new file mode 100644
index 0000000..3c674b8
--- /dev/null
+++ b/core/java/android/view/FrameRateCategoryRate.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class to create and manage FrameRateCategoryRate for
+ * categories Normal and High.
+ * @hide
+ */
+public class FrameRateCategoryRate implements Parcelable {
+
+ private final float mNormal;
+ private final float mHigh;
+
+ /**
+ * Creates a FrameRateCategoryRate with the provided rates
+ * for the categories Normal and High respectively;
+ *
+ * @param normal rate for the category Normal.
+ * @param high rate for the category High.
+ * @hide
+ */
+ public FrameRateCategoryRate(float normal, float high) {
+ this.mNormal = normal;
+ this.mHigh = high;
+ }
+
+ /**
+ * @return the value for the category normal;
+ * @hide
+ */
+ public float getNormal() {
+ return mNormal;
+ }
+
+ /**
+ * @return the value for the category high;
+ * @hide
+ */
+ public float getHigh() {
+ return mHigh;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof FrameRateCategoryRate)) {
+ return false;
+ }
+ FrameRateCategoryRate that = (FrameRateCategoryRate) o;
+ return mNormal == that.mNormal && mHigh == that.mHigh;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ result = 31 * result + Float.hashCode(mNormal);
+ result = 31 * result + Float.hashCode(mHigh);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "FrameRateCategoryRate {"
+ + "normal=" + mNormal
+ + ", high=" + mHigh
+ + '}';
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeFloat(mNormal);
+ dest.writeFloat(mHigh);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<FrameRateCategoryRate> CREATOR =
+ new Creator<>() {
+ @Override
+ public FrameRateCategoryRate createFromParcel(Parcel in) {
+ return new FrameRateCategoryRate(in.readFloat(), in.readFloat());
+ }
+
+ @Override
+ public FrameRateCategoryRate[] newArray(int size) {
+ return new FrameRateCategoryRate[size];
+ }
+ };
+}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ece2a60..0d55544 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -1798,6 +1798,7 @@
public int activeDisplayModeId;
public float renderFrameRate;
public boolean hasArrSupport;
+ public FrameRateCategoryRate frameRateCategoryRate;
public int[] supportedColorModes;
public int activeColorMode;
@@ -1816,6 +1817,7 @@
+ ", activeDisplayModeId=" + activeDisplayModeId
+ ", renderFrameRate=" + renderFrameRate
+ ", hasArrSupport=" + hasArrSupport
+ + ", frameRateCategoryRate=" + frameRateCategoryRate
+ ", supportedColorModes=" + Arrays.toString(supportedColorModes)
+ ", activeColorMode=" + activeColorMode
+ ", hdrCapabilities=" + hdrCapabilities
@@ -1836,13 +1838,15 @@
&& activeColorMode == that.activeColorMode
&& Objects.equals(hdrCapabilities, that.hdrCapabilities)
&& preferredBootDisplayMode == that.preferredBootDisplayMode
- && hasArrSupport == that.hasArrSupport;
+ && hasArrSupport == that.hasArrSupport
+ && Objects.equals(frameRateCategoryRate, that.frameRateCategoryRate);
}
@Override
public int hashCode() {
return Objects.hash(Arrays.hashCode(supportedDisplayModes), activeDisplayModeId,
- renderFrameRate, activeColorMode, hdrCapabilities, hasArrSupport);
+ renderFrameRate, activeColorMode, hdrCapabilities, hasArrSupport,
+ frameRateCategoryRate);
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0a2a2dc..3ce6870 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1132,6 +1132,8 @@
// time for evaluating the interval between current time and
// the time when frame rate was set previously.
private static final int FRAME_RATE_SETTING_REEVALUATE_TIME = 100;
+ // timeout for surface replaced.
+ private static final int FRAME_RATE_SURFACE_REPLACED_TIME = 3000;
/*
* The variables below are used to update frame rate category
@@ -3831,6 +3833,9 @@
if (surfaceReplaced) {
mSurfaceReplaced = true;
mSurfaceSequenceId++;
+ mHandler.removeMessages(MSG_SURFACE_REPLACED_TIMEOUT);
+ mHandler.sendEmptyMessageDelayed(MSG_SURFACE_REPLACED_TIMEOUT,
+ FRAME_RATE_SURFACE_REPLACED_TIME);
}
if (alwaysConsumeSystemBarsChanged) {
mAttachInfo.mAlwaysConsumeSystemBars = mPendingAlwaysConsumeSystemBars;
@@ -6696,6 +6701,7 @@
private static final int MSG_CHECK_INVALIDATION_IDLE = 40;
private static final int MSG_REFRESH_POINTER_ICON = 41;
private static final int MSG_FRAME_RATE_SETTING = 42;
+ private static final int MSG_SURFACE_REPLACED_TIMEOUT = 43;
final class ViewRootHandler extends Handler {
@Override
@@ -6767,6 +6773,8 @@
return "MSG_TOUCH_BOOST_TIMEOUT";
case MSG_FRAME_RATE_SETTING:
return "MSG_FRAME_RATE_SETTING";
+ case MSG_SURFACE_REPLACED_TIMEOUT:
+ return "MSG_SURFACE_REPLACED_TIMEOUT";
}
return super.getMessageName(message);
}
@@ -7038,6 +7046,9 @@
*/
mIsFrameRateBoosting = false;
mIsTouchBoosting = false;
+ if (!mDrawnThisFrame) {
+ setPreferredFrameRateCategory(FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ }
break;
case MSG_REFRESH_POINTER_ICON:
if (mPointerIconEvent == null) {
@@ -7049,6 +7060,9 @@
mPreferredFrameRate = 0;
mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
break;
+ case MSG_SURFACE_REPLACED_TIMEOUT:
+ mSurfaceReplaced = false;
+ break;
}
}
}
@@ -13392,6 +13406,7 @@
private void removeVrrMessages() {
mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
+ mHandler.removeMessages(MSG_SURFACE_REPLACED_TIMEOUT);
if (mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
mInvalidationIdleMessagePosted = false;
mHandler.removeMessages(MSG_CHECK_INVALIDATION_IDLE);
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index f162b74..7fefe17 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -117,6 +117,7 @@
jfieldID activeDisplayModeId;
jfieldID renderFrameRate;
jfieldID hasArrSupport;
+ jfieldID frameRateCategoryRate;
jfieldID supportedColorModes;
jfieldID activeColorMode;
jfieldID hdrCapabilities;
@@ -292,6 +293,11 @@
jfieldID frameNumber;
} gStalledTransactionInfoClassInfo;
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} gFrameRateCategoryRateClassInfo;
+
constexpr ui::Dataspace pickDataspaceFromColorMode(const ui::ColorMode colorMode) {
switch (colorMode) {
case ui::ColorMode::DISPLAY_P3:
@@ -1388,6 +1394,13 @@
return object;
}
+static jobject convertFrameRateCategoryRateToJavaObject(
+ JNIEnv* env, const ui::FrameRateCategoryRate& frameRateCategoryRate) {
+ return env->NewObject(gFrameRateCategoryRateClassInfo.clazz,
+ gFrameRateCategoryRateClassInfo.ctor, frameRateCategoryRate.getNormal(),
+ frameRateCategoryRate.getHigh());
+}
+
static jobject convertDisplayModeToJavaObject(JNIEnv* env, const ui::DisplayMode& config) {
jobject object = env->NewObject(gDisplayModeClassInfo.clazz, gDisplayModeClassInfo.ctor);
env->SetIntField(object, gDisplayModeClassInfo.id, config.id);
@@ -1456,6 +1469,8 @@
info.activeDisplayModeId);
env->SetFloatField(object, gDynamicDisplayInfoClassInfo.renderFrameRate, info.renderFrameRate);
env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.hasArrSupport, info.hasArrSupport);
+ env->SetObjectField(object, gDynamicDisplayInfoClassInfo.frameRateCategoryRate,
+ convertFrameRateCategoryRateToJavaObject(env, info.frameRateCategoryRate));
jintArray colorModesArray = env->NewIntArray(info.supportedColorModes.size());
if (colorModesArray == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
@@ -2666,6 +2681,15 @@
GetFieldIDOrDie(env, dynamicInfoClazz, "renderFrameRate", "F");
gDynamicDisplayInfoClassInfo.hasArrSupport =
GetFieldIDOrDie(env, dynamicInfoClazz, "hasArrSupport", "Z");
+
+ gDynamicDisplayInfoClassInfo.frameRateCategoryRate =
+ GetFieldIDOrDie(env, dynamicInfoClazz, "frameRateCategoryRate",
+ "Landroid/view/FrameRateCategoryRate;");
+ jclass frameRateCategoryRateClazz = FindClassOrDie(env, "android/view/FrameRateCategoryRate");
+ gFrameRateCategoryRateClassInfo.clazz = MakeGlobalRefOrDie(env, frameRateCategoryRateClazz);
+ gFrameRateCategoryRateClassInfo.ctor =
+ GetMethodIDOrDie(env, frameRateCategoryRateClazz, "<init>", "(FF)V");
+
gDynamicDisplayInfoClassInfo.supportedColorModes =
GetFieldIDOrDie(env, dynamicInfoClazz, "supportedColorModes", "[I");
gDynamicDisplayInfoClassInfo.activeColorMode =
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
index bdc4d25..0f8dc13 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
@@ -20,13 +20,26 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import android.annotation.Nullable;
import android.app.compat.CompatChanges;
+import android.hardware.broadcastradio.Alert;
+import android.hardware.broadcastradio.AlertArea;
+import android.hardware.broadcastradio.AlertCategory;
+import android.hardware.broadcastradio.AlertCertainty;
+import android.hardware.broadcastradio.AlertInfo;
+import android.hardware.broadcastradio.AlertMessageType;
+import android.hardware.broadcastradio.AlertSeverity;
+import android.hardware.broadcastradio.AlertStatus;
+import android.hardware.broadcastradio.AlertUrgency;
import android.hardware.broadcastradio.AmFmBandRange;
import android.hardware.broadcastradio.AmFmRegionConfig;
import android.hardware.broadcastradio.ConfigFlag;
+import android.hardware.broadcastradio.Coordinate;
import android.hardware.broadcastradio.DabTableEntry;
+import android.hardware.broadcastradio.Geocode;
import android.hardware.broadcastradio.IdentifierType;
import android.hardware.broadcastradio.Metadata;
+import android.hardware.broadcastradio.Polygon;
import android.hardware.broadcastradio.ProgramFilter;
import android.hardware.broadcastradio.ProgramIdentifier;
import android.hardware.broadcastradio.ProgramInfo;
@@ -37,10 +50,13 @@
import android.hardware.radio.Flags;
import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioAlert;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioMetadata;
import android.hardware.radio.UniqueProgramIdentifier;
import android.os.ServiceSpecificException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -148,6 +164,9 @@
private static final ProgramIdentifier TEST_HAL_HD_STATION_LOCATION_ID =
AidlTestUtils.makeHalIdentifier(IdentifierType.HD_STATION_LOCATION,
TEST_HD_LOCATION_VALUE);
+ private static final ProgramIdentifier TEST_HAL_HD_FM_FREQUENCY_ID =
+ AidlTestUtils.makeHalIdentifier(IdentifierType.AMFM_FREQUENCY_KHZ,
+ TEST_HD_FREQUENCY_VALUE);
private static final UniqueProgramIdentifier TEST_DAB_UNIQUE_ID = new UniqueProgramIdentifier(
TEST_DAB_SELECTOR);
@@ -173,6 +192,57 @@
private static final Metadata TEST_HAL_HD_SUBCHANNELS = Metadata.hdSubChannelsAvailable(
TEST_HD_SUBCHANNELS);
+ private static final int TEST_STATUS = RadioAlert.STATUS_ACTUAL;
+ private static final int TEST_HAL_STATUS = AlertStatus.ACTUAL;
+ private static final int TEST_TYPE = RadioAlert.MESSAGE_TYPE_ALERT;
+ private static final int TEST_HAL_TYPE = AlertMessageType.ALERT;
+ private static final int[] TEST_CATEGORY_ARRAY = new int[]{RadioAlert.CATEGORY_CBRNE,
+ RadioAlert.CATEGORY_GEO};
+ private static final int[] TEST_HAL_CATEGORY_LIST = new int[]{AlertCategory.CBRNE,
+ AlertCategory.GEO};
+ private static final int TEST_URGENCY = RadioAlert.URGENCY_FUTURE;
+ private static final int TEST_HAL_URGENCY = AlertUrgency.FUTURE;
+ private static final int TEST_SEVERITY = RadioAlert.SEVERITY_MINOR;
+ private static final int TEST_HAL_SEVERITY = AlertSeverity.MINOR;
+ private static final int TEST_CERTAINTY = RadioAlert.CERTAINTY_UNLIKELY;
+ private static final int TEST_HAL_CERTAINTY = AlertCertainty.UNLIKELY;
+ private static final String TEST_DESCRIPTION_MESSAGE = "Test Alert Description Message.";
+ private static final String TEST_GEOCODE_VALUE_NAME = "ZIP";
+ private static final String TEST_GEOCODE_VALUE_1 = "10001";
+ private static final String TEST_GEOCODE_VALUE_2 = "10002";
+ private static final double TEST_POLYGON_LATITUDE_START = -38.47;
+ private static final double TEST_POLYGON_LONGITUDE_START = -120.14;
+ private static final RadioAlert.Coordinate TEST_POLYGON_COORDINATE_START =
+ new RadioAlert.Coordinate(TEST_POLYGON_LATITUDE_START, TEST_POLYGON_LONGITUDE_START);
+ private static final List<RadioAlert.Coordinate> TEST_COORDINATES = List.of(
+ TEST_POLYGON_COORDINATE_START, new RadioAlert.Coordinate(38.34, -119.95),
+ new RadioAlert.Coordinate(38.52, -119.74), new RadioAlert.Coordinate(38.62, -119.89),
+ TEST_POLYGON_COORDINATE_START);
+ private static final RadioAlert.Polygon TEST_POLYGON = new RadioAlert.Polygon(TEST_COORDINATES);
+ private static final Polygon TEST_HAL_POLYGON = createHalPolygon(TEST_COORDINATES);
+ private static final RadioAlert.Geocode TEST_GEOCODE_1 = new RadioAlert.Geocode(
+ TEST_GEOCODE_VALUE_NAME, TEST_GEOCODE_VALUE_1);
+ private static final RadioAlert.Geocode TEST_GEOCODE_2 = new RadioAlert.Geocode(
+ TEST_GEOCODE_VALUE_NAME, TEST_GEOCODE_VALUE_2);
+ private static final RadioAlert.AlertArea TEST_AREA = new RadioAlert.AlertArea(
+ List.of(TEST_POLYGON), List.of(TEST_GEOCODE_1, TEST_GEOCODE_2));
+ private static final AlertArea TEST_HAL_ALERT_AREA = createHalAlertArea(
+ new Polygon[]{TEST_HAL_POLYGON}, new Geocode[]{
+ createHalGeocode(TEST_GEOCODE_VALUE_NAME, TEST_GEOCODE_VALUE_1),
+ createHalGeocode(TEST_GEOCODE_VALUE_NAME, TEST_GEOCODE_VALUE_2)});
+ private static final String TEST_LANGUAGE = "en-US";
+
+ private static final RadioAlert.AlertInfo TEST_ALERT_INFO_1 = new RadioAlert.AlertInfo(
+ TEST_CATEGORY_ARRAY, TEST_URGENCY, TEST_SEVERITY, TEST_CERTAINTY,
+ TEST_DESCRIPTION_MESSAGE, List.of(TEST_AREA), TEST_LANGUAGE);
+ private static final AlertInfo TEST_HAL_ALERT_INFO = createHalAlertInfo(
+ TEST_HAL_CATEGORY_LIST, TEST_HAL_URGENCY, TEST_HAL_SEVERITY, TEST_HAL_CERTAINTY,
+ TEST_DESCRIPTION_MESSAGE, new AlertArea[]{TEST_HAL_ALERT_AREA}, TEST_LANGUAGE);
+ private static final RadioAlert TEST_ALERT = new RadioAlert(TEST_STATUS, TEST_TYPE,
+ List.of(TEST_ALERT_INFO_1));
+ private static final Alert TEST_HAL_ALERT = createHalAlert(TEST_HAL_STATUS, TEST_HAL_TYPE,
+ new AlertInfo[]{TEST_HAL_ALERT_INFO});
+
@Rule
public final Expect expect = Expect.create();
@Rule
@@ -576,6 +646,42 @@
}
@Test
+ @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+ public void programInfoFromHalProgramInfo_withAlertMessageAndFlagEnabled() {
+ android.hardware.broadcastradio.ProgramSelector halHdSelector =
+ AidlTestUtils.makeHalSelector(TEST_HAL_HD_STATION_EXT_ID,
+ new ProgramIdentifier[]{});
+ ProgramInfo halHdProgramInfo = AidlTestUtils.makeHalProgramInfo(halHdSelector,
+ TEST_HAL_HD_STATION_EXT_ID, TEST_HAL_HD_FM_FREQUENCY_ID, TEST_SIGNAL_QUALITY,
+ new ProgramIdentifier[]{}, new Metadata[]{});
+ halHdProgramInfo.emergencyAlert = TEST_HAL_ALERT;
+
+ RadioManager.ProgramInfo programInfo =
+ ConversionUtils.programInfoFromHalProgramInfo(halHdProgramInfo);
+
+ expect.withMessage("Alert of converted HD program info with alert and enabled flag")
+ .that(programInfo.getAlert()).isEqualTo(TEST_ALERT);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+ public void programInfoFromHalProgramInfo_withAlertMessageAndFlagDisabled() {
+ android.hardware.broadcastradio.ProgramSelector halHdSelector =
+ AidlTestUtils.makeHalSelector(TEST_HAL_HD_STATION_EXT_ID,
+ new ProgramIdentifier[]{});
+ ProgramInfo halHdProgramInfo = AidlTestUtils.makeHalProgramInfo(halHdSelector,
+ TEST_HAL_HD_STATION_EXT_ID, TEST_HAL_HD_FM_FREQUENCY_ID, TEST_SIGNAL_QUALITY,
+ new ProgramIdentifier[]{}, new Metadata[]{});
+ halHdProgramInfo.emergencyAlert = TEST_HAL_ALERT;
+
+ RadioManager.ProgramInfo programInfo =
+ ConversionUtils.programInfoFromHalProgramInfo(halHdProgramInfo);
+
+ expect.withMessage("Alert of converted HD program info with alert and disabled flag")
+ .that(programInfo.getAlert()).isNull();
+ }
+
+ @Test
public void tunedProgramInfoFromHalProgramInfo_withInvalidDabProgramInfo() {
android.hardware.broadcastradio.ProgramSelector invalidHalDabSelector =
AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
@@ -851,7 +957,7 @@
}
@Test
- public void radioMetadataFromHalMetadata_withHdMedatadataAndFlagEnabled() {
+ public void radioMetadataFromHalMetadata_withHdMetadataAndFlagEnabled() {
mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
String genreValue = "genreTest";
String commentShortDescriptionValue = "commentShortDescriptionTest";
@@ -973,6 +1079,57 @@
.isEmpty();
}
+ @Test
+ @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+ public void radioAlertFromHalAlert() {
+ RadioAlert convertedAlert = ConversionUtils.radioAlertFromHalAlert(TEST_HAL_ALERT);
+
+ expect.withMessage("Converted alert").that(convertedAlert)
+ .isEqualTo(TEST_ALERT);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+ public void radioAlertFromHalAlert_withLowThanFourCoordinates() {
+ Polygon invalidPolygon = createHalPolygon(List.of(
+ TEST_POLYGON_COORDINATE_START, new RadioAlert.Coordinate(38.34, -119.95),
+ TEST_POLYGON_COORDINATE_START));
+ AlertInfo halAlertInfo = createHalAlertInfo(TEST_HAL_CATEGORY_LIST, TEST_HAL_URGENCY,
+ TEST_HAL_SEVERITY, TEST_HAL_CERTAINTY, TEST_DESCRIPTION_MESSAGE,
+ new AlertArea[]{createHalAlertArea(new Polygon[]{invalidPolygon},
+ new Geocode[]{})}, TEST_LANGUAGE);
+ Alert halAlert = createHalAlert(TEST_HAL_STATUS, TEST_HAL_TYPE,
+ new AlertInfo[]{halAlertInfo });
+
+ RadioAlert convertedAlert = ConversionUtils.radioAlertFromHalAlert(halAlert);
+
+ expect.withMessage("Empty polygon list with less than 4 coordinates")
+ .that(convertedAlert.getInfoList().get(0).getAreas().get(0).getPolygons())
+ .isEmpty();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+ public void radioAlertFromHalAlert_withDifferentFirstAndLastCoordinate() {
+ Polygon invalidPolygon = createHalPolygon(List.of(
+ TEST_POLYGON_COORDINATE_START, new RadioAlert.Coordinate(38.34, -119.95),
+ new RadioAlert.Coordinate(38.52, -119.74),
+ new RadioAlert.Coordinate(38.62, -119.89),
+ new RadioAlert.Coordinate(38.42, -120.14)));
+ AlertInfo halAlertInfo = createHalAlertInfo(TEST_HAL_CATEGORY_LIST, TEST_HAL_URGENCY,
+ TEST_HAL_SEVERITY, TEST_HAL_CERTAINTY, TEST_DESCRIPTION_MESSAGE,
+ new AlertArea[]{createHalAlertArea(new Polygon[]{invalidPolygon},
+ new Geocode[]{})}, TEST_LANGUAGE);
+ Alert halAlert = createHalAlert(TEST_HAL_STATUS, TEST_HAL_TYPE,
+ new AlertInfo[]{halAlertInfo});
+
+ RadioAlert convertedAlert = ConversionUtils.radioAlertFromHalAlert(halAlert);
+
+ expect.withMessage("Empty polygon list with different first and last coordinates")
+ .that(convertedAlert.getInfoList().get(0).getAreas().get(0).getPolygons())
+ .isEmpty();
+ }
+
private static RadioManager.ModuleProperties createModuleProperties() {
AmFmRegionConfig amFmConfig = createAmFmRegionConfig();
DabTableEntry[] dabTableEntries = new DabTableEntry[]{
@@ -1028,14 +1185,62 @@
return halProperties;
}
- private ProgramSelector.Identifier createHdStationLocationIdWithFlagEnabled() {
+ private static ProgramSelector.Identifier createHdStationLocationIdWithFlagEnabled() {
return new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_HD_STATION_LOCATION,
TEST_HD_LOCATION_VALUE);
}
- private ProgramSelector createHdSelectorWithFlagEnabled() {
+ private static ProgramSelector createHdSelectorWithFlagEnabled() {
return new ProgramSelector(ProgramSelector.PROGRAM_TYPE_FM_HD, TEST_HD_STATION_EXT_ID,
new ProgramSelector.Identifier[]{createHdStationLocationIdWithFlagEnabled()},
/* vendorIds= */ null);
}
+
+ private static Alert createHalAlert(int status, int messageType, AlertInfo[] alertInfos) {
+ Alert halAlert = new Alert();
+ halAlert.status = status;
+ halAlert.messageType = messageType;
+ halAlert.infoArray = alertInfos;
+ return halAlert;
+ }
+
+ private static AlertInfo createHalAlertInfo(int[] categoryArray, int urgency, int severity,
+ int certainty, String description, AlertArea[] areas, @Nullable String language) {
+ AlertInfo info = new AlertInfo();
+ info.categoryArray = categoryArray;
+ info.urgency = urgency;
+ info.severity = severity;
+ info.certainty = certainty;
+ info.description = description;
+ info.areas = areas;
+ info.language = language;
+ return info;
+ }
+
+ private static AlertArea createHalAlertArea(Polygon[] polygons, Geocode[] geocodes) {
+ AlertArea area = new AlertArea();
+ area.polygons = polygons;
+ area.geocodes = geocodes;
+ return area;
+ }
+
+ private static Polygon createHalPolygon(List<RadioAlert.Coordinate> coordinates) {
+ Coordinate[] halCoordinates = new Coordinate[coordinates.size()];
+ for (int idx = 0; idx < coordinates.size(); idx++) {
+ Coordinate halCoordinate = new Coordinate();
+ halCoordinate.latitude = coordinates.get(idx).getLatitude();
+ halCoordinate.longitude = coordinates.get(idx).getLongitude();
+ halCoordinates[idx] = halCoordinate;
+ }
+ Polygon polygon = new Polygon();
+ polygon.coordinates = halCoordinates;
+ return polygon;
+ }
+
+ private static Geocode createHalGeocode(String valueName, String value) {
+ Geocode halGeocode = new Geocode();
+ halGeocode.valueName = valueName;
+ halGeocode.value = value;
+ return halGeocode;
+ }
}
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index 483ebc2..fb1efa8 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -891,6 +891,65 @@
});
}
+ @Test
+ @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
+ })
+ public void testTouchBoostReset() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
+ mActivityRule.runOnUiThread(() -> {
+ ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
+ layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
+ mMovingView.setLayoutParams(layoutParams);
+ mMovingView.setOnClickListener((v) -> {});
+ });
+ waitForFrameRateCategoryToSettle();
+
+ int[] position = new int[2];
+ mActivityRule.runOnUiThread(() -> {
+ mMovingView.getLocationOnScreen(position);
+ position[0] += mMovingView.getWidth() / 2;
+ position[1] += mMovingView.getHeight() / 2;
+ });
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+ long now = SystemClock.uptimeMillis();
+ MotionEvent down = MotionEvent.obtain(
+ now, // downTime
+ now, // eventTime
+ MotionEvent.ACTION_DOWN, // action
+ position[0], // x
+ position[1], // y
+ 0 // metaState
+ );
+ down.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ instrumentation.sendPointerSync(down);
+ assertEquals(FRAME_RATE_CATEGORY_HIGH_HINT, mViewRoot.getLastPreferredFrameRateCategory());
+
+ MotionEvent up = MotionEvent.obtain(
+ now, // downTime
+ now, // eventTime
+ MotionEvent.ACTION_UP, // action
+ position[0], // x
+ position[1], // y
+ 0 // metaState
+ );
+ up.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ instrumentation.sendPointerSync(up);
+
+ // Wait for idle timeout - 100 ms logner to avoid flaky
+ Thread.sleep(3100);
+
+ // Should not touch boost after the time out
+ assertEquals(false, mViewRoot.getIsTouchBoosting());
+ assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ }
+
+
@LargeTest
@Test
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index 792e248..ed17fde 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -24,7 +24,6 @@
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.fonts.Font;
-import android.os.Build;
import com.android.internal.util.Preconditions;
import com.android.text.flags.Flags;
@@ -54,8 +53,6 @@
Typeface.class.getClassLoader(), nReleaseFunc());
}
- private static boolean sIsRobolectric = Build.FINGERPRINT.equals("robolectric");
-
private final long mLayoutPtr;
private final float mXOffset;
private final float mYOffset;
@@ -255,7 +252,7 @@
mXOffset = xOffset;
mYOffset = yOffset;
- if (!sIsRobolectric && Flags.typefaceRedesign()) {
+ if (Flags.typefaceRedesign()) {
int fontCount = nGetFontCount(layoutPtr);
mFonts = new ArrayList<>(fontCount);
for (int i = 0; i < fontCount; ++i) {
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/WindowAnimator.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/WindowAnimator.kt
index 0435085..91d66ea 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/WindowAnimator.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/WindowAnimator.kt
@@ -16,8 +16,9 @@
package com.android.wm.shell.shared.animation
-import android.animation.RectEvaluator
+import android.animation.PointFEvaluator
import android.animation.ValueAnimator
+import android.graphics.PointF
import android.graphics.Rect
import android.util.DisplayMetrics
import android.util.TypedValue
@@ -52,46 +53,56 @@
change: TransitionInfo.Change,
transaction: SurfaceControl.Transaction,
): ValueAnimator {
- val startBounds =
- createBounds(
+ val startPos =
+ getPosition(
displayMetrics,
- change.startAbsBounds,
+ change.endAbsBounds,
boundsAnimDef.startScale,
boundsAnimDef.startOffsetYDp,
)
val leash = change.leash
- val endBounds =
- createBounds(
+ val endPos =
+ getPosition(
displayMetrics,
- change.startAbsBounds,
+ change.endAbsBounds,
boundsAnimDef.endScale,
boundsAnimDef.endOffsetYDp,
)
- return ValueAnimator.ofObject(RectEvaluator(), startBounds, endBounds).apply {
+ return ValueAnimator.ofObject(PointFEvaluator(), startPos, endPos).apply {
duration = boundsAnimDef.durationMs
interpolator = boundsAnimDef.interpolator
addUpdateListener { animation ->
- val animBounds = animation.animatedValue as Rect
- val animScale = 1 - (1 - boundsAnimDef.endScale) * animation.animatedFraction
+ val animPos = animation.animatedValue as PointF
+ val animScale =
+ interpolate(
+ boundsAnimDef.startScale,
+ boundsAnimDef.endScale,
+ animation.animatedFraction
+ )
transaction
- .setPosition(leash, animBounds.left.toFloat(), animBounds.top.toFloat())
+ .setPosition(leash, animPos.x, animPos.y)
.setScale(leash, animScale, animScale)
.apply()
}
}
}
- private fun createBounds(
+ private fun interpolate(startVal: Float, endVal: Float, fraction: Float): Float {
+ require(fraction in 0.0f..1.0f)
+ return startVal + (endVal - startVal) * fraction
+ }
+
+ private fun getPosition(
displayMetrics: DisplayMetrics,
- origBounds: Rect,
+ bounds: Rect,
scale: Float,
offsetYDp: Float
- ) = Rect(origBounds).apply {
- check(scale in 0.0..1.0)
- // Scale the bounds down with an anchor in the center
- inset(
- (origBounds.width().toFloat() * (1 - scale) / 2).toInt(),
- (origBounds.height().toFloat() * (1 - scale) / 2).toInt(),
+ ) = PointF(bounds.left.toFloat(), bounds.top.toFloat()).apply {
+ check(scale in 0.0f..1.0f)
+ // Scale the bounds down with an anchor in the center
+ offset(
+ (bounds.width().toFloat() * (1 - scale) / 2),
+ (bounds.height().toFloat() * (1 - scale) / 2),
)
val offsetYPx =
TypedValue.applyDimension(
@@ -100,6 +111,6 @@
displayMetrics,
)
.toInt()
- offset(/* dx= */ 0, offsetYPx)
+ offset(/* dx= */ 0f, offsetYPx.toFloat())
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 61de077..09e77fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -19,6 +19,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR;
@@ -259,6 +260,7 @@
FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
lp.setTitle("Desktop Mode Visual Indicator");
lp.setTrustedOverlay();
+ lp.inputFeatures |= INPUT_FEATURE_NO_INPUT_CHANNEL;
final WindowlessWindowManager windowManager = new WindowlessWindowManager(
mTaskInfo.configuration, mLeash,
null /* hostInputToken */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index 8f089dc..2303ec9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -23,6 +23,7 @@
import android.graphics.Bitmap
import android.graphics.Rect
import android.os.IBinder
+import android.os.UserHandle
import android.util.Slog
import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
@@ -361,6 +362,8 @@
private lateinit var resizeVeilBitmap: Bitmap
private lateinit var resizeVeil: ResizeVeil
private val displayContext = displayController.getDisplayContext(taskInfo.displayId)
+ private val userContext =
+ context.createContextAsUser(UserHandle.of(taskInfo.userId), /* flags= */ 0)
fun initIfNeeded() {
if (!isInitialised) {
@@ -379,7 +382,7 @@
displayContext?.let {
createIconFactory(displayContext, R.dimen.desktop_mode_resize_veil_icon_size)
} ?: return
- val pm = context.getApplicationContext().getPackageManager()
+ val pm = userContext.getPackageManager()
val activityInfo = pm.getActivityInfo(baseActivity, 0 /* flags */)
val provider = IconProvider(displayContext)
val appIconDrawable = provider.getIcon(activityInfo)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/WindowAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/WindowAnimatorTest.kt
index 841ffcc..c19232b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/WindowAnimatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/WindowAnimatorTest.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.shared.animation
-import android.animation.ValueAnimator
+import android.graphics.PointF
import android.graphics.Rect
import android.util.DisplayMetrics
import android.view.SurfaceControl
@@ -31,6 +31,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@@ -44,12 +45,33 @@
private val displayMetrics = DisplayMetrics().apply { density = 1f }
+ private val positionXArgumentCaptor = argumentCaptor<Float>()
+ private val positionYArgumentCaptor = argumentCaptor<Float>()
+ private val scaleXArgumentCaptor = argumentCaptor<Float>()
+ private val scaleYArgumentCaptor = argumentCaptor<Float>()
+
@Before
fun setup() {
whenever(change.leash).thenReturn(leash)
- whenever(change.startAbsBounds).thenReturn(START_BOUNDS)
+ whenever(change.endAbsBounds).thenReturn(END_BOUNDS)
whenever(transaction.setPosition(any(), anyFloat(), anyFloat())).thenReturn(transaction)
whenever(transaction.setScale(any(), anyFloat(), anyFloat())).thenReturn(transaction)
+ whenever(
+ transaction.setPosition(
+ any(),
+ positionXArgumentCaptor.capture(),
+ positionYArgumentCaptor.capture(),
+ )
+ )
+ .thenReturn(transaction)
+ whenever(
+ transaction.setScale(
+ any(),
+ scaleXArgumentCaptor.capture(),
+ scaleYArgumentCaptor.capture(),
+ )
+ )
+ .thenReturn(transaction)
}
@Test
@@ -67,16 +89,18 @@
change,
transaction
)
+ valueAnimator.start()
assertThat(valueAnimator.duration).isEqualTo(100L)
assertThat(valueAnimator.interpolator).isEqualTo(Interpolators.STANDARD_ACCELERATE)
- assertStartAndEndBounds(valueAnimator, startBounds = START_BOUNDS, endBounds = START_BOUNDS)
+ val expectedPosition = PointF(END_BOUNDS.left.toFloat(), END_BOUNDS.top.toFloat())
+ assertTransactionParams(expectedPosition, expectedScale = PointF(1f, 1f))
}
@Test
- fun createBoundsAnimator_startScaleAndOffset_returnsCorrectBounds() = runOnUiThread {
+ fun createBoundsAnimator_startScaleAndOffset_correctPosAndScale() = runOnUiThread {
val bounds = Rect(/* left= */ 100, /* top= */ 200, /* right= */ 300, /* bottom= */ 400)
- whenever(change.startAbsBounds).thenReturn(bounds)
+ whenever(change.endAbsBounds).thenReturn(bounds)
val boundsAnimParams =
WindowAnimator.BoundsAnimationParams(
durationMs = 100L,
@@ -92,19 +116,18 @@
change,
transaction
)
+ valueAnimator.start()
- assertStartAndEndBounds(
- valueAnimator,
- startBounds =
- Rect(/* left= */ 150, /* top= */ 260, /* right= */ 250, /* bottom= */ 360),
- endBounds = bounds,
+ assertTransactionParams(
+ expectedPosition = PointF(150f, 260f),
+ expectedScale = PointF(0.5f, 0.5f),
)
}
@Test
- fun createBoundsAnimator_endScaleAndOffset_returnsCorrectBounds() = runOnUiThread {
+ fun createBoundsAnimator_endScaleAndOffset_correctPosAndScale() = runOnUiThread {
val bounds = Rect(/* left= */ 100, /* top= */ 200, /* right= */ 300, /* bottom= */ 400)
- whenever(change.startAbsBounds).thenReturn(bounds)
+ whenever(change.endAbsBounds).thenReturn(bounds)
val boundsAnimParams =
WindowAnimator.BoundsAnimationParams(
durationMs = 100L,
@@ -120,28 +143,56 @@
change,
transaction
)
+ valueAnimator.start()
+ valueAnimator.end()
- assertStartAndEndBounds(
- valueAnimator,
- startBounds = bounds,
- endBounds = Rect(/* left= */ 150, /* top= */ 260, /* right= */ 250, /* bottom= */ 360),
+ assertTransactionParams(
+ expectedPosition = PointF(150f, 260f),
+ expectedScale = PointF(0.5f, 0.5f),
)
}
- private fun assertStartAndEndBounds(
- valueAnimator: ValueAnimator,
- startBounds: Rect,
- endBounds: Rect,
- ) {
- valueAnimator.start()
- valueAnimator.animatedValue
- assertThat(valueAnimator.animatedValue).isEqualTo(startBounds)
- valueAnimator.end()
- assertThat(valueAnimator.animatedValue).isEqualTo(endBounds)
+ @Test
+ fun createBoundsAnimator_middleOfAnimation_correctPosAndScale() = runOnUiThread {
+ val bounds = Rect(/* left= */ 100, /* top= */ 200, /* right= */ 300, /* bottom= */ 400)
+ whenever(change.endAbsBounds).thenReturn(bounds)
+ val boundsAnimParams =
+ WindowAnimator.BoundsAnimationParams(
+ durationMs = 100L,
+ endOffsetYDp = 10f,
+ startScale = 0.5f,
+ endScale = 0.9f,
+ interpolator = Interpolators.LINEAR,
+ )
+
+ val valueAnimator =
+ WindowAnimator.createBoundsAnimator(
+ displayMetrics,
+ boundsAnimParams,
+ change,
+ transaction
+ )
+ valueAnimator.currentPlayTime = 50
+
+ assertTransactionParams(
+ // We should have a window of size 140x140, which we centre by placing at pos 130, 230.
+ // Then add 10*0.5 as y-offset
+ expectedPosition = PointF(130f, 235f),
+ expectedScale = PointF(0.7f, 0.7f),
+ )
+ }
+
+ private fun assertTransactionParams(expectedPosition: PointF, expectedScale: PointF) {
+ assertThat(positionXArgumentCaptor.lastValue).isWithin(TOLERANCE).of(expectedPosition.x)
+ assertThat(positionYArgumentCaptor.lastValue).isWithin(TOLERANCE).of(expectedPosition.y)
+ assertThat(scaleXArgumentCaptor.lastValue).isWithin(TOLERANCE).of(expectedScale.x)
+ assertThat(scaleYArgumentCaptor.lastValue).isWithin(TOLERANCE).of(expectedScale.y)
}
companion object {
- private val START_BOUNDS =
+ private val END_BOUNDS =
Rect(/* left= */ 10, /* top= */ 20, /* right= */ 30, /* bottom= */ 40)
+
+ private const val TOLERANCE = 1e-3f
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
index 0ccd424..52e93bb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
@@ -39,6 +39,7 @@
import org.mockito.kotlin.mock
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -73,6 +74,7 @@
returnToDragStartAnimatorMock,
desktopRepository,
)
+ whenever(contextMock.createContextAsUser(any(), any())).thenReturn(contextMock)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
index 0b04a21..5b0cdc3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
@@ -112,6 +112,7 @@
returnToDragStartAnimator,
desktopRepository,
)
+ whenever(context.createContextAsUser(any(), any())).thenReturn(context)
}
@Test
@@ -479,6 +480,7 @@
assertThat(tilingDecoration.rightTaskResizingHelper).isNull()
verify(desktopWindowDecoration, times(2)).removeDragResizeListener(any())
verify(tiledTaskHelper, times(2)).dispose()
+ verify(context, never()).getApplicationContext()
}
private fun initTiledTaskHelperMock(taskInfo: ActivityManager.RunningTaskInfo) {
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index d50a92b..7921ce0 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -908,16 +908,6 @@
}
flag {
- name: "delayed_wakelock_release_on_background_thread"
- namespace: "systemui"
- description: "Released delayed wakelocks on background threads to avoid janking screen transitions."
- bug: "316128516"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "notify_power_manager_user_activity_background"
namespace: "systemui"
description: "Decide whether to notify the user activity to power manager in the background thread."
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt
similarity index 90%
rename from packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt
index 5a76489..f68a1b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt
@@ -19,6 +19,7 @@
import android.content.testableContext
import android.platform.test.annotations.EnableFlags
import android.view.Display
+import android.view.layoutInflater
import android.view.mockWindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -49,14 +50,18 @@
private val applicationContext = kosmos.testableContext
private val applicationWindowManager = kosmos.mockWindowManager
+ private val applicationLayoutInflater = kosmos.layoutInflater
- private val repo =
+ // Lazy so that @EnableFlags has time to run before this repo is instantiated
+ private val repo by lazy {
DisplayWindowPropertiesRepositoryImpl(
kosmos.applicationCoroutineScope,
applicationContext,
applicationWindowManager,
+ kosmos.layoutInflater,
fakeDisplayRepository,
)
+ }
@Before
fun start() {
@@ -81,6 +86,7 @@
windowType = WINDOW_TYPE_FOO,
context = applicationContext,
windowManager = applicationWindowManager,
+ layoutInflater = applicationLayoutInflater,
)
)
}
@@ -102,6 +108,14 @@
}
@Test
+ fun get_nonDefaultDisplayId_returnsNewLayoutInflater() =
+ testScope.runTest {
+ val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)
+
+ assertThat(displayContext.layoutInflater).isNotSameInstanceAs(applicationLayoutInflater)
+ }
+
+ @Test
fun get_multipleCallsForDefaultDisplay_returnsSameInstance() =
testScope.runTest {
val displayContext = repo.get(DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 9db2014..319f1e5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -44,6 +44,8 @@
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.ui.viewmodel.lockscreenUserActionsViewModel
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -70,7 +72,6 @@
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -101,7 +102,6 @@
@EnableSceneContainer
class SceneFrameworkIntegrationTest : SysuiTestCase() {
private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
private var bouncerSceneJob: Job? = null
@Before
@@ -137,151 +137,149 @@
.isTrue()
}
- @Test
- fun startsInLockscreenScene() =
- testScope.runTest { kosmos.assertCurrentScene(Scenes.Lockscreen) }
+ @Test fun startsInLockscreenScene() = kosmos.runTest { assertCurrentScene(Scenes.Lockscreen) }
@Test
fun clickLockButtonAndEnterCorrectPin_unlocksDevice() =
- testScope.runTest {
- kosmos.emulateUserDrivenTransition(Scenes.Bouncer)
+ kosmos.runTest {
+ emulateUserDrivenTransition(Scenes.Bouncer)
- kosmos.fakeSceneDataSource.pause()
- kosmos.enterPin()
- kosmos.emulatePendingTransitionProgress(expectedVisible = false)
- kosmos.assertCurrentScene(Scenes.Gone)
+ fakeSceneDataSource.pause()
+ enterPin()
+ emulatePendingTransitionProgress(expectedVisible = false)
+ assertCurrentScene(Scenes.Gone)
}
@Test
fun swipeUpOnLockscreen_enterCorrectPin_unlocksDevice() =
- testScope.runTest {
- val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+ kosmos.runTest {
+ val actions by testScope.collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
- kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
+ emulateUserDrivenTransition(to = upDestinationSceneKey)
- kosmos.fakeSceneDataSource.pause()
- kosmos.enterPin()
- kosmos.emulatePendingTransitionProgress(expectedVisible = false)
- kosmos.assertCurrentScene(Scenes.Gone)
+ fakeSceneDataSource.pause()
+ enterPin()
+ emulatePendingTransitionProgress(expectedVisible = false)
+ assertCurrentScene(Scenes.Gone)
}
@Test
fun swipeUpOnLockscreen_withAuthMethodSwipe_dismissesLockscreen() =
- testScope.runTest {
- kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+ kosmos.runTest {
+ setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
- val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+ val actions by testScope.collectLastValue(lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
- kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
+ emulateUserDrivenTransition(to = upDestinationSceneKey)
}
@Test
fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
- testScope.runTest {
- val actions by collectLastValue(kosmos.shadeUserActionsViewModel.actions)
- kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
- kosmos.assertCurrentScene(Scenes.Lockscreen)
+ kosmos.runTest {
+ val actions by testScope.collectLastValue(shadeUserActionsViewModel.actions)
+ setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+ assertCurrentScene(Scenes.Lockscreen)
// Emulate a user swipe to the shade scene.
- kosmos.emulateUserDrivenTransition(to = Scenes.Shade)
- kosmos.assertCurrentScene(Scenes.Shade)
+ emulateUserDrivenTransition(to = Scenes.Shade)
+ assertCurrentScene(Scenes.Shade)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Lockscreen)
- kosmos.emulateUserDrivenTransition(to = Scenes.Lockscreen)
+ emulateUserDrivenTransition(to = Scenes.Lockscreen)
}
@Test
fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenDismissed_goesToGone() =
- testScope.runTest {
- val actions by collectLastValue(kosmos.shadeUserActionsViewModel.actions)
- val canSwipeToEnter by collectLastValue(kosmos.deviceEntryInteractor.canSwipeToEnter)
+ kosmos.runTest {
+ val actions by testScope.collectLastValue(shadeUserActionsViewModel.actions)
+ val canSwipeToEnter by testScope.collectLastValue(deviceEntryInteractor.canSwipeToEnter)
- kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+ setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
assertThat(canSwipeToEnter).isTrue()
- kosmos.assertCurrentScene(Scenes.Lockscreen)
+ assertCurrentScene(Scenes.Lockscreen)
// Emulate a user swipe to dismiss the lockscreen.
- kosmos.emulateUserDrivenTransition(to = Scenes.Gone)
- kosmos.assertCurrentScene(Scenes.Gone)
+ emulateUserDrivenTransition(to = Scenes.Gone)
+ assertCurrentScene(Scenes.Gone)
// Emulate a user swipe to the shade scene.
- kosmos.emulateUserDrivenTransition(to = Scenes.Shade)
- kosmos.assertCurrentScene(Scenes.Shade)
+ emulateUserDrivenTransition(to = Scenes.Shade)
+ assertCurrentScene(Scenes.Shade)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
- kosmos.emulateUserDrivenTransition(to = Scenes.Gone)
+ emulateUserDrivenTransition(to = Scenes.Gone)
}
@Test
fun withAuthMethodNone_deviceWakeUp_skipsLockscreen() =
- testScope.runTest {
- kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = false)
- kosmos.putDeviceToSleep()
- kosmos.assertCurrentScene(Scenes.Lockscreen)
+ kosmos.runTest {
+ setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = false)
+ putDeviceToSleep()
+ assertCurrentScene(Scenes.Lockscreen)
- kosmos.wakeUpDevice()
- kosmos.assertCurrentScene(Scenes.Gone)
+ wakeUpDevice()
+ assertCurrentScene(Scenes.Gone)
}
@Test
fun withAuthMethodSwipe_deviceWakeUp_doesNotSkipLockscreen() =
- testScope.runTest {
- kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
- kosmos.putDeviceToSleep()
- kosmos.assertCurrentScene(Scenes.Lockscreen)
+ kosmos.runTest {
+ setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+ putDeviceToSleep()
+ assertCurrentScene(Scenes.Lockscreen)
- kosmos.wakeUpDevice()
- kosmos.assertCurrentScene(Scenes.Lockscreen)
+ wakeUpDevice()
+ assertCurrentScene(Scenes.Lockscreen)
}
@Test
fun lockDeviceLocksDevice() =
- testScope.runTest {
- kosmos.unlockDevice()
- kosmos.assertCurrentScene(Scenes.Gone)
+ kosmos.runTest {
+ unlockDevice()
+ assertCurrentScene(Scenes.Gone)
- kosmos.lockDevice()
- kosmos.assertCurrentScene(Scenes.Lockscreen)
+ lockDevice()
+ assertCurrentScene(Scenes.Lockscreen)
}
@Test
fun deviceGoesToSleep_switchesToLockscreen() =
- testScope.runTest {
- kosmos.unlockDevice()
- kosmos.assertCurrentScene(Scenes.Gone)
+ kosmos.runTest {
+ unlockDevice()
+ assertCurrentScene(Scenes.Gone)
- kosmos.putDeviceToSleep()
- kosmos.assertCurrentScene(Scenes.Lockscreen)
+ putDeviceToSleep()
+ assertCurrentScene(Scenes.Lockscreen)
}
@Test
fun deviceGoesToSleep_wakeUp_unlock() =
- testScope.runTest {
- kosmos.unlockDevice()
- kosmos.assertCurrentScene(Scenes.Gone)
- kosmos.putDeviceToSleep()
- kosmos.assertCurrentScene(Scenes.Lockscreen)
- kosmos.wakeUpDevice()
- kosmos.assertCurrentScene(Scenes.Lockscreen)
+ kosmos.runTest {
+ unlockDevice()
+ assertCurrentScene(Scenes.Gone)
+ putDeviceToSleep()
+ assertCurrentScene(Scenes.Lockscreen)
+ wakeUpDevice()
+ assertCurrentScene(Scenes.Lockscreen)
- kosmos.unlockDevice()
- kosmos.assertCurrentScene(Scenes.Gone)
+ unlockDevice()
+ assertCurrentScene(Scenes.Gone)
}
@Test
fun swipeUpOnLockscreenWhileUnlocked_dismissesLockscreen() =
- testScope.runTest {
- kosmos.unlockDevice()
- val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+ kosmos.runTest {
+ unlockDevice()
+ val actions by testScope.collectLastValue(lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
@@ -289,46 +287,46 @@
@Test
fun deviceGoesToSleep_withLockTimeout_staysOnLockscreen() =
- testScope.runTest {
- kosmos.unlockDevice()
- kosmos.assertCurrentScene(Scenes.Gone)
- kosmos.putDeviceToSleep()
- kosmos.assertCurrentScene(Scenes.Lockscreen)
+ kosmos.runTest {
+ unlockDevice()
+ assertCurrentScene(Scenes.Gone)
+ putDeviceToSleep()
+ assertCurrentScene(Scenes.Lockscreen)
// Pretend like the timeout elapsed and now lock the device.
- kosmos.lockDevice()
- kosmos.assertCurrentScene(Scenes.Lockscreen)
+ lockDevice()
+ assertCurrentScene(Scenes.Lockscreen)
}
@Test
fun dismissingIme_whileOnPasswordBouncer_navigatesToLockscreen() =
- testScope.runTest {
- kosmos.setAuthMethod(AuthenticationMethodModel.Password)
- val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+ kosmos.runTest {
+ setAuthMethod(AuthenticationMethodModel.Password)
+ val actions by testScope.collectLastValue(lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
- kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
+ emulateUserDrivenTransition(to = upDestinationSceneKey)
- kosmos.fakeSceneDataSource.pause()
- kosmos.dismissIme()
+ fakeSceneDataSource.pause()
+ dismissIme()
- kosmos.emulatePendingTransitionProgress()
- kosmos.assertCurrentScene(Scenes.Lockscreen)
+ emulatePendingTransitionProgress()
+ assertCurrentScene(Scenes.Lockscreen)
}
@Test
fun bouncerActionButtonClick_opensEmergencyServicesDialer() =
- testScope.runTest {
- kosmos.setAuthMethod(AuthenticationMethodModel.Password)
- val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+ kosmos.runTest {
+ setAuthMethod(AuthenticationMethodModel.Password)
+ val actions by testScope.collectLastValue(lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
- kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
+ emulateUserDrivenTransition(to = upDestinationSceneKey)
val bouncerActionButton by
- collectLastValue(kosmos.bouncerSceneContentViewModel.actionButton)
+ testScope.collectLastValue(bouncerSceneContentViewModel.actionButton)
assertWithMessage("Bouncer action button not visible")
.that(bouncerActionButton)
.isNotNull()
@@ -340,56 +338,56 @@
@Test
fun bouncerActionButtonClick_duringCall_returnsToCall() =
- testScope.runTest {
- kosmos.setAuthMethod(AuthenticationMethodModel.Password)
- kosmos.startPhoneCall()
- val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+ kosmos.runTest {
+ setAuthMethod(AuthenticationMethodModel.Password)
+ startPhoneCall()
+ val actions by testScope.collectLastValue(lockscreenUserActionsViewModel.actions)
val upDestinationSceneKey =
(actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
- kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
+ emulateUserDrivenTransition(to = upDestinationSceneKey)
val bouncerActionButton by
- collectLastValue(kosmos.bouncerSceneContentViewModel.actionButton)
+ testScope.collectLastValue(bouncerSceneContentViewModel.actionButton)
assertWithMessage("Bouncer action button not visible during call")
.that(bouncerActionButton)
.isNotNull()
kosmos.bouncerSceneContentViewModel.onActionButtonClicked(bouncerActionButton!!)
runCurrent()
- verify(kosmos.mockTelecomManager).showInCallScreen(any())
+ verify(mockTelecomManager).showInCallScreen(any())
}
@Test
fun showBouncer_whenLockedSimIntroduced() =
- testScope.runTest {
- kosmos.setAuthMethod(AuthenticationMethodModel.None)
- kosmos.introduceLockedSim()
- kosmos.assertCurrentScene(Scenes.Bouncer)
+ kosmos.runTest {
+ setAuthMethod(AuthenticationMethodModel.None)
+ introduceLockedSim()
+ assertCurrentScene(Scenes.Bouncer)
}
@Test
fun goesToGone_whenSimUnlocked_whileDeviceUnlocked() =
- testScope.runTest {
- kosmos.fakeSceneDataSource.pause()
- kosmos.introduceLockedSim()
- kosmos.emulatePendingTransitionProgress(expectedVisible = true)
- kosmos.enterSimPin(
+ kosmos.runTest {
+ fakeSceneDataSource.pause()
+ introduceLockedSim()
+ emulatePendingTransitionProgress(expectedVisible = true)
+ enterSimPin(
authMethodAfterSimUnlock = AuthenticationMethodModel.None,
enableLockscreen = false,
)
- kosmos.assertCurrentScene(Scenes.Gone)
+ assertCurrentScene(Scenes.Gone)
}
@Test
fun showLockscreen_whenSimUnlocked_whileDeviceLocked() =
- testScope.runTest {
- kosmos.fakeSceneDataSource.pause()
- kosmos.introduceLockedSim()
- kosmos.emulatePendingTransitionProgress(expectedVisible = true)
- kosmos.enterSimPin(authMethodAfterSimUnlock = AuthenticationMethodModel.Pin)
- kosmos.assertCurrentScene(Scenes.Lockscreen)
+ kosmos.runTest {
+ fakeSceneDataSource.pause()
+ introduceLockedSim()
+ emulatePendingTransitionProgress(expectedVisible = true)
+ enterSimPin(authMethodAfterSimUnlock = AuthenticationMethodModel.Pin)
+ assertCurrentScene(Scenes.Lockscreen)
}
/**
@@ -457,10 +455,10 @@
*/
private fun Kosmos.emulatePendingTransitionProgress(expectedVisible: Boolean = true) {
assertWithMessage("The FakeSceneDataSource has to be paused for this to do anything.")
- .that(kosmos.fakeSceneDataSource.isPaused)
+ .that(fakeSceneDataSource.isPaused)
.isTrue()
- val to = kosmos.fakeSceneDataSource.pendingScene ?: return
+ val to = fakeSceneDataSource.pendingScene ?: return
val from = getCurrentSceneInUi()
if (to == from) {
@@ -489,7 +487,7 @@
// End the transition and report the change.
transitionState.value = ObservableTransitionState.Idle(to)
- kosmos.fakeSceneDataSource.unpause(force = true)
+ fakeSceneDataSource.unpause(force = true)
testScope.runCurrent()
assertWithMessage("Visibility mismatch after scene transition from $from to $to!")
@@ -523,7 +521,7 @@
private fun Kosmos.emulateUserDrivenTransition(to: SceneKey?) {
checkNotNull(to)
- kosmos.fakeSceneDataSource.pause()
+ fakeSceneDataSource.pause()
sceneInteractor.changeScene(to, "reason")
emulatePendingTransitionProgress(expectedVisible = to != Scenes.Gone)
@@ -634,7 +632,7 @@
}
/** Changes device wakefulness state from awake to asleep, going through intermediary states. */
- private suspend fun Kosmos.putDeviceToSleep() {
+ private fun Kosmos.putDeviceToSleep() {
val wakefulnessModel = powerInteractor.detailedWakefulness.value
assertWithMessage("Cannot put device to sleep as it's already asleep!")
.that(wakefulnessModel.isAwake())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 0ff2a4a..ea2de25 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -212,6 +212,7 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.stubbing.Answer;
@@ -515,7 +516,8 @@
when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
when(mView.findViewById(R.id.keyguard_status_view))
.thenReturn(mock(KeyguardStatusView.class));
- View rootView = mock(View.class);
+ ViewGroup rootView = mock(ViewGroup.class);
+ when(rootView.isVisibleToUser()).thenReturn(true);
when(mView.getRootView()).thenReturn(rootView);
when(rootView.findViewById(R.id.keyguard_status_view))
.thenReturn(mock(KeyguardStatusView.class));
@@ -652,12 +654,21 @@
((Runnable) invocation.getArgument(0)).run();
return null;
}).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
+ when(mNotificationShadeWindowController.getWindowRootView()).thenReturn(rootView);
doAnswer(invocation -> {
mLayoutChangeListener = invocation.getArgument(0);
return null;
}).when(mView).addOnLayoutChangeListener(any());
when(mView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ ViewTreeObserver.OnGlobalLayoutListener gll = invocation.getArgument(0);
+ gll.onGlobalLayout();
+ return null;
+ }
+ }).when(mViewTreeObserver).addOnGlobalLayoutListener(any());
when(mView.getParent()).thenReturn(mViewParent);
when(mQs.getHeader()).thenReturn(mQsHeader);
when(mDownMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_DOWN);
@@ -911,7 +922,7 @@
}
protected boolean onTouchEvent(MotionEvent ev) {
- return mTouchHandler.onTouch(mView, ev);
+ return mNotificationPanelViewController.handleExternalTouch(ev);
}
protected void setDozing(boolean dozing, boolean dozingAlwaysOn) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index ec75972..550fcf7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -364,6 +364,64 @@
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_SHADE_EXPANDS_ON_STATUS_BAR_LONG_PRESS)
+ public void onStatusBarLongPress_shadeExpands() {
+ long downTime = 42L;
+ // Start touch session with down event
+ onTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 1f, 1f, 0));
+ // Status bar triggers long press expand
+ mNotificationPanelViewController.onStatusBarLongPress(
+ MotionEvent.obtain(downTime, downTime + 27L, MotionEvent.ACTION_MOVE, 1f, 1f, 0));
+ assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
+ // Shade ignores the rest of the long press's touch session
+ assertThat(onTouchEvent(
+ MotionEvent.obtain(downTime, downTime + 42L, MotionEvent.ACTION_MOVE, 1f, 1f,
+ 0))).isFalse();
+
+ // Start new touch session
+ long downTime2 = downTime + 100L;
+ assertThat(onTouchEvent(
+ MotionEvent.obtain(downTime2, downTime2, MotionEvent.ACTION_DOWN, 1f, 1f,
+ 0))).isTrue();
+ // Shade no longer ignoring touches
+ assertThat(onTouchEvent(
+ MotionEvent.obtain(downTime2, downTime2 + 2L, MotionEvent.ACTION_MOVE, 1f, 1f,
+ 0))).isTrue();
+ }
+
+ @Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_SHADE_EXPANDS_ON_STATUS_BAR_LONG_PRESS)
+ public void onStatusBarLongPress_qsExpands() {
+ long downTime = 42L;
+ // Start with shade already expanded
+ mNotificationPanelViewController.setExpandedFraction(1F);
+
+ // Start touch session with down event
+ onTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 1f, 1f, 0));
+ // Status bar triggers long press expand
+ mNotificationPanelViewController.onStatusBarLongPress(
+ MotionEvent.obtain(downTime, downTime + 27L, MotionEvent.ACTION_MOVE, 1f, 1f, 0));
+ assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
+ // Shade expands to QS
+ verify(mQsController, atLeastOnce()).flingQs(0F, ShadeViewController.FLING_EXPAND);
+ // Shade ignores the rest of the long press's touch session
+ assertThat(onTouchEvent(
+ MotionEvent.obtain(downTime, downTime + 42L, MotionEvent.ACTION_MOVE, 1f, 1f,
+ 0))).isFalse();
+
+ // Start new touch session
+ long downTime2 = downTime + 100L;
+ assertThat(onTouchEvent(
+ MotionEvent.obtain(downTime2, downTime2, MotionEvent.ACTION_DOWN, 1f, 1f,
+ 0))).isTrue();
+ // Shade no longer ignoring touches
+ assertThat(onTouchEvent(
+ MotionEvent.obtain(downTime2, downTime2 + 2L, MotionEvent.ACTION_MOVE, 1f, 1f,
+ 0))).isTrue();
+
+ }
+
+ @Test
@DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
public void test_pulsing_onTouchEvent_noTracking() {
// GIVEN device is pulsing
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt
index 16da3d2..4795a12 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt
@@ -29,6 +29,10 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
import com.android.systemui.statusbar.FakeStatusBarStateController
+import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomLeft
+import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomRight
+import com.android.systemui.statusbar.events.PrivacyDotCorner.TopLeft
+import com.android.systemui.statusbar.events.PrivacyDotCorner.TopRight
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -71,15 +75,15 @@
private fun createController() =
PrivacyDotViewControllerImpl(
- executor,
- testScope.backgroundScope,
- statusBarStateController,
- configurationController,
- contentInsetsProvider,
- animationScheduler = mock<SystemStatusAnimationScheduler>(),
- shadeInteractor = null,
- )
- .also { it.setUiExecutor(executor) }
+ executor,
+ testScope.backgroundScope,
+ statusBarStateController,
+ configurationController,
+ contentInsetsProvider,
+ animationScheduler = mock<SystemStatusAnimationScheduler>(),
+ shadeInteractor = null,
+ uiExecutor = executor,
+ )
@Test
fun topMargin_topLeftView_basedOnSeascapeArea() {
@@ -215,7 +219,7 @@
val controller = createAndInitializeController()
- assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_RIGHT)
+ assertThat(controller.currentViewState.corner).isEqualTo(TopRight)
assertThat(controller.currentViewState.designatedCorner).isEqualTo(topRightView)
}
@@ -225,7 +229,7 @@
val controller = createAndInitializeController()
- assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_RIGHT)
+ assertThat(controller.currentViewState.corner).isEqualTo(BottomRight)
assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomRightView)
}
@@ -235,7 +239,7 @@
val controller = createAndInitializeController()
- assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_LEFT)
+ assertThat(controller.currentViewState.corner).isEqualTo(TopLeft)
assertThat(controller.currentViewState.designatedCorner).isEqualTo(topLeftView)
}
@@ -245,7 +249,7 @@
val controller = createAndInitializeController()
- assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_LEFT)
+ assertThat(controller.currentViewState.corner).isEqualTo(BottomLeft)
assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomLeftView)
}
@@ -256,7 +260,7 @@
enableRtl()
val controller = createAndInitializeController()
- assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_LEFT)
+ assertThat(controller.currentViewState.corner).isEqualTo(TopLeft)
assertThat(controller.currentViewState.designatedCorner).isEqualTo(topLeftView)
}
@@ -267,7 +271,7 @@
enableRtl()
val controller = createAndInitializeController()
- assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_RIGHT)
+ assertThat(controller.currentViewState.corner).isEqualTo(TopRight)
assertThat(controller.currentViewState.designatedCorner).isEqualTo(topRightView)
}
@@ -278,7 +282,7 @@
enableRtl()
val controller = createAndInitializeController()
- assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_LEFT)
+ assertThat(controller.currentViewState.corner).isEqualTo(BottomLeft)
assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomLeftView)
}
@@ -289,7 +293,7 @@
enableRtl()
val controller = createAndInitializeController()
- assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_RIGHT)
+ assertThat(controller.currentViewState.corner).isEqualTo(BottomRight)
assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomRightView)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/ConvenienceExtensionsKtTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/ConvenienceExtensionsKtTest.kt
new file mode 100644
index 0000000..2d57e2f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/ConvenienceExtensionsKtTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ConvenienceExtensionsKtTest : SysuiTestCase() {
+
+ @Test
+ fun containsExactly_notDuplicatedElements_allSame_returnsTrue() {
+ val list = listOf(1, 2, 3)
+
+ assertThat(list.containsExactly(2, 1, 3)).isTrue()
+ }
+
+ @Test
+ fun containsExactly_duplicatedElements_allSame_returnsTrue() {
+ val list = listOf(1, 1, 2, 3, 3)
+
+ assertThat(list.containsExactly(1, 1, 2, 3, 3)).isTrue()
+ }
+
+ @Test
+ fun containsExactly_duplicatedElements_sameButNotDuplicated_returnsFalse() {
+ val list = listOf(1, 1, 2, 3, 3)
+
+ assertThat(list.containsExactly(1, 2, 3)).isFalse()
+ }
+
+ @Test
+ fun containsExactly_duplicatedElements_sameButNotSameAmount_returnsFalse() {
+ val list = listOf(1, 1, 2, 3, 3)
+
+ assertThat(list.containsExactly(1, 2, 2, 3, 3)).isFalse()
+ }
+
+ @Test
+ fun eachCountMap_returnsExpectedCount() {
+ val list = listOf(1, 3, 1, 3, 3, 3, 2)
+
+ assertThat(list.eachCountMap()).isEqualTo(mapOf(1 to 2, 2 to 1, 3 to 4))
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/WakeLockTest.java
index 207c35d..90aecfb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/WakeLockTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/WakeLockTest.java
@@ -21,43 +21,23 @@
import android.os.Build;
import android.os.PowerManager;
-import android.platform.test.flag.junit.FlagsParameterization;
-import android.platform.test.flag.junit.SetFlagsRule;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.List;
-
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
@SmallTest
-@RunWith(ParameterizedAndroidJunit4.class)
+@RunWith(AndroidJUnit4.class)
public class WakeLockTest extends SysuiTestCase {
- @Parameters(name = "{0}")
- public static List<FlagsParameterization> getFlags() {
- return FlagsParameterization.allCombinationsOf(
- Flags.FLAG_DELAYED_WAKELOCK_RELEASE_ON_BACKGROUND_THREAD);
- }
-
- @Rule public final SetFlagsRule mSetFlagsRule;
-
- public WakeLockTest(FlagsParameterization flags) {
- mSetFlagsRule = new SetFlagsRule(SetFlagsRule.DefaultInitValueType.NULL_DEFAULT, flags);
- }
-
private static final String WHY = "test";
WakeLock mWakeLock;
PowerManager.WakeLock mInner;
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 9b5d5b6..46e45aa 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -92,7 +92,6 @@
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.concurrency.DelayableExecutor;
-import com.android.systemui.util.concurrency.ThreadFactory;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;
@@ -147,7 +146,6 @@
private CameraAvailabilityListener mCameraListener;
private final UserTracker mUserTracker;
private final PrivacyDotViewController mDotViewController;
- private final ThreadFactory mThreadFactory;
private final DecorProviderFactory mDotFactory;
private final FaceScanningProviderFactory mFaceScanningFactory;
private final CameraProtectionLoader mCameraProtectionLoader;
@@ -172,7 +170,6 @@
private ViewCaptureAwareWindowManager mWindowManager;
private int mRotation;
private UserSettingObserver mColorInversionSetting;
- @Nullable
private DelayableExecutor mExecutor;
private Handler mHandler;
boolean mPendingConfigChange;
@@ -327,27 +324,28 @@
}
@Inject
- public ScreenDecorations(Context context,
+ public ScreenDecorations(
+ Context context,
SecureSettings secureSettings,
CommandRegistry commandRegistry,
UserTracker userTracker,
DisplayTracker displayTracker,
PrivacyDotViewController dotViewController,
- ThreadFactory threadFactory,
PrivacyDotDecorProviderFactory dotFactory,
FaceScanningProviderFactory faceScanningFactory,
ScreenDecorationsLogger logger,
FacePropertyRepository facePropertyRepository,
JavaAdapter javaAdapter,
CameraProtectionLoader cameraProtectionLoader,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+ @ScreenDecorationsThread Handler handler,
+ @ScreenDecorationsThread DelayableExecutor executor) {
mContext = context;
mSecureSettings = secureSettings;
mCommandRegistry = commandRegistry;
mUserTracker = userTracker;
mDisplayTracker = displayTracker;
mDotViewController = dotViewController;
- mThreadFactory = threadFactory;
mDotFactory = dotFactory;
mFaceScanningFactory = faceScanningFactory;
mCameraProtectionLoader = cameraProtectionLoader;
@@ -356,6 +354,8 @@
mFacePropertyRepository = facePropertyRepository;
mJavaAdapter = javaAdapter;
mWindowManager = viewCaptureAwareWindowManager;
+ mHandler = handler;
+ mExecutor = executor;
}
private final ScreenDecorCommand.Callback mScreenDecorCommandCallback = (cmd, pw) -> {
@@ -403,10 +403,7 @@
Log.i(TAG, "ScreenDecorations is disabled");
return;
}
- mHandler = mThreadFactory.buildHandlerOnNewThread("ScreenDecorations");
- mExecutor = mThreadFactory.buildDelayableExecutorOnHandler(mHandler);
mExecutor.execute(this::startOnScreenDecorationsThread);
- mDotViewController.setUiExecutor(mExecutor);
mCommandRegistry.registerCommand(ScreenDecorCommand.SCREEN_DECOR_CMD_NAME,
() -> new ScreenDecorCommand(mScreenDecorCommandCallback));
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorationsModule.kt b/packages/SystemUI/src/com/android/systemui/ScreenDecorationsModule.kt
index 6fc50fb..6786a71 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorationsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorationsModule.kt
@@ -17,16 +17,23 @@
package com.android.systemui
import android.content.Context
+import android.os.Handler
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.decor.FaceScanningProviderFactory
import com.android.systemui.decor.FaceScanningProviderFactoryImpl
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.concurrency.ThreadFactory
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import dagger.multibindings.IntoSet
+import java.util.concurrent.Executor
+import javax.inject.Qualifier
+
+@Qualifier annotation class ScreenDecorationsThread
@Module
interface ScreenDecorationsModule {
@@ -41,6 +48,12 @@
@IntoSet
fun bindScreenDecorationsConfigListener(impl: ScreenDecorations): ConfigurationListener
+ @Binds
+ @ScreenDecorationsThread
+ fun screenDecorationsExecutor(
+ @ScreenDecorationsThread delayableExecutor: DelayableExecutor
+ ): Executor
+
companion object {
@Provides
@SysUISingleton
@@ -50,5 +63,22 @@
): FaceScanningProviderFactory {
return creator.create(context)
}
+
+ @Provides
+ @SysUISingleton
+ @ScreenDecorationsThread
+ fun screenDecorationsHandler(threadFactory: ThreadFactory): Handler {
+ return threadFactory.buildHandlerOnNewThread("ScreenDecorations")
+ }
+
+ @Provides
+ @SysUISingleton
+ @ScreenDecorationsThread
+ fun screenDecorationsDelayableExecutor(
+ @ScreenDecorationsThread handler: Handler,
+ threadFactory: ThreadFactory,
+ ): DelayableExecutor {
+ return threadFactory.buildDelayableExecutorOnHandler(handler)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
index 7253621..80eb9ee 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
@@ -19,7 +19,9 @@
import android.annotation.SuppressLint
import android.content.Context
import android.view.Display
+import android.view.LayoutInflater
import android.view.WindowManager
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -30,9 +32,7 @@
import com.google.common.collect.Table
import java.io.PrintWriter
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Provides per display instances of [DisplayWindowProperties]. */
interface DisplayWindowPropertiesRepository {
@@ -55,6 +55,7 @@
@Background private val backgroundApplicationScope: CoroutineScope,
private val globalContext: Context,
private val globalWindowManager: WindowManager,
+ private val globalLayoutInflater: LayoutInflater,
private val displayRepository: DisplayRepository,
) : DisplayWindowPropertiesRepository, CoreStartable {
@@ -93,12 +94,14 @@
windowType = windowType,
context = globalContext,
windowManager = globalWindowManager,
+ layoutInflater = globalLayoutInflater,
)
} else {
val context = createWindowContext(display, windowType)
@SuppressLint("NonInjectedService") // Need to manually get the service
val windowManager = context.getSystemService(WindowManager::class.java) as WindowManager
- DisplayWindowProperties(displayId, windowType, context, windowManager)
+ val layoutInflater = LayoutInflater.from(context)
+ DisplayWindowProperties(displayId, windowType, context, windowManager, layoutInflater)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/shared/model/DisplayWindowProperties.kt b/packages/SystemUI/src/com/android/systemui/display/shared/model/DisplayWindowProperties.kt
index 6acc296..3f1c088 100644
--- a/packages/SystemUI/src/com/android/systemui/display/shared/model/DisplayWindowProperties.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/shared/model/DisplayWindowProperties.kt
@@ -17,6 +17,7 @@
package com.android.systemui.display.shared.model
import android.content.Context
+import android.view.LayoutInflater
import android.view.WindowManager
/** Represents a display specific group of window related properties. */
@@ -40,4 +41,7 @@
* associated with this instance.
*/
val windowManager: WindowManager,
+
+ /** The [LayoutInflater] to be used with the associated [Context]. */
+ val layoutInflater: LayoutInflater,
)
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
index 7905950..ed7d182 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
@@ -38,7 +38,7 @@
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.transform
-/** A view-model to trigger haptics feedback on Quick Settings tiles */
+/** A view-model to trigger haptic feedback on Quick Settings tiles */
@OptIn(ExperimentalCoroutinesApi::class)
class TileHapticsViewModel
@AssistedInject
@@ -149,7 +149,7 @@
onActivityLaunchTransitionEnd = ::onActivityLaunchTransitionEnd,
)
- /** Models the state of toggle haptics to play */
+ /** Models the state of haptics to play */
enum class TileHapticsState {
TOGGLE_ON,
TOGGLE_OFF,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LongPressGestureDetector.kt b/packages/SystemUI/src/com/android/systemui/shade/LongPressGestureDetector.kt
new file mode 100644
index 0000000..6fb3ca5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/LongPressGestureDetector.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+import android.content.Context
+import android.view.GestureDetector
+import android.view.GestureDetector.SimpleOnGestureListener
+import android.view.MotionEvent
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** Accepts touch events, detects long press, and calls ShadeViewController#onStatusBarLongPress. */
+@SysUISingleton
+class LongPressGestureDetector
+@Inject
+constructor(context: Context, val shadeViewController: ShadeViewController) {
+ val gestureDetector =
+ GestureDetector(
+ context,
+ object : SimpleOnGestureListener() {
+ override fun onLongPress(event: MotionEvent) {
+ shadeViewController.onStatusBarLongPress(event)
+ }
+ },
+ )
+
+ /** Accepts touch events to detect long presses. */
+ fun handleTouch(ev: MotionEvent) {
+ gestureDetector.onTouchEvent(ev)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 1b281b1..0e82bf8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -365,6 +365,7 @@
private final TouchHandler mTouchHandler = new TouchHandler();
private long mDownTime;
+ private long mStatusBarLongPressDowntime;
private boolean mTouchSlopExceededBeforeDown;
private float mOverExpansion;
private CentralSurfaces mCentralSurfaces;
@@ -3098,6 +3099,25 @@
}
}
+ /** @deprecated Temporary a11y solution until dual shade launch b/371224114 */
+ @Override
+ @Deprecated
+ public void onStatusBarLongPress(MotionEvent event) {
+ mShadeLog.d("Status Bar was long pressed.");
+ ShadeExpandsOnStatusBarLongPress.assertInNewMode();
+ mStatusBarLongPressDowntime = event.getDownTime();
+ if (isTracking()) {
+ onTrackingStopped(true);
+ }
+ if (isExpanded() && !mQsController.getExpanded()) {
+ mShadeLog.d("Status Bar was long pressed. Expanding to QS.");
+ expandToQs();
+ } else {
+ mShadeLog.d("Status Bar was long pressed. Expanding to Notifications.");
+ expandToNotifications();
+ }
+ }
+
@Override
public int getBarState() {
return mBarState;
@@ -3761,6 +3781,7 @@
private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
mShadeLog.logEndMotionEvent("endMotionEvent called", forceCancel, false);
mTrackingPointer = -1;
+ mStatusBarLongPressDowntime = 0L;
mAmbientState.setSwipingUp(false);
if ((isTracking() && mTouchSlopExceeded) || Math.abs(x - mInitialExpandX) > mTouchSlop
|| Math.abs(y - mInitialExpandY) > mTouchSlop
@@ -5077,6 +5098,13 @@
}
return true;
}
+ // This touch session has already resulted in shade expansion. Ignore everything else.
+ if (ShadeExpandsOnStatusBarLongPress.isEnabled()
+ && event.getActionMasked() != MotionEvent.ACTION_DOWN
+ && event.getDownTime() == mStatusBarLongPressDowntime) {
+ mShadeLog.d("Touch has same down time as Status Bar long press. Ignoring.");
+ return false;
+ }
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
handled = true;
@@ -5157,6 +5185,7 @@
mUpdateFlingOnLayout = false;
mMotionAborted = false;
mDownTime = mSystemClock.uptimeMillis();
+ mStatusBarLongPressDowntime = 0L;
mTouchAboveFalsingThreshold = false;
mCollapsedAndHeadsUpOnDown =
isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index daea977..b085aec 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -98,6 +98,10 @@
/** Returns the ShadeHeadsUpTracker. */
val shadeHeadsUpTracker: ShadeHeadsUpTracker
+ @Deprecated("Temporary a11y solution until dual shade launch b/371224114")
+ /** Notifies the shade that a status bar detected a long press gesture. */
+ fun onStatusBarLongPress(event: MotionEvent)
+
/** Returns the ShadeFoldAnimator. */
@Deprecated("This interface is deprecated in Scene Container")
val shadeFoldAnimator: ShadeFoldAnimator
@@ -179,9 +183,7 @@
/** Returns the expanded height of the panel view. */
@Deprecated("deprecated by SceneContainerFlag.isEnabled") val panelViewExpandedHeight: Float
- /**
- * Returns true if heads up should be visible.
- */
+ /** Returns true if heads up should be visible. */
@Deprecated("deprecated by SceneContainerFlag.isEnabled.") fun shouldHeadsUpBeVisible(): Boolean
/** Return the fraction of the shade that's expanded, when in lockscreen. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index 9322d31..53617d0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -35,60 +35,95 @@
ShadeLockscreenInteractor,
PanelExpansionInteractor {
@Deprecated("Use ShadeInteractor instead") override fun expandToNotifications() {}
+
@Deprecated("Use ShadeInteractor instead") override val isExpanded: Boolean = false
override val isPanelExpanded: Boolean = false
+
override fun animateCollapseQs(fullyCollapse: Boolean) {}
+
override fun canBeCollapsed(): Boolean = false
+
@Deprecated("Use ShadeAnimationInteractor instead") override val isCollapsing: Boolean = false
@Deprecated("Use !ShadeInteractor.isAnyExpanded instead")
override val isFullyCollapsed: Boolean = false
override val isTracking: Boolean = false
override val isViewEnabled: Boolean = false
+
override fun shouldHideStatusBarIconsWhenExpanded() = false
+
@Deprecated("Not supported by scenes") override fun blockExpansionForCurrentTouch() {}
+
override fun startExpandLatencyTracking() {}
+
override fun startBouncerPreHideAnimation() {}
+
override fun dozeTimeTick() {}
+
override fun resetViews(animate: Boolean) {}
+
override val barState: Int = 0
+
@Deprecated("Only supported by very old devices that will not adopt scenes.")
override fun closeUserSwitcherIfOpen(): Boolean {
return false
}
+
override fun onBackPressed() {}
+
@Deprecated("According to b/318376223, shade predictive back is not be supported.")
override fun onBackProgressed(progressFraction: Float) {}
+
override fun setAlpha(alpha: Int, animate: Boolean) {}
+
override fun setAlphaChangeAnimationEndAction(r: Runnable) {}
+
@Deprecated("Not supported by scenes") override fun setPulsing(pulsing: Boolean) {}
+
override fun setQsScrimEnabled(qsScrimEnabled: Boolean) {}
+
override fun setAmbientIndicationTop(ambientIndicationTop: Int, ambientTextVisible: Boolean) {}
+
override fun updateSystemUiStateFlags() {}
+
override fun updateTouchableRegion() {}
+
override fun transitionToExpandedShade(delay: Long) {}
@Deprecated("Not supported by scenes") override fun resetViewGroupFade() {}
+
@Deprecated("Not supported by scenes")
override fun setKeyguardTransitionProgress(keyguardAlpha: Float, keyguardTranslationY: Int) {}
+
@Deprecated("Not supported by scenes") override fun setOverStretchAmount(amount: Float) {}
+
@Deprecated("TODO(b/325072511) delete this")
override fun setKeyguardStatusBarAlpha(alpha: Float) {}
+
override fun showAodUi() {}
+
@Deprecated(
"depends on the state you check, use {@link #isShadeFullyExpanded()},\n" +
"{@link #isOnAod()}, {@link #isOnKeyguard()} instead."
)
override val isFullyExpanded = false
+
override fun handleExternalTouch(event: MotionEvent): Boolean {
return false
}
+
override fun handleExternalInterceptTouch(event: MotionEvent): Boolean {
return false
}
override fun startInputFocusTransfer() {}
+
override fun cancelInputFocusTransfer() {}
+
override fun finishInputFocusTransfer(velocity: Float) {}
+
+ @Deprecated("Temporary a11y solution until dual shade launch b/371224114")
+ override fun onStatusBarLongPress(event: MotionEvent) {}
+
override val shadeHeadsUpTracker = ShadeHeadsUpTrackerEmptyImpl()
override val shadeFoldAnimator = ShadeFoldAnimatorEmptyImpl()
@Deprecated("Use SceneInteractor.currentScene instead.")
@@ -98,20 +133,26 @@
class ShadeHeadsUpTrackerEmptyImpl : ShadeHeadsUpTracker {
override fun addTrackingHeadsUpListener(listener: Consumer<ExpandableNotificationRow>) {}
+
override fun removeTrackingHeadsUpListener(listener: Consumer<ExpandableNotificationRow>) {}
+
override fun setHeadsUpAppearanceController(
headsUpAppearanceController: HeadsUpAppearanceController?
) {}
+
override val trackedHeadsUpNotification: ExpandableNotificationRow? = null
}
class ShadeFoldAnimatorEmptyImpl : ShadeFoldAnimator {
override fun prepareFoldToAodAnimation() {}
+
override fun startFoldToAodAnimation(
startAction: Runnable,
endAction: Runnable,
cancelAction: Runnable,
) {}
+
override fun cancelFoldToAodAnimation() {}
+
override val view: ViewGroup? = null
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
index 8a850b0..c416bf7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.data
import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositoryModule
-import com.android.systemui.statusbar.data.repository.PrivacyDotViewControllerStoreModule
import com.android.systemui.statusbar.data.repository.RemoteInputRepositoryModule
import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerModule
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStoreModule
@@ -28,7 +27,6 @@
includes =
[
KeyguardStatusBarRepositoryModule::class,
- PrivacyDotViewControllerStoreModule::class,
RemoteInputRepositoryModule::class,
StatusBarConfigurationControllerModule::class,
StatusBarContentInsetsProviderStoreModule::class,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotCorner.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotCorner.kt
new file mode 100644
index 0000000..8a6f355
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotCorner.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.events
+
+import android.view.Gravity
+import android.view.Surface
+
+/** Represents a corner on the display for the privacy dot. */
+enum class PrivacyDotCorner(
+ val index: Int,
+ val gravity: Int,
+ val innerGravity: Int,
+ val title: String,
+) {
+ TopLeft(
+ index = 0,
+ gravity = Gravity.TOP or Gravity.LEFT,
+ innerGravity = Gravity.CENTER_VERTICAL or Gravity.RIGHT,
+ title = "TopLeft",
+ ),
+ TopRight(
+ index = 1,
+ gravity = Gravity.TOP or Gravity.RIGHT,
+ innerGravity = Gravity.CENTER_VERTICAL or Gravity.LEFT,
+ title = "TopRight",
+ ),
+ BottomRight(
+ index = 2,
+ gravity = Gravity.BOTTOM or Gravity.RIGHT,
+ innerGravity = Gravity.CENTER_VERTICAL or Gravity.RIGHT,
+ title = "BottomRight",
+ ),
+ BottomLeft(
+ index = 3,
+ gravity = Gravity.BOTTOM or Gravity.LEFT,
+ innerGravity = Gravity.CENTER_VERTICAL or Gravity.LEFT,
+ title = "BottomLeft",
+ ),
+}
+
+fun PrivacyDotCorner.rotatedCorner(@Surface.Rotation rotation: Int): PrivacyDotCorner {
+ var modded = index - rotation
+ if (modded < 0) {
+ modded += 4
+ }
+ return PrivacyDotCorner.entries[modded]
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 914cc50..f7bc23c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -20,12 +20,13 @@
import android.graphics.Point
import android.graphics.Rect
import android.util.Log
-import android.view.Gravity
import android.view.View
import android.widget.FrameLayout
import androidx.core.animation.Animator
import com.android.app.animation.Interpolators
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.annotations.GuardedBy
+import com.android.systemui.ScreenDecorationsThread
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
@@ -36,6 +37,10 @@
import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
+import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomLeft
+import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomRight
+import com.android.systemui.statusbar.events.PrivacyDotCorner.TopLeft
+import com.android.systemui.statusbar.events.PrivacyDotCorner.TopRight
import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -53,7 +58,6 @@
import dagger.assisted.AssistedInject
import java.util.concurrent.Executor
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* Understands how to keep the persistent privacy dot in the corner of the screen in
@@ -81,10 +85,6 @@
var showingListener: ShowingListener?
- fun setUiExecutor(e: DelayableExecutor)
-
- fun getUiExecutor(): DelayableExecutor?
-
@UiThread fun setNewRotation(rot: Int)
@UiThread fun hideDotView(dot: View, animate: Boolean)
@@ -117,6 +117,7 @@
@Assisted private val contentInsetsProvider: StatusBarContentInsetsProvider,
private val animationScheduler: SystemStatusAnimationScheduler,
shadeInteractor: ShadeInteractor?,
+ @ScreenDecorationsThread val uiExecutor: DelayableExecutor,
) : PrivacyDotViewController {
private lateinit var tl: View
private lateinit var tr: View
@@ -136,9 +137,6 @@
private val lock = Object()
private var cancelRunnable: Runnable? = null
- // Privacy dots are created in ScreenDecoration's UiThread, which is not the main thread
- private var uiExecutor: DelayableExecutor? = null
-
private val views: Sequence<View>
get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl)
@@ -155,7 +153,7 @@
private val configurationListener =
object : ConfigurationController.ConfigurationListener {
override fun onLayoutDirectionChanged(isRtl: Boolean) {
- uiExecutor?.execute {
+ uiExecutor.execute {
// If rtl changed, hide all dots until the next state resolves
setCornerVisibilities(View.INVISIBLE)
@@ -198,14 +196,6 @@
stateController.removeCallback(statusBarStateListener)
}
- override fun setUiExecutor(e: DelayableExecutor) {
- uiExecutor = e
- }
-
- override fun getUiExecutor(): DelayableExecutor? {
- return uiExecutor
- }
-
@UiThread
override fun setNewRotation(rot: Int) {
dlog("updateRotation: $rot")
@@ -222,8 +212,8 @@
// If we rotated, hide all dotes until the next state resolves
setCornerVisibilities(View.INVISIBLE)
- val newCorner = selectDesignatedCorner(rot, isRtl)
- val index = newCorner.cornerIndex()
+ val newCornerView = selectDesignatedCorner(rot, isRtl)
+ val corner = newCornerView.corner()
val paddingTop = contentInsetsProvider.getStatusBarPaddingTop(rot)
synchronized(lock) {
@@ -231,8 +221,8 @@
nextViewState.copy(
rotation = rot,
paddingTop = paddingTop,
- designatedCorner = newCorner,
- cornerIndex = index,
+ designatedCorner = newCornerView,
+ corner = corner,
)
}
}
@@ -284,24 +274,15 @@
views.forEach { corner ->
corner.setPadding(0, paddingTop, 0, 0)
- val rotatedCorner = rotatedCorner(cornerForView(corner), rotation)
+ val rotatedCorner = cornerForView(corner).rotatedCorner(rotation)
(corner.layoutParams as FrameLayout.LayoutParams).apply {
- gravity = rotatedCorner.toGravity()
+ gravity = rotatedCorner.gravity
}
// Set the dot's view gravity to hug the status bar
(corner.requireViewById<View>(R.id.privacy_dot).layoutParams
as FrameLayout.LayoutParams)
- .gravity = rotatedCorner.innerGravity()
- }
- }
-
- @UiThread
- private fun updateCornerSizes(l: Int, r: Int, rotation: Int) {
- views.forEach { corner ->
- val rotatedCorner = rotatedCorner(cornerForView(corner), rotation)
- val w = widthForCorner(rotatedCorner, l, r)
- (corner.layoutParams as FrameLayout.LayoutParams).width = w
+ .gravity = rotatedCorner.innerGravity
}
}
@@ -419,25 +400,16 @@
}
}
- private fun cornerForView(v: View): Int {
+ private fun cornerForView(v: View): PrivacyDotCorner {
return when (v) {
- tl -> TOP_LEFT
- tr -> TOP_RIGHT
- bl -> BOTTOM_LEFT
- br -> BOTTOM_RIGHT
+ tl -> TopLeft
+ tr -> TopRight
+ bl -> BottomLeft
+ br -> BottomRight
else -> throw IllegalArgumentException("not a corner view")
}
}
- private fun rotatedCorner(corner: Int, rotation: Int): Int {
- var modded = corner - rotation
- if (modded < 0) {
- modded += 4
- }
-
- return modded
- }
-
@Rotation
private fun activeRotationForCorner(corner: View, rtl: Boolean): Int {
// Each corner will only be visible in a single rotation, based on rtl
@@ -449,16 +421,6 @@
}
}
- private fun widthForCorner(corner: Int, left: Int, right: Int): Int {
- return when (corner) {
- TOP_LEFT,
- BOTTOM_LEFT -> left
- TOP_RIGHT,
- BOTTOM_RIGHT -> right
- else -> throw IllegalArgumentException("Unknown corner")
- }
- }
-
override fun initialize(topLeft: View, topRight: View, bottomLeft: View, bottomRight: View) {
if (
this::tl.isInitialized &&
@@ -478,9 +440,9 @@
val rtl = configurationController.isLayoutRtl
val currentRotation = RotationUtils.getExactRotation(tl.context)
- val dc = selectDesignatedCorner(currentRotation, rtl)
+ val designatedCornerView = selectDesignatedCorner(currentRotation, rtl)
- val index = dc.cornerIndex()
+ val corner = designatedCornerView.corner()
mainExecutor.execute { animationScheduler.addCallback(systemStatusAnimationCallback) }
@@ -494,8 +456,8 @@
nextViewState =
nextViewState.copy(
viewInitialized = true,
- designatedCorner = dc,
- cornerIndex = index,
+ designatedCorner = designatedCornerView,
+ corner = corner,
seascapeRect = left,
portraitRect = top,
landscapeRect = right,
@@ -524,7 +486,7 @@
dlog("scheduleUpdate: ")
cancelRunnable?.run()
- cancelRunnable = uiExecutor?.executeDelayed({ processNextViewState() }, 100)
+ cancelRunnable = uiExecutor.executeDelayed({ processNextViewState() }, 100)
}
@UiThread
@@ -613,11 +575,11 @@
}
}
- private fun View?.cornerIndex(): Int {
+ private fun View?.corner(): PrivacyDotCorner? {
if (this != null) {
return cornerForView(this)
}
- return -1
+ return null
}
// Returns [left, top, right, bottom] aka [seascape, none, landscape, upside-down]
@@ -666,35 +628,11 @@
}
}
-const val TOP_LEFT = 0
-const val TOP_RIGHT = 1
-const val BOTTOM_RIGHT = 2
-const val BOTTOM_LEFT = 3
private const val DURATION = 160L
private const val TAG = "PrivacyDotViewController"
private const val DEBUG = false
private const val DEBUG_VERBOSE = false
-private fun Int.toGravity(): Int {
- return when (this) {
- TOP_LEFT -> Gravity.TOP or Gravity.LEFT
- TOP_RIGHT -> Gravity.TOP or Gravity.RIGHT
- BOTTOM_LEFT -> Gravity.BOTTOM or Gravity.LEFT
- BOTTOM_RIGHT -> Gravity.BOTTOM or Gravity.RIGHT
- else -> throw IllegalArgumentException("Not a corner")
- }
-}
-
-private fun Int.innerGravity(): Int {
- return when (this) {
- TOP_LEFT -> Gravity.CENTER_VERTICAL or Gravity.RIGHT
- TOP_RIGHT -> Gravity.CENTER_VERTICAL or Gravity.LEFT
- BOTTOM_LEFT -> Gravity.CENTER_VERTICAL or Gravity.RIGHT
- BOTTOM_RIGHT -> Gravity.CENTER_VERTICAL or Gravity.LEFT
- else -> throw IllegalArgumentException("Not a corner")
- }
-}
-
data class ViewState(
val viewInitialized: Boolean = false,
val systemPrivacyEventIsActive: Boolean = false,
@@ -707,7 +645,7 @@
val layoutRtl: Boolean = false,
val rotation: Int = 0,
val paddingTop: Int = 0,
- val cornerIndex: Int = -1,
+ val corner: PrivacyDotCorner? = null,
val designatedCorner: View? = null,
val contentDescription: String? = null,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index e7d9717..91c43dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -39,6 +39,8 @@
import com.android.systemui.Flags;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.res.R;
+import com.android.systemui.shade.LongPressGestureDetector;
+import com.android.systemui.shade.ShadeExpandsOnStatusBarLongPress;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder;
@@ -67,6 +69,7 @@
private InsetsFetcher mInsetsFetcher;
private int mDensity;
private float mFontScale;
+ private LongPressGestureDetector mLongPressGestureDetector;
/**
* Draw this many pixels into the left/right side of the cutout to optimally use the space
@@ -78,6 +81,12 @@
mStatusBarWindowControllerStore = Dependency.get(StatusBarWindowControllerStore.class);
}
+ void setLongPressGestureDetector(LongPressGestureDetector longPressGestureDetector) {
+ if (ShadeExpandsOnStatusBarLongPress.isEnabled()) {
+ mLongPressGestureDetector = longPressGestureDetector;
+ }
+ }
+
void setTouchEventHandler(Gefingerpoken handler) {
mTouchEventHandler = handler;
}
@@ -198,6 +207,9 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
+ if (ShadeExpandsOnStatusBarLongPress.isEnabled() && mLongPressGestureDetector != null) {
+ mLongPressGestureDetector.handleTouch(event);
+ }
if (mTouchEventHandler == null) {
Log.w(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 746d6a7..c24f432 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -33,7 +33,9 @@
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.ui.view.WindowRootView
+import com.android.systemui.shade.LongPressGestureDetector
import com.android.systemui.shade.ShadeController
+import com.android.systemui.shade.ShadeExpandsOnStatusBarLongPress
import com.android.systemui.shade.ShadeLogger
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
@@ -66,6 +68,7 @@
private val shadeController: ShadeController,
private val shadeViewController: ShadeViewController,
private val panelExpansionInteractor: PanelExpansionInteractor,
+ private val longPressGestureDetector: Provider<LongPressGestureDetector>,
private val windowRootView: Provider<WindowRootView>,
private val shadeLogger: ShadeLogger,
private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
@@ -114,6 +117,10 @@
addDarkReceivers()
addCursorSupportToIconContainers()
+ if (ShadeExpandsOnStatusBarLongPress.isEnabled) {
+ mView.setLongPressGestureDetector(longPressGestureDetector.get())
+ }
+
progressProvider?.setReadyToHandleTransition(true)
configurationController.addCallback(configurationListener)
@@ -328,6 +335,7 @@
private val shadeController: ShadeController,
private val shadeViewController: ShadeViewController,
private val panelExpansionInteractor: PanelExpansionInteractor,
+ private val longPressGestureDetector: Provider<LongPressGestureDetector>,
private val windowRootView: Provider<WindowRootView>,
private val shadeLogger: ShadeLogger,
private val viewUtil: ViewUtil,
@@ -352,6 +360,7 @@
shadeController,
shadeViewController,
panelExpansionInteractor,
+ longPressGestureDetector,
windowRootView,
shadeLogger,
statusBarMoveFromCenterAnimationController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
index 99f25bd..e4a75be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.core.StatusBarInitializerStore
import com.android.systemui.statusbar.core.StatusBarOrchestrator
import com.android.systemui.statusbar.core.StatusBarSimpleFragment
+import com.android.systemui.statusbar.data.repository.PrivacyDotViewControllerStoreModule
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.events.PrivacyDotViewControllerModule
import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks
@@ -46,7 +47,9 @@
import kotlinx.coroutines.CoroutineScope
/** Similar in purpose to [StatusBarModule], but scoped only to phones */
-@Module(includes = [PrivacyDotViewControllerModule::class])
+@Module(
+ includes = [PrivacyDotViewControllerModule::class, PrivacyDotViewControllerStoreModule::class]
+)
interface StatusBarPhoneModule {
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
index f2132248..70fd5ab 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
@@ -25,9 +25,7 @@
/** [Sequence] that yields all of the direct children of this [ViewGroup] */
val ViewGroup.children
- get() = sequence {
- for (i in 0 until childCount) yield(getChildAt(i))
- }
+ get() = sequence { for (i in 0 until childCount) yield(getChildAt(i)) }
/** Inclusive version of [Iterable.takeWhile] */
fun <T> Sequence<T>.takeUntil(pred: (T) -> Boolean): Sequence<T> = sequence {
@@ -62,3 +60,25 @@
fun <T> Lazy<T>.toKotlinLazy(): kotlin.Lazy<T> {
return lazy { this.get() }
}
+
+/**
+ * Returns whether this [Collection] contains exactly all [elements].
+ *
+ * Order of elements is not taken into account, but multiplicity is. For example, an element
+ * duplicated exactly 3 times in the parameter asserts that the element must likewise be duplicated
+ * exactly 3 times in this [Collection].
+ */
+fun <T> Collection<T>.containsExactly(vararg elements: T): Boolean {
+ return eachCountMap() == elements.asList().eachCountMap()
+}
+
+/**
+ * Returns a map where keys are the distinct elements of the collection and values are their
+ * corresponding counts.
+ *
+ * This is a convenient extension function for any [Collection] that allows you to easily count the
+ * occurrences of each element.
+ */
+fun <T> Collection<T>.eachCountMap(): Map<T, Int> {
+ return groupingBy { it }.eachCount()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
index d2ed71c..55cdfb2 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
@@ -19,11 +19,8 @@
import android.content.Context;
import android.os.Handler;
-import com.android.systemui.Flags;
import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
-import dagger.Lazy;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
@@ -40,13 +37,11 @@
private final WakeLock mInner;
@AssistedInject
- public DelayedWakeLock(@Background Lazy<Handler> bgHandler,
- @Main Lazy<Handler> mainHandler,
+ public DelayedWakeLock(@Background Handler bgHandler,
Context context, WakeLockLogger logger,
@Assisted String tag) {
mInner = WakeLock.createPartial(context, logger, tag);
- mHandler = Flags.delayedWakelockReleaseOnBackgroundThread() ? bgHandler.get()
- : mainHandler.get();
+ mHandler = bgHandler;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
index f763ee4..f00e3d1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
@@ -18,14 +18,9 @@
import android.content.Context;
import android.os.PowerManager;
-import android.util.Log;
import androidx.annotation.VisibleForTesting;
-import com.android.systemui.Flags;
-
-import java.util.HashMap;
-
import javax.inject.Inject;
/** WakeLock wrapper for testability */
@@ -114,59 +109,7 @@
@VisibleForTesting
static WakeLock wrap(
final PowerManager.WakeLock inner, WakeLockLogger logger, long maxTimeout) {
- if (Flags.delayedWakelockReleaseOnBackgroundThread()) {
- return new ClientTrackingWakeLock(inner, logger, maxTimeout);
- }
-
- // Non-thread safe implementation, remove when flag above is removed.
- return new WakeLock() {
- private final HashMap<String, Integer> mActiveClients = new HashMap<>();
-
- /** @see PowerManager.WakeLock#acquire() */
- public void acquire(String why) {
- mActiveClients.putIfAbsent(why, 0);
- int count = mActiveClients.get(why) + 1;
- mActiveClients.put(why, count);
- if (logger != null) {
- logger.logAcquire(inner, why, count);
- }
- if (maxTimeout == Builder.NO_TIMEOUT) {
- inner.acquire();
- } else {
- inner.acquire(maxTimeout);
- }
- }
-
- /** @see PowerManager.WakeLock#release() */
- public void release(String why) {
- Integer count = mActiveClients.get(why);
- if (count == null) {
- Log.wtf(TAG, "Releasing WakeLock with invalid reason: " + why,
- new Throwable());
- return;
- }
- count--;
- if (count == 0) {
- mActiveClients.remove(why);
- } else {
- mActiveClients.put(why, count);
- }
- if (logger != null) {
- logger.logRelease(inner, why, count);
- }
- inner.release();
- }
-
- /** @see PowerManager.WakeLock#wrap(Runnable) */
- public Runnable wrap(Runnable runnable) {
- return wrapImpl(this, runnable);
- }
-
- @Override
- public String toString() {
- return "active clients= " + mActiveClients;
- }
- };
+ return new ClientTrackingWakeLock(inner, logger, maxTimeout);
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 344d065..0769ada 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -104,6 +104,7 @@
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
import com.android.systemui.util.kotlin.JavaAdapter;
@@ -186,16 +187,17 @@
private List<DecorProvider> mMockCutoutList;
private final CameraProtectionLoader mCameraProtectionLoader =
new CameraProtectionLoaderImpl(mContext);
+ private Handler mMainHandler;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- Handler mainHandler = new Handler(TestableLooper.get(this).getLooper());
+ mMainHandler = new Handler(TestableLooper.get(this).getLooper());
mSecureSettings = new FakeSettings();
mExecutor = new FakeExecutor(new FakeSystemClock());
mThreadFactory = new FakeThreadFactory(mExecutor);
- mThreadFactory.setHandler(mainHandler);
+ mThreadFactory.setHandler(mMainHandler);
mWindowManager = mock(WindowManager.class);
WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
@@ -214,26 +216,26 @@
when(mMockTypedArray.length()).thenReturn(0);
mPrivacyDotTopLeftDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
R.id.privacy_dot_top_left_container,
- DisplayCutout.BOUNDS_POSITION_TOP,
- DisplayCutout.BOUNDS_POSITION_LEFT,
+ BOUNDS_POSITION_TOP,
+ BOUNDS_POSITION_LEFT,
R.layout.privacy_dot_top_left));
mPrivacyDotTopRightDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
R.id.privacy_dot_top_right_container,
- DisplayCutout.BOUNDS_POSITION_TOP,
- DisplayCutout.BOUNDS_POSITION_RIGHT,
+ BOUNDS_POSITION_TOP,
+ BOUNDS_POSITION_RIGHT,
R.layout.privacy_dot_top_right));
mPrivacyDotBottomLeftDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
R.id.privacy_dot_bottom_left_container,
- DisplayCutout.BOUNDS_POSITION_BOTTOM,
- DisplayCutout.BOUNDS_POSITION_LEFT,
+ BOUNDS_POSITION_BOTTOM,
+ BOUNDS_POSITION_LEFT,
R.layout.privacy_dot_bottom_left));
mPrivacyDotBottomRightDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
R.id.privacy_dot_bottom_right_container,
- DisplayCutout.BOUNDS_POSITION_BOTTOM,
- DisplayCutout.BOUNDS_POSITION_RIGHT,
+ BOUNDS_POSITION_BOTTOM,
+ BOUNDS_POSITION_RIGHT,
R.layout.privacy_dot_bottom_right));
// Default no cutout
@@ -256,11 +258,10 @@
mLazyViewCapture, false);
mScreenDecorations = spy(new ScreenDecorations(mContext, mSecureSettings,
mCommandRegistry, mUserTracker, mDisplayTracker, mDotViewController,
- mThreadFactory,
mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader,
- mViewCaptureAwareWindowManager) {
+ mViewCaptureAwareWindowManager, mMainHandler, mExecutor) {
@Override
public void start() {
super.start();
@@ -1272,10 +1273,10 @@
ScreenDecorations screenDecorations = new ScreenDecorations(mContext,
mSecureSettings, mCommandRegistry, mUserTracker, mDisplayTracker,
mDotViewController,
- mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
+ mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader,
- mViewCaptureAwareWindowManager);
+ mViewCaptureAwareWindowManager, mMainHandler, mExecutor);
screenDecorations.start();
when(mContext.getDisplay()).thenReturn(mDisplay);
when(mDisplay.getDisplayInfo(any())).thenAnswer(new Answer<Boolean>() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 008e8ce..638f195 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -40,6 +40,7 @@
import com.android.systemui.plugins.fakeDarkIconDispatcher
import com.android.systemui.res.R
import com.android.systemui.scene.ui.view.WindowRootView
+import com.android.systemui.shade.LongPressGestureDetector
import com.android.systemui.shade.ShadeControllerImpl
import com.android.systemui.shade.ShadeLogger
import com.android.systemui.shade.ShadeViewController
@@ -97,6 +98,7 @@
@Mock private lateinit var windowRootView: Provider<WindowRootView>
@Mock private lateinit var shadeLogger: ShadeLogger
@Mock private lateinit var viewUtil: ViewUtil
+ @Mock private lateinit var longPressGestureDetector: LongPressGestureDetector
private lateinit var statusBarWindowStateController: StatusBarWindowStateController
private lateinit var view: PhoneStatusBarView
@@ -393,6 +395,7 @@
shadeControllerImpl,
shadeViewController,
panelExpansionInteractor,
+ { longPressGestureDetector },
windowRootView,
shadeLogger,
viewUtil,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
index 9282f27..92dc897 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayWindowPropertiesRepository.kt
@@ -31,6 +31,7 @@
windowType = windowType,
context = mock(),
windowManager = mock(),
+ layoutInflater = mock(),
)
.also { properties.put(displayId, windowType, it) }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
index a9a80b5..ddae581 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -6,6 +6,8 @@
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
var Kosmos.testDispatcher by Fixture { StandardTestDispatcher() }
@@ -34,3 +36,12 @@
testScope.backgroundScope.coroutineContext
}
var Kosmos.mainCoroutineContext: CoroutineContext by Fixture { testScope.coroutineContext }
+
+/**
+ * Run this test body with a [Kosmos] as receiver, and using the [testScope] currently installed in
+ * that kosmos instance
+ */
+fun Kosmos.runTest(testBody: suspend Kosmos.() -> Unit) =
+ testScope.runTest { this@runTest.testBody() }
+
+fun Kosmos.runCurrent() = testScope.runCurrent()
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 31ae966..c31b9ef 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2383,7 +2383,7 @@
// If running in background is disabled or mStopUserOnSwitch mode, stop the user.
if (hasRestriction || isStopUserOnSwitchEnabled()) {
Slogf.i(TAG, "Stopping user %d and its profiles on user switch", oldUserId);
- stopUsersLU(oldUserId, /* allowDelayedLocking= */ false, null, null);
+ stopUsersLU(oldUserId, /* allowDelayedLocking= */ !hasRestriction, null, null);
return;
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
index a118415..21c5140 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
@@ -162,7 +162,7 @@
STATE_WAITING_IN_QUEUE,
STATE_WAITING_FOR_COOKIE,
STATE_WAITING_IN_QUEUE_CANCELING)) {
- return false;
+ return hasOperationAlreadyStarted();
}
if (mClientMonitor.getCookie() != 0) {
@@ -191,7 +191,7 @@
STATE_WAITING_IN_QUEUE,
STATE_WAITING_FOR_COOKIE,
STATE_WAITING_IN_QUEUE_CANCELING)) {
- return false;
+ return hasOperationAlreadyStarted();
}
return doStart(callback);
@@ -230,6 +230,10 @@
return true;
}
+ private boolean hasOperationAlreadyStarted() {
+ return mState == STATE_STARTED;
+ }
+
/**
* Abort a pending operation.
*
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
index a3c68f9..afffa66 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
@@ -21,6 +21,7 @@
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
+import android.hardware.broadcastradio.Alert;
import android.hardware.broadcastradio.AmFmRegionConfig;
import android.hardware.broadcastradio.Announcement;
import android.hardware.broadcastradio.ConfigFlag;
@@ -36,6 +37,7 @@
import android.hardware.radio.Flags;
import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioAlert;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioMetadata;
import android.hardware.radio.RadioTuner;
@@ -573,6 +575,86 @@
return builder.build();
}
+ @Nullable private static RadioAlert.Polygon polygonFromHalPolygon(
+ android.hardware.broadcastradio.Polygon halPolygon) {
+ if (halPolygon.coordinates.length < 4) {
+ Slogf.e(TAG, "Number of coordinates in alert polygon cannot be less than 4");
+ return null;
+ } else if (halPolygon.coordinates[0].latitude
+ != halPolygon.coordinates[halPolygon.coordinates.length - 1].latitude
+ || halPolygon.coordinates[0].longitude
+ != halPolygon.coordinates[halPolygon.coordinates.length - 1].longitude) {
+ Slogf.e(TAG, "The first and the last coordinate in alert polygon cannot be different");
+ return null;
+ }
+ List<RadioAlert.Coordinate> coordinates = new ArrayList<>(halPolygon.coordinates.length);
+ for (int idx = 0; idx < halPolygon.coordinates.length; idx++) {
+ coordinates.add(new RadioAlert.Coordinate(halPolygon.coordinates[idx].latitude,
+ halPolygon.coordinates[idx].longitude));
+ }
+ return new RadioAlert.Polygon(coordinates);
+ }
+
+ private static RadioAlert.Geocode geocodeFromHalGeocode(
+ android.hardware.broadcastradio.Geocode geocode) {
+ return new RadioAlert.Geocode(geocode.valueName, geocode.value);
+ }
+
+ private static RadioAlert.AlertArea alertAreaFromHalAlertArea(
+ android.hardware.broadcastradio.AlertArea halAlertArea) {
+ List<RadioAlert.Polygon> polygonList = new ArrayList<>();
+ for (int idx = 0; idx < halAlertArea.polygons.length; idx++) {
+ RadioAlert.Polygon polygon = polygonFromHalPolygon(halAlertArea.polygons[idx]);
+ if (polygon != null) {
+ polygonList.add(polygon);
+ }
+ }
+ List<RadioAlert.Geocode> geocodeList = new ArrayList<>(halAlertArea.geocodes.length);
+ for (int idx = 0; idx < halAlertArea.geocodes.length; idx++) {
+ geocodeList.add(geocodeFromHalGeocode(halAlertArea.geocodes[idx]));
+ }
+ return new RadioAlert.AlertArea(polygonList, geocodeList);
+ }
+
+ private static RadioAlert.AlertInfo alertInfoFromHalAlertInfo(
+ android.hardware.broadcastradio.AlertInfo halAlertInfo) {
+ int[] categoryArray = new int[halAlertInfo.categoryArray.length];
+ for (int idx = 0; idx < halAlertInfo.categoryArray.length; idx++) {
+ // Integer values in android.hardware.radio.RadioAlert.AlertCategory and
+ // android.hardware.broadcastradio.AlertCategory match.
+ categoryArray[idx] = halAlertInfo.categoryArray[idx];
+ }
+ List<RadioAlert.AlertArea> alertAreaList = new ArrayList<>();
+ for (int idx = 0; idx < halAlertInfo.areas.length; idx++) {
+ alertAreaList.add(alertAreaFromHalAlertArea(halAlertInfo.areas[idx]));
+ }
+ // Integer values in android.hardware.radio.RadioAlert.AlertUrgency and
+ // android.hardware.broadcastradio.AlertUrgency match.
+ // Integer values in android.hardware.radio.RadioAlert.AlertSeverity and
+ // android.hardware.broadcastradio.AlertSeverity match.
+ // Integer values in android.hardware.radio.RadioAlert.AlertCertainty and
+ // android.hardware.broadcastradio.AlertCertainty match.
+ return new RadioAlert.AlertInfo(categoryArray, halAlertInfo.urgency, halAlertInfo.severity,
+ halAlertInfo.certainty, halAlertInfo.description, alertAreaList,
+ halAlertInfo.language);
+ }
+
+ @VisibleForTesting
+ @Nullable static RadioAlert radioAlertFromHalAlert(Alert halAlert) {
+ if (halAlert == null) {
+ return null;
+ }
+ List<RadioAlert.AlertInfo> alertInfo = new ArrayList<>(halAlert.infoArray.length);
+ for (int idx = 0; idx < halAlert.infoArray.length; idx++) {
+ alertInfo.add(alertInfoFromHalAlertInfo(halAlert.infoArray[idx]));
+ }
+ // Integer values in android.hardware.radio.RadioAlert.AlertStatus and
+ // android.hardware.broadcastradio.AlertStatus match.
+ // Integer values in android.hardware.radio.RadioAlert.AlertMessageType and
+ // android.hardware.broadcastradio.AlertMessageType match.
+ return new RadioAlert(halAlert.status, halAlert.messageType, alertInfo);
+ }
+
private static boolean isValidLogicallyTunedTo(ProgramIdentifier id) {
return id.type == IdentifierType.AMFM_FREQUENCY_KHZ || id.type == IdentifierType.RDS_PI
|| id.type == IdentifierType.HD_STATION_ID_EXT
@@ -605,7 +687,18 @@
}
}
}
-
+ if (!Flags.hdRadioEmergencyAlertSystem()) {
+ return new RadioManager.ProgramInfo(
+ Objects.requireNonNull(programSelectorFromHalProgramSelector(info.selector)),
+ identifierFromHalProgramIdentifier(info.logicallyTunedTo),
+ identifierFromHalProgramIdentifier(info.physicallyTunedTo),
+ relatedContent,
+ info.infoFlags,
+ info.signalQuality,
+ radioMetadataFromHalMetadata(info.metadata),
+ vendorInfoFromHalVendorKeyValues(info.vendorInfo)
+ );
+ }
return new RadioManager.ProgramInfo(
Objects.requireNonNull(programSelectorFromHalProgramSelector(info.selector)),
identifierFromHalProgramIdentifier(info.logicallyTunedTo),
@@ -614,7 +707,8 @@
info.infoFlags,
info.signalQuality,
radioMetadataFromHalMetadata(info.metadata),
- vendorInfoFromHalVendorKeyValues(info.vendorInfo)
+ vendorInfoFromHalVendorKeyValues(info.vendorInfo),
+ radioAlertFromHalAlert(info.emergencyAlert)
);
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 0807c70..4ad7c10 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -26,6 +26,7 @@
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
import android.view.DisplayShape;
+import android.view.FrameRateCategoryRate;
import android.view.RoundedCorners;
import android.view.Surface;
@@ -300,6 +301,11 @@
public boolean hasArrSupport;
/**
+ * Represents frame rate for the FrameRateCategory Normal and High.
+ * @see android.view.Display#getSuggestedFrameRate(int) for more details.
+ */
+ public FrameRateCategoryRate frameRateCategoryRate;
+ /**
* The default mode of the display.
*/
public int defaultModeId;
@@ -548,7 +554,8 @@
|| !Objects.equals(roundedCorners, other.roundedCorners)
|| installOrientation != other.installOrientation
|| !Objects.equals(displayShape, other.displayShape)
- || hasArrSupport != other.hasArrSupport) {
+ || hasArrSupport != other.hasArrSupport
+ || !Objects.equals(frameRateCategoryRate, other.frameRateCategoryRate)) {
diff |= DIFF_OTHER;
}
return diff;
@@ -567,6 +574,7 @@
modeId = other.modeId;
renderFrameRate = other.renderFrameRate;
hasArrSupport = other.hasArrSupport;
+ frameRateCategoryRate = other.frameRateCategoryRate;
defaultModeId = other.defaultModeId;
userPreferredModeId = other.userPreferredModeId;
supportedModes = other.supportedModes;
@@ -612,6 +620,7 @@
sb.append(", modeId ").append(modeId);
sb.append(", renderFrameRate ").append(renderFrameRate);
sb.append(", hasArrSupport ").append(hasArrSupport);
+ sb.append(", frameRateCategoryRate ").append(frameRateCategoryRate);
sb.append(", defaultModeId ").append(defaultModeId);
sb.append(", userPreferredModeId ").append(userPreferredModeId);
sb.append(", supportedModes ").append(Arrays.toString(supportedModes));
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index f9c3a46..a4bb8c3 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -45,6 +45,7 @@
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
import android.view.DisplayShape;
+import android.view.FrameRateCategoryRate;
import android.view.RoundedCorners;
import android.view.SurfaceControl;
@@ -247,6 +248,7 @@
private boolean mDisplayModeSpecsInvalid;
private int mActiveColorMode;
private boolean mHasArrSupport;
+ private FrameRateCategoryRate mFrameRateCategoryRate;
private Display.HdrCapabilities mHdrCapabilities;
private boolean mAllmSupported;
private boolean mGameContentTypeSupported;
@@ -313,6 +315,7 @@
changed |= updateAllmSupport(dynamicInfo.autoLowLatencyModeSupported);
changed |= updateGameContentTypeSupport(dynamicInfo.gameContentTypeSupported);
changed |= updateHasArrSupportLocked(dynamicInfo.hasArrSupport);
+ changed |= updateFrameRateCategoryRatesLocked(dynamicInfo.frameRateCategoryRate);
if (changed) {
mHavePendingChanges = true;
@@ -604,6 +607,15 @@
return true;
}
+ private boolean updateFrameRateCategoryRatesLocked(
+ FrameRateCategoryRate newFrameRateCategoryRate) {
+ if (Objects.equals(mFrameRateCategoryRate, newFrameRateCategoryRate)) {
+ return false;
+ }
+ mFrameRateCategoryRate = newFrameRateCategoryRate;
+ return true;
+ }
+
private boolean updateHasArrSupportLocked(boolean newHasArrSupport) {
if (mHasArrSupport == newHasArrSupport) {
return false;
@@ -695,6 +707,7 @@
}
mInfo.hdrCapabilities = mHdrCapabilities;
mInfo.hasArrSupport = mHasArrSupport;
+ mInfo.frameRateCategoryRate = mFrameRateCategoryRate;
mInfo.appVsyncOffsetNanos = mActiveSfDisplayMode.appVsyncOffsetNanos;
mInfo.presentationDeadlineNanos = mActiveSfDisplayMode.presentationDeadlineNanos;
mInfo.state = mState;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 074a4d8..7cfdcaf 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -507,6 +507,7 @@
mBaseDisplayInfo.modeId = deviceInfo.modeId;
mBaseDisplayInfo.renderFrameRate = deviceInfo.renderFrameRate;
mBaseDisplayInfo.hasArrSupport = deviceInfo.hasArrSupport;
+ mBaseDisplayInfo.frameRateCategoryRate = deviceInfo.frameRateCategoryRate;
mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId;
mBaseDisplayInfo.userPreferredModeId = deviceInfo.userPreferredModeId;
mBaseDisplayInfo.supportedModes = Arrays.copyOf(
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 36cadf5..f49608b 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -430,3 +430,11 @@
bug: "350617205"
is_fixed_read_only: true
}
+
+flag {
+ name: "enable_get_suggested_frame_rate"
+ namespace: "core_graphics"
+ description: "Flag for an API to get suggested frame rates"
+ bug: "361433796"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 15d76a2..0104373 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1082,6 +1082,19 @@
AdditionalSubtypeMapRepository.remove(userId);
InputMethodSettingsRepository.remove(userId);
mService.mUserDataRepository.remove(userId);
+ synchronized (ImfLock.class) {
+ final int nextOrCurrentUser = mService.mUserSwitchHandlerTask != null
+ ? mService.mUserSwitchHandlerTask.mToUserId : mService.mCurrentImeUserId;
+ if (!mService.mConcurrentMultiUserModeEnabled && userId == nextOrCurrentUser) {
+ // The current user was removed without an ongoing switch, or the user targeted
+ // by the ongoing switch was removed. Switch to the current non-profile user
+ // to allow starting input on it or one of its profile users later.
+ // Note: non-profile users cannot be removed while they are the current user.
+ final int currentUserId = mService.mActivityManagerInternal.getCurrentUserId();
+ mService.scheduleSwitchUserTaskLocked(currentUserId,
+ null /* clientToBeReset */);
+ }
+ }
}
@Override
@@ -1332,7 +1345,7 @@
+ " prevUserId=" + prevUserId);
}
- // Clean up stuff for mCurrentUserId, which soon becomes the previous user.
+ // Clean up stuff for mCurrentImeUserId, which soon becomes the previous user.
// TODO(b/338461930): Check if this is still necessary or not.
onUnbindCurrentMethodByReset(prevUserId);
diff --git a/services/core/java/com/android/server/notification/NotificationBackupHelper.java b/services/core/java/com/android/server/notification/NotificationBackupHelper.java
index ee9ec15..9df44a4 100644
--- a/services/core/java/com/android/server/notification/NotificationBackupHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationBackupHelper.java
@@ -16,6 +16,8 @@
package com.android.server.notification;
+import static android.app.backup.NotificationLoggingConstants.KEY_NOTIFICATIONS;
+
import android.app.INotificationManager;
import android.app.backup.BlobBackupHelper;
import android.os.ServiceManager;
@@ -31,9 +33,6 @@
// Current version of the blob schema
static final int BLOB_VERSION = 1;
- // Key under which the payload blob is stored
- static final String KEY_NOTIFICATIONS = "notifications";
-
private final int mUserId;
private final NotificationManagerInternal mNm;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bb0aaaf..62df825 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -161,6 +161,8 @@
import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
+import static android.app.backup.NotificationLoggingConstants.ERROR_XML_PARSING;
import static com.android.server.notification.Flags.expireBitmaps;
import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_ANIM_BUFFER;
import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
@@ -1098,7 +1100,7 @@
}
void readPolicyXml(InputStream stream, boolean forRestore, int userId,
- BackupRestoreEventLogger logger)
+ @Nullable BackupRestoreEventLogger logger)
throws XmlPullParserException, NumberFormatException, IOException {
final TypedXmlPullParser parser;
if (forRestore) {
@@ -1114,7 +1116,27 @@
int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (ZenModeConfig.ZEN_TAG.equals(parser.getName())) {
- mZenModeHelper.readXml(parser, forRestore, userId);
+ int successfulReads = 0;
+ int unsuccessfulReads = 0;
+ try {
+ boolean loadedCorrectly =
+ mZenModeHelper.readXml(parser, forRestore, userId, logger);
+ if (loadedCorrectly)
+ successfulReads++;
+ else
+ unsuccessfulReads++;
+ } catch (Exception e) {
+ Slog.wtf(TAG, "failed to read config", e);
+ unsuccessfulReads++;
+ }
+ if (logger != null) {
+ logger.logItemsRestored(DATA_TYPE_ZEN_CONFIG, successfulReads);
+ if (unsuccessfulReads > 0) {
+ logger.logItemsRestoreFailed(
+ DATA_TYPE_ZEN_CONFIG, unsuccessfulReads, ERROR_XML_PARSING);
+ }
+ }
+
} else if (PreferencesHelper.TAG_RANKING.equals(parser.getName())){
mPreferencesHelper.readXml(parser, forRestore, userId);
}
@@ -1246,7 +1268,7 @@
}
}
- private void writePolicyXml(OutputStream stream, boolean forBackup, int userId,
+ void writePolicyXml(OutputStream stream, boolean forBackup, int userId,
BackupRestoreEventLogger logger) throws IOException {
final TypedXmlSerializer out;
if (forBackup) {
@@ -1258,7 +1280,7 @@
out.startDocument(null, true);
out.startTag(null, TAG_NOTIFICATION_POLICY);
out.attributeInt(null, ATTR_VERSION, DB_VERSION);
- mZenModeHelper.writeXml(out, forBackup, null, userId);
+ mZenModeHelper.writeXml(out, forBackup, null, userId, logger);
mPreferencesHelper.writeXml(out, forBackup, userId);
mListeners.writeXml(out, forBackup, userId);
mAssistants.writeXml(out, forBackup, userId);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 3900870..ce249c6 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -43,6 +43,8 @@
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.Preconditions.checkArgument;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
+import static android.app.backup.NotificationLoggingConstants.ERROR_XML_PARSING;
import static java.util.Objects.requireNonNull;
@@ -56,6 +58,7 @@
import android.app.Flags;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
+import android.app.backup.BackupRestoreEventLogger;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
@@ -1759,11 +1762,10 @@
pw.println(config);
}
- public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId)
- throws XmlPullParserException, IOException {
- ZenModeConfig config = ZenModeConfig.readXml(parser);
+ public boolean readXml(TypedXmlPullParser parser, boolean forRestore, int userId,
+ @Nullable BackupRestoreEventLogger logger) throws XmlPullParserException, IOException {
+ ZenModeConfig config = ZenModeConfig.readXml(parser, logger);
String reason = "readXml";
-
if (config != null) {
if (forRestore) {
config.user = userId;
@@ -1855,22 +1857,38 @@
if (DEBUG) Log.d(TAG, reason);
synchronized (mConfigLock) {
- setConfigLocked(config, null,
+ return setConfigLocked(config, null,
forRestore ? ORIGIN_RESTORE_BACKUP : ORIGIN_INIT, reason,
Process.SYSTEM_UID);
}
}
+ return false;
}
- public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId)
- throws IOException {
+ public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId,
+ @Nullable BackupRestoreEventLogger logger) throws IOException {
synchronized (mConfigLock) {
+ int successfulWrites = 0;
+ int unsuccessfulWrites = 0;
final int n = mConfigs.size();
for (int i = 0; i < n; i++) {
if (forBackup && mConfigs.keyAt(i) != userId) {
continue;
}
- mConfigs.valueAt(i).writeXml(out, version, forBackup);
+ try {
+ mConfigs.valueAt(i).writeXml(out, version, forBackup, logger);
+ successfulWrites++;
+ } catch (Exception e) {
+ Slog.e(TAG, "failed to write config", e);
+ unsuccessfulWrites++;
+ }
+ }
+ if (logger != null) {
+ logger.logItemsBackedUp(DATA_TYPE_ZEN_CONFIG, successfulWrites);
+ if (unsuccessfulWrites > 0) {
+ logger.logItemsBackupFailed(DATA_TYPE_ZEN_CONFIG,
+ unsuccessfulWrites, ERROR_XML_PARSING);
+ }
}
}
}
@@ -2501,7 +2519,8 @@
try {
parser = resources.getXml(R.xml.default_zen_mode_config);
while (parser.next() != XmlPullParser.END_DOCUMENT) {
- final ZenModeConfig config = ZenModeConfig.readXml(XmlUtils.makeTyped(parser));
+ final ZenModeConfig config =
+ ZenModeConfig.readXml(XmlUtils.makeTyped(parser), null);
if (config != null) return config;
}
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
index d1d6ed0..77572e0 100644
--- a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
+++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
@@ -179,13 +179,17 @@
final String action = intent.getAction().substring(actionIndex);
Log.d(LOG_TAG, "Action requested: " + action + ", by userId "
+ ActivityManager.getCurrentUser() + " for alarm on user "
- + UserHandle.getUserHandleForUid(clientUid));
+ + UserHandle.getUserHandleForUid(clientUid).getIdentifier());
}
if (ACTION_MUTE_SOUND.equals(intent.getAction())) {
muteAlarmSounds(clientUid);
} else if (ACTION_SWITCH_USER.equals(intent.getAction())) {
- activityManager.switchUser(UserHandle.getUserId(clientUid));
+ int userId = UserHandle.getUserId(clientUid);
+ if (mUserManager.isProfile(userId)) {
+ userId = mUserManager.getProfileParent(userId).id;
+ }
+ activityManager.switchUser(userId);
}
if (Flags.multipleAlarmNotificationsSupport()) {
mNotificationClientUids.remove(clientUid);
@@ -237,11 +241,12 @@
UserHandle.of(ActivityManager.getCurrentUser()), 0);
final int userId = UserHandle.getUserId(afi.getClientUid());
final int usage = afi.getAttributes().getUsage();
- UserInfo userInfo = mUserManager.getUserInfo(userId);
-
+ UserInfo userInfo = mUserManager.isProfile(userId) ? mUserManager.getProfileParent(userId) :
+ mUserManager.getUserInfo(userId);
+ ActivityManager activityManager = foregroundContext.getSystemService(ActivityManager.class);
// Only show notification if the sound is coming from background user and the notification
// for this UID is not already shown.
- if (userInfo != null && userId != foregroundContext.getUserId()
+ if (userInfo != null && !activityManager.isProfileForeground(userInfo.getUserHandle())
&& !isNotificationShown(afi.getClientUid())) {
//TODO: b/349138482 - Add handling of cases when usage == USAGE_NOTIFICATION_RINGTONE
if (usage == USAGE_ALARM) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0745fec..b036cfc 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5896,29 +5896,6 @@
}
/**
- * Registers an app that uses the Strict Mode for detecting BAL.
- *
- * @param callback the callback to register
- * @return {@code true} if the callback was registered successfully.
- */
- @Override
- public boolean registerBackgroundActivityStartCallback(IBinder callback) {
- return mTaskSupervisor.getBackgroundActivityLaunchController()
- .addStrictModeCallback(Binder.getCallingUid(), callback);
- }
-
- /**
- * Unregisters an app that uses the Strict Mode for detecting BAL.
- *
- * @param callback the callback to unregister
- */
- @Override
- public void unregisterBackgroundActivityStartCallback(IBinder callback) {
- mTaskSupervisor.getBackgroundActivityLaunchController()
- .removeStrictModeCallback(Binder.getCallingUid(), callback);
- }
-
- /**
* Wrap the {@link ActivityOptions} in {@link SafeActivityOptions} and attach caller options
* that allow using the callers permissions to start background activities.
*/
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index a462c67..9c7a6f9 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -52,7 +52,6 @@
import static com.android.window.flags.Flags.balRequireOptInSameUid;
import static com.android.window.flags.Flags.balRespectAppSwitchStateWhenCheckBoundByForegroundUid;
import static com.android.window.flags.Flags.balShowToastsBlocked;
-import static com.android.window.flags.Flags.balStrictMode;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import static java.util.Objects.requireNonNull;
@@ -64,7 +63,6 @@
import android.app.ActivityOptions;
import android.app.AppOpsManager;
import android.app.BackgroundStartPrivileges;
-import android.app.IBackgroundActivityLaunchCallback;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
@@ -72,17 +70,13 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.os.IBinder;
import android.os.Process;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.DeviceConfig;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.Slog;
-import android.util.SparseArray;
import android.widget.Toast;
import com.android.internal.R;
@@ -97,7 +91,6 @@
import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Map;
import java.util.StringJoiner;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -148,10 +141,6 @@
private final ActivityTaskManagerService mService;
private final ActivityTaskSupervisor mSupervisor;
- @GuardedBy("mStrictModeBalCallbacks")
- private final SparseArray<ArrayMap<IBinder, IBackgroundActivityLaunchCallback>>
- mStrictModeBalCallbacks = new SparseArray<>();
-
// TODO(b/263368846) Rename when ASM logic is moved in
@Retention(SOURCE)
@@ -850,120 +839,7 @@
// only show a toast if either caller or real caller could launch if they opted in
showToast("BAL blocked. goo.gle/android-bal");
}
- BalVerdict verdict = statsLog(BalVerdict.BLOCK, state);
- if (balStrictMode()) {
- String abortDebugMessage;
- if (state.isPendingIntent()) {
- abortDebugMessage =
- "PendingIntent Activity start blocked in " + state.mRealCallingPackage
- + ". "
- + "PendingIntent was created in " + state.mCallingPackage
- + ". "
- + (state.mResultForRealCaller.allows()
- ? state.mRealCallingPackage
- + " could opt in to grant BAL privileges when sending. "
- : "")
- + (state.mResultForCaller.allows()
- ? state.mCallingPackage
- + " could opt in to grant BAL privileges when creating."
- : "")
- + "The intent would have started " + state.mIntent.getComponent();
- } else {
- abortDebugMessage = "Activity start blocked. "
- + "The intent would have started " + state.mIntent.getComponent();
- }
- strictModeLaunchAborted(state.mCallingUid, abortDebugMessage);
- if (!state.callerIsRealCaller()) {
- strictModeLaunchAborted(state.mRealCallingUid, abortDebugMessage);
- }
- }
- return verdict;
- }
-
- /**
- * Retrieve a registered strict mode callback for BAL.
- * @param uid the uid of the app.
- * @return the callback if it exists, returns <code>null</code> otherwise.
- */
- @Nullable
- Map<IBinder, IBackgroundActivityLaunchCallback> getStrictModeBalCallbacks(int uid) {
- ArrayMap<IBinder, IBackgroundActivityLaunchCallback> callbackMap;
- synchronized (mStrictModeBalCallbacks) {
- callbackMap =
- mStrictModeBalCallbacks.get(uid);
- if (callbackMap == null) {
- return null;
- }
- return new ArrayMap<>(callbackMap);
- }
- }
-
- /**
- * Add strict mode callback for BAL.
- *
- * @param uid the UID for which the binder is registered.
- * @param callback the {@link IBackgroundActivityLaunchCallback} binder to call when BAL is
- * blocked.
- * @return {@code true} if the callback has been successfully added.
- */
- boolean addStrictModeCallback(int uid, IBinder callback) {
- IBackgroundActivityLaunchCallback balCallback =
- IBackgroundActivityLaunchCallback.Stub.asInterface(callback);
- synchronized (mStrictModeBalCallbacks) {
- ArrayMap<IBinder, IBackgroundActivityLaunchCallback> callbackMap =
- mStrictModeBalCallbacks.get(uid);
- if (callbackMap == null) {
- callbackMap = new ArrayMap<>();
- mStrictModeBalCallbacks.put(uid, callbackMap);
- }
- if (callbackMap.containsKey(callback)) {
- return false;
- }
- callbackMap.put(callback, balCallback);
- }
- try {
- callback.linkToDeath(() -> removeStrictModeCallback(uid, callback), 0);
- } catch (RemoteException e) {
- removeStrictModeCallback(uid, callback);
- }
- return true;
- }
-
- /**
- * Remove strict mode callback for BAL.
- *
- * @param uid the UID for which the binder is registered.
- * @param callback the {@link IBackgroundActivityLaunchCallback} binder to call when BAL is
- * blocked.
- */
- void removeStrictModeCallback(int uid, IBinder callback) {
- synchronized (mStrictModeBalCallbacks) {
- Map<IBinder, IBackgroundActivityLaunchCallback> callbackMap =
- mStrictModeBalCallbacks.get(uid);
- if (callback == null || !callbackMap.containsKey(callback)) {
- return;
- }
- callbackMap.remove(callback);
- if (callbackMap.isEmpty()) {
- mStrictModeBalCallbacks.remove(uid);
- }
- }
- }
-
- private void strictModeLaunchAborted(int callingUid, String message) {
- Map<IBinder, IBackgroundActivityLaunchCallback> strictModeBalCallbacks =
- getStrictModeBalCallbacks(callingUid);
- if (strictModeBalCallbacks == null) {
- return;
- }
- for (Map.Entry<IBinder, IBackgroundActivityLaunchCallback> callbackEntry :
- strictModeBalCallbacks.entrySet()) {
- try {
- callbackEntry.getValue().onBackgroundActivityLaunchAborted(message);
- } catch (RemoteException e) {
- removeStrictModeCallback(callingUid, callbackEntry.getKey());
- }
- }
+ return statsLog(BalVerdict.BLOCK, state);
}
/**
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index cbe3d79..6f8c17a 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -402,6 +402,7 @@
|| first.modeId != second.modeId
|| first.renderFrameRate != second.renderFrameRate
|| first.hasArrSupport != second.hasArrSupport
+ || !Objects.equals(first.frameRateCategoryRate, second.frameRateCategoryRate)
|| first.defaultModeId != second.defaultModeId
|| first.userPreferredModeId != second.userPreferredModeId
|| !Arrays.equals(first.supportedModes, second.supportedModes)
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
index bf946a1..3d0c637 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
@@ -103,6 +103,7 @@
assumeTrue(UserManager.supportsMultipleUsers());
AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build();
UserInfo user = createUser("User", UserManager.USER_TYPE_FULL_SECONDARY, 0);
+
final int fgUserId = mSpiedContext.getUserId();
final int bgUserUid = user.id * 100000;
doReturn(UserHandle.of(fgUserId)).when(mSpiedContext).getUser();
@@ -209,6 +210,28 @@
eq(UserHandle.of(fgUserId)));
}
+ @Test
+ public void testOnAudioFocusGrant_alarmOnProfileOfForegroundUser_foregroundUserNotNotified() {
+ assumeTrue(UserManager.supportsMultipleUsers());
+ final int fgUserId = mSpiedContext.getUserId();
+ UserInfo fgUserProfile = createProfileForUser("Background profile",
+ UserManager.USER_TYPE_PROFILE_MANAGED, fgUserId, null);
+ assumeTrue("Cannot add a profile", fgUserProfile != null);
+ int fgUserProfileUid = fgUserProfile.id * 100_000;
+
+ AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build();
+ AudioFocusInfo afi = new AudioFocusInfo(aa, fgUserProfileUid, "", "",
+ AudioManager.AUDIOFOCUS_GAIN, 0, 0, Build.VERSION.SDK_INT);
+
+ mBackgroundUserSoundNotifier.getAudioPolicyFocusListener()
+ .onAudioFocusGrant(afi, AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
+
+ verify(mNotificationManager, never())
+ .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()),
+ eq(afi.getClientUid()), any(Notification.class),
+ eq(UserHandle.of(fgUserId)));
+ }
+
@Test
public void testCreateNotification_UserSwitcherEnabled_bothActionsAvailable() {
@@ -327,6 +350,17 @@
}
return user;
}
+
+ private UserInfo createProfileForUser(String name, String userType, int userHandle,
+ String[] disallowedPackages) {
+ UserInfo profile = mUserManager.createProfileForUser(
+ name, userType, 0, userHandle, disallowedPackages);
+ if (profile != null) {
+ mUsersToRemove.add(profile.id);
+ }
+ return profile;
+ }
+
private void removeUser(int userId) {
mUserManager.removeUser(userId);
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
index 4f07380..26e2614 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -147,7 +147,7 @@
when(mInterruptableClientMonitor.getFreshDaemon()).thenReturn(mHal);
assertThat(mInterruptableOperation.startWithCookie(mOnStartCallback, cookie)).isTrue();
- assertThat(mInterruptableOperation.startWithCookie(mOnStartCallback, cookie)).isFalse();
+ assertThat(mInterruptableOperation.startWithCookie(mOnStartCallback, cookie)).isTrue();
}
@Test
@@ -201,7 +201,7 @@
when(mInterruptableClientMonitor.getFreshDaemon()).thenReturn(mHal);
assertThat(mInterruptableOperation.start(mOnStartCallback)).isTrue();
- assertThat(mInterruptableOperation.start(mOnStartCallback)).isFalse();
+ assertThat(mInterruptableOperation.start(mOnStartCallback)).isTrue();
}
@Test
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 f60ec99..b99ab05 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -77,6 +77,8 @@
import static android.app.PendingIntent.FLAG_ONE_SHOT;
import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_RULES;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.PackageManager.FEATURE_TELECOM;
import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -92,6 +94,7 @@
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.UserHandle.USER_SYSTEM;
import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
+import static android.os.UserManager.USER_TYPE_FULL_SYSTEM;
import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
@@ -361,6 +364,7 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
+import java.io.OutputStream;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
@@ -6499,6 +6503,35 @@
}
@Test
+ @EnableFlags(android.app.Flags.FLAG_BACKUP_RESTORE_LOGGING)
+ public void testReadPolicyXml_backupRestoreLogging() throws Exception {
+ BackupRestoreEventLogger logger = mock(BackupRestoreEventLogger.class);
+
+ UserInfo ui = new UserInfo(ActivityManager.getCurrentUser(), "Clone", UserInfo.FLAG_FULL);
+ ui.userType = USER_TYPE_FULL_SYSTEM;
+ when(mUmInternal.getUserInfo(ActivityManager.getCurrentUser())).thenReturn(ui);
+ when(mPermissionHelper.getNotificationPermissionValues(0)).thenReturn(new ArrayMap<>());
+ TypedXmlSerializer serializer = Xml.newFastSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ mService.writePolicyXml(baos, true, ActivityManager.getCurrentUser(), logger);
+ serializer.flush();
+
+ mService.readPolicyXml(
+ new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
+ true, ActivityManager.getCurrentUser(), logger);
+
+ verify(logger).logItemsBackedUp(DATA_TYPE_ZEN_CONFIG, 1);
+ verify(logger, never())
+ .logItemsBackupFailed(eq(DATA_TYPE_ZEN_CONFIG), anyInt(), anyString());
+
+ verify(logger).logItemsRestored(DATA_TYPE_ZEN_CONFIG, 1);
+ verify(logger, never())
+ .logItemsRestoreFailed(eq(DATA_TYPE_ZEN_CONFIG), anyInt(), anyString());
+ }
+
+ @Test
public void testLocaleChangedCallsUpdateDefaultZenModeRules() throws Exception {
ZenModeHelper mZenModeHelper = mock(ZenModeHelper.class);
mService.mZenModeHelper = mZenModeHelper;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 5709d88..bdf146f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -17,6 +17,8 @@
package com.android.server.notification;
import static android.app.AutomaticZenRule.TYPE_BEDTIME;
+import static android.app.Flags.FLAG_BACKUP_RESTORE_LOGGING;
+import static android.app.Flags.FLAG_MODES_API;
import static android.app.Flags.FLAG_MODES_UI;
import static android.app.Flags.modesUi;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
@@ -24,6 +26,8 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.suppressedEffectsToString;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_RULES;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
import static android.service.notification.Condition.SOURCE_UNKNOWN;
@@ -52,17 +56,22 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AutomaticZenRule;
import android.app.Flags;
import android.app.NotificationManager.Policy;
+import android.app.backup.BackupRestoreEventLogger;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Parcel;
+import android.os.UserHandle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.FlagsParameterization;
@@ -135,7 +144,7 @@
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
return FlagsParameterization.allCombinationsOf(
- FLAG_MODES_UI);
+ FLAG_MODES_UI, FLAG_BACKUP_RESTORE_LOGGING);
}
public ZenModeConfigTest(FlagsParameterization flags) {
@@ -144,7 +153,6 @@
@Before
public final void setUp() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
MockitoAnnotations.initMocks(this);
mContext.setMockPackageManager(mPm);
}
@@ -515,6 +523,98 @@
}
@Test
+ @EnableFlags({FLAG_MODES_UI, FLAG_BACKUP_RESTORE_LOGGING})
+ public void testBackupRestore_fromPreModesUi() throws IOException, XmlPullParserException {
+ String xml = "<zen version=\"12\">\n"
+ + "<allow calls=\"true\" repeatCallers=\"true\" messages=\"true\""
+ + " reminders=\"false\" events=\"false\" callsFrom=\"2\" messagesFrom=\"2\""
+ + " alarms=\"true\" media=\"true\" system=\"false\" convos=\"true\" convosFrom=\"2\""
+ + " priorityChannelsAllowed=\"true\" />\n"
+ + "<disallow visualEffects=\"157\" />\n"
+ + "<manual enabled=\"true\" zen=\"1\" creationTime=\"0\" modified=\"false\" />\n"
+ + "<state areChannelsBypassingDnd=\"true\" />\n"
+ + "</zen>";
+
+ BackupRestoreEventLogger logger = mock(BackupRestoreEventLogger.class);
+ readConfigXml(new ByteArrayInputStream(xml.getBytes()), logger);
+
+ verify(logger).logItemsRestored(DATA_TYPE_ZEN_RULES, 1);
+ }
+
+ @Test
+ public void testBackupRestore() throws IOException, XmlPullParserException {
+ ZenModeConfig config = new ZenModeConfig();
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.configurationActivity = CONFIG_ACTIVITY;
+ rule.component = OWNER;
+ rule.conditionId = CONDITION_ID;
+ rule.condition = CONDITION;
+ rule.enabled = ENABLED;
+ rule.creationTime = 123;
+ rule.id = "id";
+ rule.zenMode = INTERRUPTION_FILTER;
+ rule.modified = true;
+ rule.name = NAME;
+ rule.setConditionOverride(OVERRIDE_DEACTIVATE);
+ rule.pkg = OWNER.getPackageName();
+ rule.zenPolicy = POLICY;
+
+ rule.allowManualInvocation = ALLOW_MANUAL;
+ rule.type = TYPE;
+ rule.userModifiedFields = 16;
+ rule.zenPolicyUserModifiedFields = 5;
+ rule.zenDeviceEffectsUserModifiedFields = 2;
+ rule.iconResName = ICON_RES_NAME;
+ rule.triggerDescription = TRIGGER_DESC;
+ rule.deletionInstant = Instant.ofEpochMilli(1701790147000L);
+ if (Flags.modesUi()) {
+ rule.disabledOrigin = ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
+ }
+ config.automaticRules.put(rule.id, rule);
+
+ BackupRestoreEventLogger logger = null;
+ if (Flags.backupRestoreLogging()) {
+ logger = mock(BackupRestoreEventLogger.class);
+ }
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ writeConfigXml(config, XML_VERSION_MODES_API, true, baos, logger);
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ ZenModeConfig fromXml = readConfigXml(bais, logger);
+
+ ZenModeConfig.ZenRule ruleActual = fromXml.automaticRules.get(rule.id);
+ assertEquals(rule.pkg, ruleActual.pkg);
+ assertEquals(OVERRIDE_NONE, ruleActual.getConditionOverride());
+ assertEquals(rule.enabler, ruleActual.enabler);
+ assertEquals(rule.component, ruleActual.component);
+ assertEquals(rule.configurationActivity, ruleActual.configurationActivity);
+ assertEquals(rule.condition, ruleActual.condition);
+ assertEquals(rule.enabled, ruleActual.enabled);
+ assertEquals(rule.creationTime, ruleActual.creationTime);
+ assertEquals(rule.modified, ruleActual.modified);
+ assertEquals(rule.conditionId, ruleActual.conditionId);
+ assertEquals(rule.name, ruleActual.name);
+ assertEquals(rule.zenMode, ruleActual.zenMode);
+
+ assertEquals(rule.allowManualInvocation, ruleActual.allowManualInvocation);
+ assertEquals(rule.iconResName, ruleActual.iconResName);
+ assertEquals(rule.type, ruleActual.type);
+ assertEquals(rule.userModifiedFields, ruleActual.userModifiedFields);
+ assertEquals(rule.zenPolicyUserModifiedFields, ruleActual.zenPolicyUserModifiedFields);
+ assertEquals(rule.zenDeviceEffectsUserModifiedFields,
+ ruleActual.zenDeviceEffectsUserModifiedFields);
+ assertEquals(rule.triggerDescription, ruleActual.triggerDescription);
+ assertEquals(rule.zenPolicy, ruleActual.zenPolicy);
+ assertEquals(rule.deletionInstant, ruleActual.deletionInstant);
+ if (Flags.modesUi()) {
+ assertEquals(rule.disabledOrigin, ruleActual.disabledOrigin);
+ }
+ if (Flags.backupRestoreLogging()) {
+ verify(logger).logItemsBackedUp(DATA_TYPE_ZEN_RULES, 2);
+ verify(logger).logItemsRestored(DATA_TYPE_ZEN_RULES, 2);
+ }
+ }
+
+ @Test
public void testWriteToParcel() {
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.configurationActivity = CONFIG_ACTIVITY;
@@ -1023,9 +1123,9 @@
// write out entire config xml
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- writeConfigXml(config, XML_VERSION_MODES_API, /* forBackup= */ false, baos);
+ writeConfigXml(config, XML_VERSION_MODES_API, /* forBackup= */ false, baos, null);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
- ZenModeConfig fromXml = readConfigXml(bais);
+ ZenModeConfig fromXml = readConfigXml(bais, null);
// The result should be valid and contain a manual rule; the rule should have a non-null
@@ -1055,9 +1155,9 @@
// write out entire config xml
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- writeConfigXml(config, XML_VERSION_MODES_API, /* forBackup= */ false, baos);
+ writeConfigXml(config, XML_VERSION_MODES_API, /* forBackup= */ false, baos, null);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
- ZenModeConfig fromXml = readConfigXml(bais);
+ ZenModeConfig fromXml = readConfigXml(bais, null);
// The result should have a manual rule; it should have a non-null ZenPolicy and a condition
// whose state is true. The conditionId and enabler data should also be preserved.
@@ -1084,9 +1184,9 @@
// write out entire config xml
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- writeConfigXml(config, XML_VERSION_MODES_API, /* forBackup= */ false, baos);
+ writeConfigXml(config, XML_VERSION_MODES_API, /* forBackup= */ false, baos, null);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
- ZenModeConfig fromXml = readConfigXml(bais);
+ ZenModeConfig fromXml = readConfigXml(bais, null);
// The result should have a manual rule; it should not be changed from the previous rule.
assertThat(fromXml.manualRule).isEqualTo(config.manualRule);
@@ -1213,9 +1313,9 @@
config.manualRule.enabled = false;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- writeConfigXml(config, XML_VERSION_MODES_UI, /* forBackup= */ false, baos);
+ writeConfigXml(config, XML_VERSION_MODES_UI, /* forBackup= */ false, baos, null);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
- ZenModeConfig fromXml = readConfigXml(bais);
+ ZenModeConfig fromXml = readConfigXml(bais, null);
assertThat(fromXml.manualRule.enabled).isTrue();
}
@@ -1359,23 +1459,23 @@
}
private void writeConfigXml(ZenModeConfig config, Integer version, boolean forBackup,
- ByteArrayOutputStream os) throws IOException {
+ ByteArrayOutputStream os, BackupRestoreEventLogger logger) throws IOException {
String tag = ZEN_TAG;
TypedXmlSerializer out = Xml.newFastSerializer();
out.setOutput(new BufferedOutputStream(os), "utf-8");
out.startDocument(null, true);
out.startTag(null, tag);
- config.writeXml(out, version, forBackup);
+ config.writeXml(out, version, forBackup, logger);
out.endTag(null, tag);
out.endDocument();
}
- private ZenModeConfig readConfigXml(ByteArrayInputStream is)
+ private ZenModeConfig readConfigXml(ByteArrayInputStream is, BackupRestoreEventLogger logger)
throws XmlPullParserException, IOException {
TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setInput(new BufferedInputStream(is), null);
parser.nextTag();
- return ZenModeConfig.readXml(parser);
+ return ZenModeConfig.readXml(parser, logger);
}
}
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 a196594..6792377 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -20,6 +20,7 @@
import static android.app.AutomaticZenRule.TYPE_IMMERSIVE;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
import static android.app.AutomaticZenRule.TYPE_UNKNOWN;
+import static android.app.Flags.FLAG_BACKUP_RESTORE_LOGGING;
import static android.app.Flags.FLAG_MODES_API;
import static android.app.Flags.FLAG_MODES_MULTIUSER;
import static android.app.Flags.FLAG_MODES_UI;
@@ -83,6 +84,8 @@
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 android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_RULES;
import static com.android.server.notification.ZenModeEventLogger.ACTIVE_RULE_TYPE_MANUAL;
import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
@@ -104,6 +107,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.notNull;
@@ -118,15 +122,17 @@
import android.Manifest;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AutomaticZenRule;
import android.app.Flags;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
+import android.app.backup.BackupRestoreEventLogger;
import android.app.compat.CompatChanges;
import android.content.ComponentName;
-import android.content.ContentResolver;
+import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -273,7 +279,6 @@
private TestableLooper mTestableLooper;
private final TestClock mTestClock = new TestClock();
private ZenModeHelper mZenModeHelper;
- private ContentResolver mContentResolver;
@Mock
DeviceEffectsApplier mDeviceEffectsApplier;
@Mock
@@ -284,8 +289,7 @@
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
- return FlagsParameterization.progressionOf(FLAG_MODES_API,
- FLAG_MODES_UI);
+ return FlagsParameterization.allCombinationsOf(FLAG_MODES_UI, FLAG_BACKUP_RESTORE_LOGGING);
}
public ZenModeHelperTest(FlagsParameterization flags) {
@@ -298,7 +302,6 @@
mTestableLooper = TestableLooper.get(this);
mContext.ensureTestableResources();
- mContentResolver = mContext.getContentResolver();
mResources = mock(Resources.class, withSettings()
.spiedInstance(mContext.getResources()));
mPkg = mContext.getPackageName();
@@ -316,11 +319,16 @@
mContext.addMockSystemService(AppOpsManager.class, mAppOps);
mContext.addMockSystemService(NotificationManager.class, mNotificationManager);
+ mContext.addMockSystemService(Context.ALARM_SERVICE, mock(AlarmManager.class));
mConditionProviders = new ConditionProviders(mContext, new UserProfiles(),
AppGlobals.getPackageManager());
- mConditionProviders.addSystemProvider(new CountdownConditionProvider());
- mConditionProviders.addSystemProvider(new ScheduleConditionProvider());
+ CountdownConditionProvider countdown = spy(new CountdownConditionProvider());
+ ScheduleConditionProvider schedule = spy(new ScheduleConditionProvider());
+ doNothing().when(countdown).notifyConditions(any());
+ doNothing().when(schedule).notifyConditions(any());
+ mConditionProviders.addSystemProvider(countdown);
+ mConditionProviders.addSystemProvider(schedule);
mZenModeEventLogger = new ZenModeEventLoggerFake(mPackageManager);
mZenModeHelper = new ZenModeHelper(mContext, mTestableLooper.getLooper(), mTestClock,
mConditionProviders, mTestFlagResolver, mZenModeEventLogger);
@@ -379,7 +387,7 @@
ByteArrayOutputStream baos = new ByteArrayOutputStream();
serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
serializer.startDocument(null, true);
- mZenModeHelper.writeXml(serializer, false, version, UserHandle.USER_ALL);
+ mZenModeHelper.writeXml(serializer, false, version, UserHandle.USER_ALL, null);
serializer.endDocument();
serializer.flush();
mZenModeHelper.setConfig(new ZenModeConfig(), null, ORIGIN_INIT, "writing xml",
@@ -387,13 +395,14 @@
return baos;
}
- private ByteArrayOutputStream writeXmlAndPurgeForUser(Integer version, int userId)
+ private ByteArrayOutputStream writeXmlAndPurgeForUser(Integer version, int userId,
+ boolean forBackup, BackupRestoreEventLogger logger)
throws Exception {
TypedXmlSerializer serializer = Xml.newFastSerializer();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
serializer.startDocument(null, true);
- mZenModeHelper.writeXml(serializer, true, version, userId);
+ mZenModeHelper.writeXml(serializer, forBackup, version, userId, logger);
serializer.endDocument();
serializer.flush();
ZenModeConfig newConfig = new ZenModeConfig();
@@ -1093,7 +1102,7 @@
ByteArrayOutputStream baos = writeXmlAndPurge(null);
TypedXmlPullParser parser = getParserForByteStream(baos);
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
assertEquals("Config mismatch: current vs expected: "
+ new ZenModeDiff.ConfigDiff(mZenModeHelper.mConfig, expected), expected,
@@ -1169,7 +1178,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
List<StatsEvent> events = new LinkedList<>();
mZenModeHelper.pullRules(events);
@@ -1377,6 +1386,10 @@
@Test
public void testWriteXml_onlyBackupsTargetUser() throws Exception {
+ BackupRestoreEventLogger logger = null;
+ if (android.app.Flags.backupRestoreLogging()) {
+ logger = mock(BackupRestoreEventLogger.class);
+ }
// Setup configs for user 10 and 11.
setupZenConfig();
ZenModeConfig config10 = mZenModeHelper.mConfig.copy();
@@ -1393,15 +1406,16 @@
SYSTEM_UID);
// Backup user 10 and reset values.
- ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, 10);
+ ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, 10, true, logger);
ZenModeConfig newConfig11 = new ZenModeConfig();
newConfig11.user = 11;
mZenModeHelper.mConfigs.put(11, newConfig11);
// Parse backup data.
TypedXmlPullParser parser = getParserForByteStream(baos);
- mZenModeHelper.readXml(parser, true, 10);
- mZenModeHelper.readXml(parser, true, 11);
+ mZenModeHelper.readXml(parser, true, 10, logger);
+ parser = getParserForByteStream(baos);
+ mZenModeHelper.readXml(parser, true, 11, logger);
ZenModeConfig actual = mZenModeHelper.mConfigs.get(10);
if (Flags.modesUi()) {
@@ -1415,39 +1429,72 @@
"Config mismatch: current vs expected: "
+ new ZenModeDiff.ConfigDiff(actual, config10), config10, actual);
assertNotEquals("Expected config mismatch", config11, mZenModeHelper.mConfigs.get(11));
+
+ if (android.app.Flags.backupRestoreLogging()) {
+ verify(logger).logItemsBackedUp(DATA_TYPE_ZEN_CONFIG, 1);
+ // If this is modes_ui, this is manual + single default rule
+ // If not modes_ui, it's two default automatic rules + manual policy
+ verify(logger).logItemsBackedUp(DATA_TYPE_ZEN_RULES, Flags.modesUi() ? 2 : 3);
+ verify(logger, never())
+ .logItemsBackupFailed(anyString(), anyInt(), anyString());
+
+ verify(logger, times(2)).logItemsRestored(DATA_TYPE_ZEN_RULES, Flags.modesUi() ? 2 : 3);
+ verify(logger, never())
+ .logItemsRestoreFailed(anyString(), anyInt(), anyString());
+ }
}
@Test
public void testReadXmlRestore_forSystemUser() throws Exception {
+ BackupRestoreEventLogger logger = null;
+ if (android.app.Flags.backupRestoreLogging()) {
+ logger = mock(BackupRestoreEventLogger.class);
+ }
setupZenConfig();
// one enabled automatic rule
mZenModeHelper.mConfig.automaticRules = getCustomAutomaticRules();
ZenModeConfig original = mZenModeHelper.mConfig.copy();
- ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM);
+ ByteArrayOutputStream baos = writeXmlAndPurgeForUser(
+ null, UserHandle.USER_SYSTEM, true, logger);
TypedXmlPullParser parser = getParserForByteStream(baos);
- mZenModeHelper.readXml(parser, true, UserHandle.USER_SYSTEM);
+ mZenModeHelper.readXml(parser, true, UserHandle.USER_SYSTEM, logger);
assertEquals("Config mismatch: current vs original: "
+ new ZenModeDiff.ConfigDiff(mZenModeHelper.mConfig, original),
original, mZenModeHelper.mConfig);
assertEquals(original.hashCode(), mZenModeHelper.mConfig.hashCode());
+
+ if (android.app.Flags.backupRestoreLogging()) {
+ verify(logger).logItemsBackedUp(DATA_TYPE_ZEN_CONFIG, 1);
+ verify(logger).logItemsBackedUp(DATA_TYPE_ZEN_RULES, 2);
+ verify(logger, never())
+ .logItemsBackupFailed(anyString(), anyInt(), anyString());
+ verify(logger).logItemsRestored(DATA_TYPE_ZEN_RULES, 2);
+ verify(logger, never())
+ .logItemsRestoreFailed(anyString(), anyInt(), anyString());
+ }
}
/** Restore should ignore the data's user id and restore for the target user. */
@Test
public void testReadXmlRestore_forNonSystemUser() throws Exception {
+ BackupRestoreEventLogger logger = null;
+ if (android.app.Flags.backupRestoreLogging()) {
+ logger = mock(BackupRestoreEventLogger.class);
+ }
// Setup config.
setupZenConfig();
mZenModeHelper.mConfig.automaticRules = getCustomAutomaticRules();
ZenModeConfig expected = mZenModeHelper.mConfig.copy();
// Backup data for user 0.
- ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM);
+ ByteArrayOutputStream baos = writeXmlAndPurgeForUser(
+ null, UserHandle.USER_SYSTEM, true, logger);
// Restore data for user 10.
TypedXmlPullParser parser = getParserForByteStream(baos);
- mZenModeHelper.readXml(parser, true, 10);
+ mZenModeHelper.readXml(parser, true, 10, logger);
ZenModeConfig actual = mZenModeHelper.mConfigs.get(10);
expected.user = 10;
@@ -1461,6 +1508,10 @@
@Test
public void testReadXmlRestore_doesNotEnableManualRule() throws Exception {
+ BackupRestoreEventLogger logger = null;
+ if (android.app.Flags.backupRestoreLogging()) {
+ logger = mock(BackupRestoreEventLogger.class);
+ }
setupZenConfig();
// Turn on manual zen mode
@@ -1471,7 +1522,7 @@
ByteArrayOutputStream baos = writeXmlAndPurge(null);
TypedXmlPullParser parser = getParserForByteStream(baos);
- mZenModeHelper.readXml(parser, true, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, true, UserHandle.USER_ALL, logger);
ZenModeConfig result = mZenModeHelper.getConfig();
assertThat(result.isManualActive()).isFalse();
@@ -1525,7 +1576,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId);
ZenModeConfig.ZenRule current = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -1536,6 +1587,10 @@
@Test
public void testReadXmlRestoreWithZenPolicy_forSystemUser() throws Exception {
+ BackupRestoreEventLogger logger = null;
+ if (android.app.Flags.backupRestoreLogging()) {
+ logger = mock(BackupRestoreEventLogger.class);
+ }
final String ruleId = "customRule";
setupZenConfig();
@@ -1567,9 +1622,10 @@
SystemZenRules.maybeUpgradeRules(mContext, expected);
}
- ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM);
+ ByteArrayOutputStream baos = writeXmlAndPurgeForUser(
+ null, UserHandle.USER_SYSTEM, true, logger);
TypedXmlPullParser parser = getParserForByteStream(baos);
- mZenModeHelper.readXml(parser, true, UserHandle.USER_SYSTEM);
+ mZenModeHelper.readXml(parser, true, UserHandle.USER_SYSTEM, logger);
ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId);
ZenModeConfig.ZenRule current = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -1601,7 +1657,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
assertTrue(mZenModeHelper.mConfig.automaticRules.containsKey("customRule"));
assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT));
@@ -1621,7 +1677,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(xml.getBytes())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects());
@@ -1637,7 +1693,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(xml.getBytes())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects());
}
@@ -1656,7 +1712,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(xml.getBytes())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects());
}
@@ -1675,7 +1731,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(xml.getBytes())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
assertThat(mZenModeHelper.mConfig.getZenPolicy()
.isVisualEffectAllowed(VISUAL_EFFECT_FULL_SCREEN_INTENT, true)).isFalse();
@@ -1700,7 +1756,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(xml.getBytes())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
assertThat(mZenModeHelper.mConfig.getZenPolicy()
.isVisualEffectAllowed(VISUAL_EFFECT_PEEK, true)).isFalse();
@@ -1719,7 +1775,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(xml.getBytes())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
assertThat(mZenModeHelper.mConfig.getZenPolicy()
.isVisualEffectAllowed(VISUAL_EFFECT_FULL_SCREEN_INTENT, true)).isFalse();
@@ -1746,7 +1802,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
// check default rules
ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -1782,7 +1838,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
// check default rules
ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -1836,7 +1892,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
// check default rules
ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -1906,7 +1962,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
// check default rules
int expectedNumAutoRules = 1 + ZenModeConfig.getDefaultRuleIds().size(); // custom + default
@@ -1959,7 +2015,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
// basic check: global config maintained
assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT));
@@ -1998,7 +2054,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
// basic check: global config maintained
assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT));
@@ -2055,7 +2111,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
// basic check: global config maintained
assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT));
@@ -2121,7 +2177,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
// check default rules
ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -2173,7 +2229,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
// Implicit rule was updated.
assertThat(mZenModeHelper.mConfig.automaticRules.get(implicitRuleBeforeModesUi.id))
@@ -2211,7 +2267,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
// Both rules were untouched
assertThat(mZenModeHelper.mConfig.automaticRules.get(implicitRuleWithModesUi.id))
@@ -6965,7 +7021,7 @@
// Now simulate a reboot -> reload the configuration after purging.
TypedXmlPullParser parser = getParserForByteStream(xmlBytes);
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
if (Flags.modesUi()) {
assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
@@ -7006,7 +7062,7 @@
// Now simulate a reboot -> reload the configuration after purging.
TypedXmlPullParser parser = getParserForByteStream(xmlBytes);
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
.isEqualTo(STATE_TRUE);
@@ -7045,7 +7101,7 @@
ByteArrayOutputStream xmlBytes = writeXmlAndPurge(ZenModeConfig.XML_VERSION_MODES_UI);
TypedXmlPullParser parser = getParserForByteStream(xmlBytes);
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
assertThat(mZenModeHelper.mConfig.automaticRules).doesNotContainKey(
ZenModeConfig.EVENTS_OBSOLETE_RULE_ID);
@@ -7065,7 +7121,7 @@
ByteArrayOutputStream xmlBytes = writeXmlAndPurge(ZenModeConfig.XML_VERSION_MODES_UI);
TypedXmlPullParser parser = getParserForByteStream(xmlBytes);
- mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
assertThat(mZenModeHelper.mConfig.automaticRules).containsKey(
ZenModeConfig.EVENTS_OBSOLETE_RULE_ID);
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
index db3ce0b..7bc9f30 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -194,7 +194,6 @@
mService.mTaskSupervisor = mSupervisor;
mService.mContext = mContext;
setViaReflection(mService, "mActiveUids", mActiveUids);
- setViaReflection(mService, "mGlobalLock", new WindowManagerGlobalLock());
Mockito.when(mService.getPackageManagerInternalLocked()).thenReturn(
mPackageManagerInternal);
mService.mRootWindowContainer = mRootWindowContainer;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java b/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java
index 44b69f1..c9c31df 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java
@@ -39,6 +39,7 @@
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.DisplayShape;
+import android.view.FrameRateCategoryRate;
import android.view.RoundedCorner;
import android.view.RoundedCorners;
import android.view.SurfaceControl.RefreshRateRange;
@@ -235,6 +236,9 @@
} else if (type.isArray() && type.getComponentType().equals(Display.Mode.class)) {
field.set(first, new Display.Mode[]{new Display.Mode(100, 200, 300)});
field.set(second, new Display.Mode[]{new Display.Mode(10, 20, 30)});
+ } else if (type.equals(FrameRateCategoryRate.class)) {
+ field.set(first, new FrameRateCategoryRate(16666667, 11111111));
+ field.set(second, new FrameRateCategoryRate(11111111, 8333333));
} else {
throw new IllegalArgumentException("Field " + field
+ " is not supported by this test, please add implementation of setting "