Merge "Adding OWNERS to foldable folder." into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 8974daa..5a1561a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10992,6 +10992,7 @@
field public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
field public static final String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED";
field public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
+ field @FlaggedApi("android.content.pm.stay_stopped") public static final String ACTION_PACKAGE_UNSTOPPED = "android.intent.action.PACKAGE_UNSTOPPED";
field public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
field public static final String ACTION_PASTE = "android.intent.action.PASTE";
field public static final String ACTION_PICK = "android.intent.action.PICK";
@@ -12673,6 +12674,7 @@
method public boolean isDeviceUpgrading();
method public abstract boolean isInstantApp();
method public abstract boolean isInstantApp(@NonNull String);
+ method @FlaggedApi("android.content.pm.stay_stopped") public boolean isPackageStopped(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean isPackageSuspended(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean isPackageSuspended();
method @CheckResult public abstract boolean isPermissionRevokedByPolicy(@NonNull String, @NonNull String);
@@ -46673,10 +46675,10 @@
method @NonNull public android.text.DynamicLayout.Builder setHyphenationFrequency(int);
method @NonNull public android.text.DynamicLayout.Builder setIncludePad(boolean);
method @NonNull public android.text.DynamicLayout.Builder setJustificationMode(int);
- method @NonNull public android.text.DynamicLayout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig);
+ method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.text.DynamicLayout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig);
method @NonNull public android.text.DynamicLayout.Builder setLineSpacing(float, @FloatRange(from=0.0) float);
method @NonNull public android.text.DynamicLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic);
- method @NonNull public android.text.DynamicLayout.Builder setUseBoundsForWidth(boolean);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.DynamicLayout.Builder setUseBoundsForWidth(boolean);
method @NonNull public android.text.DynamicLayout.Builder setUseLineSpacingFromFallbacks(boolean);
}
@@ -47200,7 +47202,7 @@
method @NonNull public android.text.StaticLayout.Builder setMaxLines(@IntRange(from=0) int);
method public android.text.StaticLayout.Builder setText(CharSequence);
method @NonNull public android.text.StaticLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic);
- method @NonNull public android.text.StaticLayout.Builder setUseBoundsForWidth(boolean);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.StaticLayout.Builder setUseBoundsForWidth(boolean);
method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean);
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index aa48451..b87a640 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3283,12 +3283,12 @@
}
public class MeasuredParagraph {
- method @NonNull public static android.text.MeasuredParagraph buildForStaticLayoutTest(@NonNull android.text.TextPaint, @Nullable android.graphics.text.LineBreakConfig, @NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, int, boolean, @Nullable android.text.MeasuredParagraph.StyleRunCallback);
+ method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public static android.text.MeasuredParagraph buildForStaticLayoutTest(@NonNull android.text.TextPaint, @Nullable android.graphics.text.LineBreakConfig, @NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, int, boolean, @Nullable android.text.MeasuredParagraph.StyleRunCallback);
}
- public static interface MeasuredParagraph.StyleRunCallback {
- method public void onAppendReplacementRun(@NonNull android.graphics.Paint, @IntRange(from=0) int, @FloatRange(from=0) @Px float);
- method public void onAppendStyleRun(@NonNull android.graphics.Paint, @Nullable android.graphics.text.LineBreakConfig, @IntRange(from=0) int, boolean);
+ @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static interface MeasuredParagraph.StyleRunCallback {
+ method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public void onAppendReplacementRun(@NonNull android.graphics.Paint, @IntRange(from=0) int, @FloatRange(from=0) @Px float);
+ method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public void onAppendStyleRun(@NonNull android.graphics.Paint, @Nullable android.graphics.text.LineBreakConfig, @IntRange(from=0) int, boolean);
}
public static final class Selection.MemoryTextWatcher implements android.text.TextWatcher {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index dd1e108..6858945 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1478,7 +1478,8 @@
AppProtoEnums.APP_OP_RECORD_AUDIO_SANDBOXED;
/**
- * Allows the assistant app to receive the PCC-validated hotword and be voice-triggered.
+ * Allows the assistant app to be voice-triggered by detected hotwords from a trusted detection
+ * service.
*
* @hide
*/
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index e5a73be..21ed098 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2954,6 +2954,17 @@
}
}
+ @Override
+ public boolean isPackageStopped(@NonNull String packageName) throws NameNotFoundException {
+ try {
+ return mPM.isPackageStoppedForUser(packageName, getUserId());
+ } catch (IllegalArgumentException ie) {
+ throw new NameNotFoundException(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
@Override
public void setApplicationCategoryHint(String packageName, int categoryHint) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 44a9acd..5f4c05f 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2790,6 +2790,20 @@
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
+
+ /**
+ * Broadcast Action: An application package that was previously in the stopped state has been
+ * started and is no longer considered stopped.
+ * <ul>
+ * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
+ * </ul>
+ *
+ * <p class="note">This is a protected intent that can only be sent by the system.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @FlaggedApi(android.content.pm.Flags.FLAG_STAY_STOPPED)
+ public static final String ACTION_PACKAGE_UNSTOPPED = "android.intent.action.PACKAGE_UNSTOPPED";
+
/**
* Broadcast Action: Sent to the system rollback manager when a package
* needs to have rollback enabled.
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index aca88d6..9926415 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -308,6 +308,8 @@
boolean isPackageQuarantinedForUser(String packageName, int userId);
+ boolean isPackageStoppedForUser(String packageName, int userId);
+
Bundle getSuspendedPackageAppExtras(String packageName, int userId);
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8fbe50c3..45338bb 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -9878,6 +9878,18 @@
}
/**
+ * Query if an app is currently stopped.
+ *
+ * @return {@code true} if the given package is stopped, {@code false} otherwise
+ * @throws NameNotFoundException if the package could not be found.
+ * @see ApplicationInfo#FLAG_STOPPED
+ */
+ @FlaggedApi(android.content.pm.Flags.FLAG_STAY_STOPPED)
+ public boolean isPackageStopped(@NonNull String packageName) throws NameNotFoundException {
+ throw new UnsupportedOperationException("isPackageStopped not implemented");
+ }
+
+ /**
* Query if an app is currently quarantined.
*
* @return {@code true} if the given package is quarantined, {@code false} otherwise
@@ -9888,7 +9900,6 @@
public boolean isPackageQuarantined(@NonNull String packageName) throws NameNotFoundException {
throw new UnsupportedOperationException("isPackageQuarantined not implemented");
}
-
/**
* Provide a hint of what the {@link ApplicationInfo#category} value should
* be for the given package.
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 3e12a1a..3ec239c 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -8,6 +8,14 @@
}
flag {
+ name: "save_global_and_guest_restrictions_on_system_user_xml_read_only"
+ namespace: "multiuser"
+ description: "Save guest and device policy global restrictions on the SYSTEM user's XML file. (Read only flag)"
+ bug: "301067944"
+ is_fixed_read_only: true
+}
+
+flag {
name: "bind_wallpaper_service_on_its_own_thread_during_a_user_switch"
namespace: "multiuser"
description: "Bind wallpaper service on its own thread instead of system_server's main handler during a user switch."
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 36d3180..5c64389 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4866,7 +4866,8 @@
"display_color_mode_vendor_hint";
/**
- * The user selected min refresh rate in frames per second.
+ * The user selected min refresh rate in frames per second. If infinite, the user wants
+ * the highest possible refresh rate.
*
* If this isn't set, 0 will be used.
* @hide
@@ -4875,7 +4876,8 @@
public static final String MIN_REFRESH_RATE = "min_refresh_rate";
/**
- * The user selected peak refresh rate in frames per second.
+ * The user selected peak refresh rate in frames per second. If infinite, the user wants
+ * the highest possible refresh rate.
*
* If this isn't set, the system falls back to a device specific default.
* @hide
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 07f8aac..1cfff14 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -2007,6 +2007,20 @@
return mSmartActions == null ? Collections.emptyList() : mSmartActions;
}
+
+ /**
+ * Sets the smart {@link Notification.Action} objects.
+ *
+ * Should ONLY be used in cases where smartActions need to be removed from, then restored
+ * on, Ranking objects during Parceling, when they are transmitted between processes via
+ * Shared Memory.
+ *
+ * @hide
+ */
+ public void setSmartActions(@Nullable ArrayList<Notification.Action> smartActions) {
+ mSmartActions = smartActions;
+ }
+
/**
* Returns a list of smart replies that can be added by the
* {@link NotificationAssistantService}
@@ -2353,11 +2367,9 @@
/**
* Get a reference to the actual Ranking object corresponding to the key.
- * Used only by unit tests.
*
* @hide
*/
- @VisibleForTesting
public Ranking getRawRankingObject(String key) {
return mRankings.get(key);
}
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index f3b4c6d..0dad96e 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -18,6 +18,8 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
+import android.app.Notification;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SharedMemory;
@@ -29,9 +31,12 @@
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
/**
* Represents an update to notification rankings.
+ *
* @hide
*/
@SuppressLint({"ParcelNotFinal", "ParcelCreator"})
@@ -64,6 +69,7 @@
// The ranking map should be stored in shared memory when it is parceled, so we
// unwrap the SharedMemory object.
mRankingMapFd = in.readParcelable(getClass().getClassLoader(), SharedMemory.class);
+ Bundle smartActionsBundle = in.readBundle(getClass().getClassLoader());
// In the case that the ranking map can't be read, readParcelable may return null.
// In this case, we set mRankingMap to null;
@@ -82,8 +88,13 @@
mapParcel.unmarshall(payload, 0, payload.length);
mapParcel.setDataPosition(0);
- mRankingMap = mapParcel.readParcelable(getClass().getClassLoader(),
- android.service.notification.NotificationListenerService.RankingMap.class);
+ mRankingMap =
+ mapParcel.readParcelable(
+ getClass().getClassLoader(),
+ NotificationListenerService.RankingMap.class);
+
+ addSmartActionsFromBundleToRankingMap(smartActionsBundle);
+
} catch (ErrnoException e) {
// TODO(b/284297289): remove throw when associated flag is moved to droidfood, to
// avoid crashes; change to Log.wtf.
@@ -102,7 +113,31 @@
}
/**
+ * For each key in the rankingMap, extracts lists of smart actions stored in the provided
+ * bundle and adds them to the corresponding Ranking object in the provided ranking
+ * map, then returns the rankingMap.
+ *
+ * @hide
+ */
+ private void addSmartActionsFromBundleToRankingMap(Bundle smartActionsBundle) {
+ if (smartActionsBundle == null) {
+ return;
+ }
+
+ String[] rankingMapKeys = mRankingMap.getOrderedKeys();
+ for (int i = 0; i < rankingMapKeys.length; i++) {
+ String key = rankingMapKeys[i];
+ ArrayList<Notification.Action> smartActions =
+ smartActionsBundle.getParcelableArrayList(key, Notification.Action.class);
+ // Get the ranking object from the ranking map.
+ NotificationListenerService.Ranking ranking = mRankingMap.getRawRankingObject(key);
+ ranking.setSmartActions(smartActions);
+ }
+ }
+
+ /**
* Confirms that the SharedMemory file descriptor is closed. Should only be used for testing.
+ *
* @hide
*/
@TestApi
@@ -145,9 +180,45 @@
if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(
SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) {
final Parcel mapParcel = Parcel.obtain();
+ ArrayList<NotificationListenerService.Ranking> marshalableRankings = new ArrayList<>();
+ Bundle smartActionsBundle = new Bundle();
+
+ // We need to separate the SmartActions from the RankingUpdate objects.
+ // SmartActions can contain PendingIntents, which cannot be marshalled,
+ // so we extract them to send separately.
+ String[] rankingMapKeys = mRankingMap.getOrderedKeys();
+ for (int i = 0; i < rankingMapKeys.length; i++) {
+ String key = rankingMapKeys[i];
+ NotificationListenerService.Ranking ranking = mRankingMap.getRawRankingObject(key);
+
+ // Removes the SmartActions and stores them in a separate map.
+ // Note that getSmartActions returns a Collections.emptyList() if there are no
+ // smart actions, and we don't want to needlessly store an empty list object, so we
+ // check for null before storing.
+ List<Notification.Action> smartActions = ranking.getSmartActions();
+ if (!smartActions.isEmpty()) {
+ smartActionsBundle.putParcelableList(key, smartActions);
+ }
+
+ // Create a copy of the ranking object that doesn't have the smart actions.
+ NotificationListenerService.Ranking rankingCopy =
+ new NotificationListenerService.Ranking();
+ rankingCopy.populate(ranking);
+ rankingCopy.setSmartActions(null);
+ marshalableRankings.add(rankingCopy);
+ }
+
+ // Create a new marshalable RankingMap.
+ NotificationListenerService.RankingMap marshalableRankingMap =
+ new NotificationListenerService.RankingMap(
+ marshalableRankings.toArray(
+ new NotificationListenerService.Ranking[0]
+ )
+ );
+
try {
// Parcels the ranking map and measures its size.
- mapParcel.writeParcelable(mRankingMap, flags);
+ mapParcel.writeParcelable(marshalableRankingMap, flags);
int mapSize = mapParcel.dataSize();
// Creates a new SharedMemory object with enough space to hold the ranking map.
@@ -158,15 +229,14 @@
// Gets a read/write buffer mapping the entire shared memory region.
final ByteBuffer buffer = mRankingMapFd.mapReadWrite();
-
// Puts the ranking map into the shared memory region buffer.
buffer.put(mapParcel.marshall(), 0, mapSize);
-
// Protects the region from being written to, by setting it to be read-only.
mRankingMapFd.setProtect(OsConstants.PROT_READ);
-
// Puts the SharedMemory object in the parcel.
out.writeParcelable(mRankingMapFd, flags);
+ // Writes the Parceled smartActions separately.
+ out.writeBundle(smartActionsBundle);
} catch (ErrnoException e) {
// TODO(b/284297289): remove throw when associated flag is moved to droidfood, to
// avoid crashes; change to Log.wtf.
@@ -180,8 +250,8 @@
}
/**
- * @hide
- */
+ * @hide
+ */
public static final @android.annotation.NonNull Parcelable.Creator<NotificationRankingUpdate> CREATOR
= new Parcelable.Creator<NotificationRankingUpdate>() {
public NotificationRankingUpdate createFromParcel(Parcel parcel) {
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 2b3a081..8862f1d 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -16,6 +16,10 @@
package android.text;
+import static com.android.text.flags.Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN;
+import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
+
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -280,6 +284,7 @@
* @see android.widget.TextView#setLineBreakWordStyle
*/
@NonNull
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
public Builder setLineBreakConfig(@NonNull LineBreakConfig lineBreakConfig) {
mLineBreakConfig = lineBreakConfig;
return this;
@@ -303,6 +308,7 @@
* @see Layout.Builder#setUseBoundsForWidth(boolean)
*/
@NonNull
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public Builder setUseBoundsForWidth(boolean useBoundsForWidth) {
mUseBoundsForWidth = useBoundsForWidth;
return this;
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index c1d0e9b9..ac5eb3c 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -16,6 +16,9 @@
package android.text;
+import static com.android.text.flags.Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN;
+
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -416,10 +419,12 @@
* @hide
*/
@TestApi
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
public interface StyleRunCallback {
/**
* Called when a single style run is identified.
*/
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
void onAppendStyleRun(@NonNull Paint paint,
@Nullable LineBreakConfig lineBreakConfig, @IntRange(from = 0) int length,
boolean isRtl);
@@ -427,6 +432,7 @@
/**
* Called when a single replacement run is identified.
*/
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
void onAppendReplacementRun(@NonNull Paint paint,
@IntRange(from = 0) int length, @Px @FloatRange(from = 0) float width);
}
@@ -488,6 +494,7 @@
@SuppressLint("ExecutorRegistration")
@TestApi
@NonNull
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
public static MeasuredParagraph buildForStaticLayoutTest(
@NonNull TextPaint paint,
@Nullable LineBreakConfig lineBreakConfig,
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index e3c72c9..01279ce 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -16,6 +16,9 @@
package android.text;
+import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
+
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -439,6 +442,7 @@
* @see Layout.Builder#setUseBoundsForWidth(boolean)
*/
@NonNull
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public Builder setUseBoundsForWidth(boolean useBoundsForWidth) {
mUseBoundsForWidth = useBoundsForWidth;
return this;
diff --git a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
new file mode 100644
index 0000000..e55c641
--- /dev/null
+++ b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.display;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.util.Log;
+import android.view.Display;
+
+/**
+ * Constants and utility methods for refresh rate settings.
+ */
+public class RefreshRateSettingsUtils {
+
+ private static final String TAG = "RefreshRateSettingsUtils";
+
+ public static final float DEFAULT_REFRESH_RATE = 60f;
+
+ /**
+ * Find the highest refresh rate among all the modes of the default display.
+ *
+ * @param context The context
+ * @return The highest refresh rate
+ */
+ public static float findHighestRefreshRateForDefaultDisplay(Context context) {
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
+
+ if (display == null) {
+ Log.w(TAG, "No valid default display device");
+ return DEFAULT_REFRESH_RATE;
+ }
+
+ float maxRefreshRate = DEFAULT_REFRESH_RATE;
+ for (Display.Mode mode : display.getSupportedModes()) {
+ if (mode.getRefreshRate() > maxRefreshRate) {
+ maxRefreshRate = mode.getRefreshRate();
+ }
+ }
+ return maxRefreshRate;
+ }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 298eb2f..a0b8cca 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -48,6 +48,7 @@
<protected-broadcast android:name="android.intent.action.CANCEL_ENABLE_ROLLBACK" />
<protected-broadcast android:name="android.intent.action.ROLLBACK_COMMITTED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_RESTARTED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_UNSTOPPED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_FIRST_LAUNCH" />
<protected-broadcast android:name="android.intent.action.PACKAGE_NEEDS_INTEGRITY_VERIFICATION" />
diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
index 55ded9c..bfdb15b 100644
--- a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
+++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
@@ -16,6 +16,10 @@
package android.service.notification;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
+
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM;
import static junit.framework.Assert.assertEquals;
@@ -24,20 +28,38 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.Mockito.spy;
+
+import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.os.Bundle;
import android.os.Parcel;
+import android.os.SharedMemory;
+import android.testing.TestableContext;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import java.util.ArrayList;
+import java.util.List;
+
@SmallTest
@RunWith(Parameterized.class)
public class NotificationRankingUpdateTest {
@@ -56,6 +78,345 @@
@Parameterized.Parameter
public boolean mRankingUpdateAshmem;
+ @Rule
+ public TestableContext mContext =
+ spy(new TestableContext(InstrumentationRegistry.getContext(), null));
+
+ protected TestableContext getContext() {
+ return mContext;
+ }
+
+ public static String[] mKeys = new String[] { "key", "key1", "key2", "key3", "key4"};
+
+ /**
+ * Creates a NotificationRankingUpdate with prepopulated Ranking entries
+ * @param context A testable context, used for PendingIntent creation
+ * @return The NotificationRankingUpdate to be used as test data
+ */
+ public static NotificationRankingUpdate generateUpdate(TestableContext context) {
+ NotificationListenerService.Ranking[] rankings =
+ new NotificationListenerService.Ranking[mKeys.length];
+ for (int i = 0; i < mKeys.length; i++) {
+ final String key = mKeys[i];
+ NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
+ ranking.populate(
+ key,
+ i,
+ !isIntercepted(i),
+ getVisibilityOverride(i),
+ getSuppressedVisualEffects(i),
+ getImportance(i),
+ getExplanation(key),
+ getOverrideGroupKey(key),
+ getChannel(key, i),
+ getPeople(key, i),
+ getSnoozeCriteria(key, i),
+ getShowBadge(i),
+ getUserSentiment(i),
+ getHidden(i),
+ lastAudiblyAlerted(i),
+ getNoisy(i),
+ getSmartActions(key, i, context),
+ getSmartReplies(key, i),
+ canBubble(i),
+ isTextChanged(i),
+ isConversation(i),
+ getShortcutInfo(i),
+ getRankingAdjustment(i),
+ isBubble(i),
+ getProposedImportance(i),
+ hasSensitiveContent(i)
+ );
+ rankings[i] = ranking;
+ }
+ return new NotificationRankingUpdate(rankings);
+ }
+
+ /**
+ * Produces a visibility override value based on the provided index.
+ */
+ public static int getVisibilityOverride(int index) {
+ return index * 9;
+ }
+
+ /**
+ * Produces a group key based on the provided key.
+ */
+ public static String getOverrideGroupKey(String key) {
+ return key + key;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent isIntercepted, based on the provided index.
+ */
+ public static boolean isIntercepted(int index) {
+ return index % 2 == 0;
+ }
+
+ /**
+ * Produces a suppressed visual effects value based on the provided index
+ */
+ public static int getSuppressedVisualEffects(int index) {
+ return index * 2;
+ }
+
+ /**
+ * Produces an importance value, based on the provided index
+ */
+ public static int getImportance(int index) {
+ return index;
+ }
+
+ /**
+ * Produces an explanation value, based on the provided key
+ */
+ public static String getExplanation(String key) {
+ return key + "explain";
+ }
+
+ /**
+ * Produces a notification channel, based on the provided key and index
+ */
+ public static NotificationChannel getChannel(String key, int index) {
+ return new NotificationChannel(key, key, getImportance(index));
+ }
+
+ /**
+ * Produces a boolean that can be used to represent showBadge, based on the provided index
+ */
+ public static boolean getShowBadge(int index) {
+ return index % 3 == 0;
+ }
+
+ /**
+ * Produces a user sentiment value, based on the provided index
+ */
+ public static int getUserSentiment(int index) {
+ switch(index % 3) {
+ case 0:
+ return USER_SENTIMENT_NEGATIVE;
+ case 1:
+ return USER_SENTIMENT_NEUTRAL;
+ case 2:
+ return USER_SENTIMENT_POSITIVE;
+ }
+ return USER_SENTIMENT_NEUTRAL;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent "hidden," based on the provided index.
+ */
+ public static boolean getHidden(int index) {
+ return index % 2 == 0;
+ }
+
+ /**
+ * Produces a long to represent lastAudiblyAlerted based on the provided index.
+ */
+ public static long lastAudiblyAlerted(int index) {
+ return index * 2000L;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent "noisy," based on the provided index.
+ */
+ public static boolean getNoisy(int index) {
+ return index < 1;
+ }
+
+ /**
+ * Produces strings that can be used to represent people, based on the provided key and index.
+ */
+ public static ArrayList<String> getPeople(String key, int index) {
+ ArrayList<String> people = new ArrayList<>();
+ for (int i = 0; i < index; i++) {
+ people.add(i + key);
+ }
+ return people;
+ }
+
+ /**
+ * Produces a number of snoozeCriteria, based on the provided key and index.
+ */
+ public static ArrayList<SnoozeCriterion> getSnoozeCriteria(String key, int index) {
+ ArrayList<SnoozeCriterion> snooze = new ArrayList<>();
+ for (int i = 0; i < index; i++) {
+ snooze.add(new SnoozeCriterion(key + i, getExplanation(key), key));
+ }
+ return snooze;
+ }
+
+ /**
+ * Produces a list of Actions which can be used to represent smartActions.
+ * These actions are built from pending intents with intent titles based on the provided
+ * key, and ids based on the provided index.
+ */
+ public static ArrayList<Notification.Action> getSmartActions(String key,
+ int index,
+ TestableContext context) {
+ ArrayList<Notification.Action> actions = new ArrayList<>();
+ for (int i = 0; i < index; i++) {
+ PendingIntent intent = PendingIntent.getBroadcast(
+ context,
+ index /*requestCode*/,
+ new Intent("ACTION_" + key),
+ PendingIntent.FLAG_IMMUTABLE /*flags*/);
+ actions.add(new Notification.Action.Builder(null /*icon*/, key, intent).build());
+ }
+ return actions;
+ }
+
+ /**
+ * Produces index number of "smart replies," all based on the provided key and index
+ */
+ public static ArrayList<CharSequence> getSmartReplies(String key, int index) {
+ ArrayList<CharSequence> choices = new ArrayList<>();
+ for (int i = 0; i < index; i++) {
+ choices.add("choice_" + key + "_" + i);
+ }
+ return choices;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent canBubble, based on the provided index
+ */
+ public static boolean canBubble(int index) {
+ return index % 4 == 0;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent isTextChanged, based on the provided index.
+ */
+ public static boolean isTextChanged(int index) {
+ return index % 4 == 0;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent isConversation, based on the provided index.
+ */
+ public static boolean isConversation(int index) {
+ return index % 4 == 0;
+ }
+
+ /**
+ * Produces a ShortcutInfo value based on the provided index.
+ */
+ public static ShortcutInfo getShortcutInfo(int index) {
+ ShortcutInfo si = new ShortcutInfo(
+ index, String.valueOf(index), "packageName", new ComponentName("1", "1"), null,
+ "title", 0, "titleResName", "text", 0, "textResName",
+ "disabledMessage", 0, "disabledMessageResName",
+ null, null, 0, null, 0, 0,
+ 0, "iconResName", "bitmapPath", null, 0,
+ null, null, null, null);
+ return si;
+ }
+
+ /**
+ * Produces a rankingAdjustment value, based on the provided index.
+ */
+ public static int getRankingAdjustment(int index) {
+ return index % 3 - 1;
+ }
+
+ /**
+ * Produces a proposedImportance, based on the provided index.
+ */
+ public static int getProposedImportance(int index) {
+ return index % 5 - 1;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent hasSensitiveContent, based on the provided
+ * index.
+ */
+ public static boolean hasSensitiveContent(int index) {
+ return index % 3 == 0;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent isBubble, based on the provided index.
+ */
+ public static boolean isBubble(int index) {
+ return index % 4 == 0;
+ }
+
+ /**
+ * Checks that each of the pairs of actions in the two provided lists has identical titles,
+ * and that the lists have the same number of elements.
+ */
+ public void assertActionsEqual(
+ List<Notification.Action> expecteds, List<Notification.Action> actuals) {
+ Assert.assertEquals(expecteds.size(), actuals.size());
+ for (int i = 0; i < expecteds.size(); i++) {
+ Notification.Action expected = expecteds.get(i);
+ Notification.Action actual = actuals.get(i);
+ Assert.assertEquals(expected.title.toString(), actual.title.toString());
+ }
+ }
+
+ /**
+ * Checks that all subelements of the provided NotificationRankingUpdates are equal.
+ */
+ public void detailedAssertEquals(NotificationRankingUpdate a, NotificationRankingUpdate b) {
+ detailedAssertEquals(a.getRankingMap(), b.getRankingMap());
+ }
+
+ /**
+ * Checks that all subelements of the provided Ranking objects are equal.
+ */
+ public void detailedAssertEquals(String comment, NotificationListenerService.Ranking a,
+ NotificationListenerService.Ranking b) {
+ Assert.assertEquals(comment, a.getKey(), b.getKey());
+ Assert.assertEquals(comment, a.getRank(), b.getRank());
+ Assert.assertEquals(comment, a.matchesInterruptionFilter(), b.matchesInterruptionFilter());
+ Assert.assertEquals(comment, a.getLockscreenVisibilityOverride(),
+ b.getLockscreenVisibilityOverride());
+ Assert.assertEquals(comment, a.getSuppressedVisualEffects(),
+ b.getSuppressedVisualEffects());
+ Assert.assertEquals(comment, a.getImportance(), b.getImportance());
+ Assert.assertEquals(comment, a.getImportanceExplanation(), b.getImportanceExplanation());
+ Assert.assertEquals(comment, a.getOverrideGroupKey(), b.getOverrideGroupKey());
+ Assert.assertEquals(comment, a.getChannel().toString(), b.getChannel().toString());
+ Assert.assertEquals(comment, a.getAdditionalPeople(), b.getAdditionalPeople());
+ Assert.assertEquals(comment, a.getSnoozeCriteria(), b.getSnoozeCriteria());
+ Assert.assertEquals(comment, a.canShowBadge(), b.canShowBadge());
+ Assert.assertEquals(comment, a.getUserSentiment(), b.getUserSentiment());
+ Assert.assertEquals(comment, a.isSuspended(), b.isSuspended());
+ Assert.assertEquals(comment, a.getLastAudiblyAlertedMillis(),
+ b.getLastAudiblyAlertedMillis());
+ Assert.assertEquals(comment, a.isNoisy(), b.isNoisy());
+ Assert.assertEquals(comment, a.getSmartReplies(), b.getSmartReplies());
+ Assert.assertEquals(comment, a.canBubble(), b.canBubble());
+ Assert.assertEquals(comment, a.isConversation(), b.isConversation());
+ if (a.getConversationShortcutInfo() != null && b.getConversationShortcutInfo() != null) {
+ Assert.assertEquals(comment, a.getConversationShortcutInfo().getId(),
+ b.getConversationShortcutInfo().getId());
+ } else {
+ // One or both must be null, so we can check for equality.
+ Assert.assertEquals(a.getConversationShortcutInfo(), b.getConversationShortcutInfo());
+ }
+ assertActionsEqual(a.getSmartActions(), b.getSmartActions());
+ Assert.assertEquals(a.getProposedImportance(), b.getProposedImportance());
+ Assert.assertEquals(a.hasSensitiveContent(), b.hasSensitiveContent());
+ }
+
+ /**
+ * Checks that the two RankingMaps have identical keys, and that each Ranking object for
+ * each of those keys is identical.
+ */
+ public void detailedAssertEquals(NotificationListenerService.RankingMap a,
+ NotificationListenerService.RankingMap b) {
+ NotificationListenerService.Ranking arank = new NotificationListenerService.Ranking();
+ NotificationListenerService.Ranking brank = new NotificationListenerService.Ranking();
+ assertArrayEquals(a.getOrderedKeys(), b.getOrderedKeys());
+ for (String key : a.getOrderedKeys()) {
+ a.getRanking(key, arank);
+ b.getRanking(key, brank);
+ detailedAssertEquals("ranking for key <" + key + ">", arank, brank);
+ }
+ }
+
@Before
public void setUp() {
mNotificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "test channel",
@@ -74,8 +435,13 @@
SystemUiSystemPropertiesFlags.TEST_RESOLVER = null;
}
- public NotificationListenerService.Ranking createTestRanking(String key, int rank) {
+ /**
+ * Creates a mostly empty Test Ranking object with the specified key, rank, and smartActions.
+ */
+ public NotificationListenerService.Ranking createEmptyTestRanking(
+ String key, int rank, ArrayList<Notification.Action> actions) {
NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
+
ranking.populate(
/* key= */ key,
/* rank= */ rank,
@@ -93,7 +459,7 @@
/* hidden= */ false,
/* lastAudiblyAlertedMs= */ -1,
/* noisy= */ false,
- /* smartActions= */ null,
+ /* smartActions= */ actions,
/* smartReplies= */ null,
/* canBubble= */ false,
/* isTextChanged= */ false,
@@ -107,54 +473,111 @@
return ranking;
}
+ // Tests parceling of NotificationRankingUpdate, and by extension, RankingMap and Ranking.
@Test
- public void testRankingUpdate_rankingConstructor() {
- NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
- NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
- new NotificationListenerService.Ranking[]{ranking});
-
- NotificationListenerService.RankingMap retrievedRankings = rankingUpdate.getRankingMap();
- NotificationListenerService.Ranking retrievedRanking =
- new NotificationListenerService.Ranking();
- assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking));
- assertEquals(123, retrievedRanking.getRank());
- }
-
- @Test
- public void testRankingUpdate_parcelConstructor() {
- NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
- NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
- new NotificationListenerService.Ranking[]{ranking});
-
- Parcel parceledRankingUpdate = Parcel.obtain();
- rankingUpdate.writeToParcel(parceledRankingUpdate, 0);
- parceledRankingUpdate.setDataPosition(0);
-
- NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate(
- parceledRankingUpdate);
-
- NotificationListenerService.RankingMap retrievedRankings =
- retrievedRankingUpdate.getRankingMap();
- assertNotNull(retrievedRankings);
+ public void testRankingUpdate_parcel() {
+ NotificationRankingUpdate nru = generateUpdate(getContext());
+ Parcel parcel = Parcel.obtain();
+ nru.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ NotificationRankingUpdate nru1 = NotificationRankingUpdate.CREATOR.createFromParcel(parcel);
// The rankingUpdate file descriptor is only non-null in the new path.
if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(
SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) {
- assertTrue(retrievedRankingUpdate.isFdNotNullAndClosed());
+ assertTrue(nru1.isFdNotNullAndClosed());
}
- NotificationListenerService.Ranking retrievedRanking =
- new NotificationListenerService.Ranking();
- assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking));
- assertEquals(123, retrievedRanking.getRank());
- assertTrue(retrievedRankingUpdate.equals(rankingUpdate));
- parceledRankingUpdate.recycle();
+ detailedAssertEquals(nru, nru1);
+ parcel.recycle();
+ }
+
+ // Tests parceling of RankingMap and RankingMap.equals
+ @Test
+ public void testRankingMap_parcel() {
+ NotificationListenerService.RankingMap rmap = generateUpdate(getContext()).getRankingMap();
+ Parcel parcel = Parcel.obtain();
+ rmap.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ NotificationListenerService.RankingMap rmap1 =
+ NotificationListenerService.RankingMap.CREATOR.createFromParcel(parcel);
+
+ detailedAssertEquals(rmap, rmap1);
+ Assert.assertEquals(rmap, rmap1);
+ parcel.recycle();
+ }
+
+ // Tests parceling of Ranking and Ranking.equals
+ @Test
+ public void testRanking_parcel() {
+ NotificationListenerService.Ranking ranking =
+ generateUpdate(getContext()).getRankingMap().getRawRankingObject(mKeys[0]);
+ Parcel parcel = Parcel.obtain();
+ ranking.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ NotificationListenerService.Ranking ranking1 =
+ new NotificationListenerService.Ranking(parcel);
+ detailedAssertEquals("rankings differ: ", ranking, ranking1);
+ Assert.assertEquals(ranking, ranking1);
+ parcel.recycle();
+ }
+
+ // Tests NotificationRankingUpdate.equals(), and by extension, RankingMap and Ranking.
+ @Test
+ public void testRankingUpdate_equals_legacy() {
+ NotificationRankingUpdate nru = generateUpdate(getContext());
+ NotificationRankingUpdate nru2 = generateUpdate(getContext());
+ detailedAssertEquals(nru, nru2);
+ Assert.assertEquals(nru, nru2);
+ NotificationListenerService.Ranking tweak =
+ nru2.getRankingMap().getRawRankingObject(mKeys[0]);
+ tweak.populate(
+ tweak.getKey(),
+ tweak.getRank(),
+ !tweak.matchesInterruptionFilter(), // note the inversion here!
+ tweak.getLockscreenVisibilityOverride(),
+ tweak.getSuppressedVisualEffects(),
+ tweak.getImportance(),
+ tweak.getImportanceExplanation(),
+ tweak.getOverrideGroupKey(),
+ tweak.getChannel(),
+ (ArrayList) tweak.getAdditionalPeople(),
+ (ArrayList) tweak.getSnoozeCriteria(),
+ tweak.canShowBadge(),
+ tweak.getUserSentiment(),
+ tweak.isSuspended(),
+ tweak.getLastAudiblyAlertedMillis(),
+ tweak.isNoisy(),
+ (ArrayList) tweak.getSmartActions(),
+ (ArrayList) tweak.getSmartReplies(),
+ tweak.canBubble(),
+ tweak.isTextChanged(),
+ tweak.isConversation(),
+ tweak.getConversationShortcutInfo(),
+ tweak.getRankingAdjustment(),
+ tweak.isBubble(),
+ tweak.getProposedImportance(),
+ tweak.hasSensitiveContent()
+ );
+ assertNotEquals(nru, nru2);
+ }
+
+ @Test
+ public void testRankingUpdate_rankingConstructor() {
+ NotificationRankingUpdate nru = generateUpdate(getContext());
+ NotificationRankingUpdate constructedNru = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{
+ nru.getRankingMap().getRawRankingObject(mKeys[0]),
+ nru.getRankingMap().getRawRankingObject(mKeys[1]),
+ nru.getRankingMap().getRawRankingObject(mKeys[2]),
+ nru.getRankingMap().getRawRankingObject(mKeys[3]),
+ nru.getRankingMap().getRawRankingObject(mKeys[4])
+ });
+
+ detailedAssertEquals(nru, constructedNru);
}
@Test
public void testRankingUpdate_emptyParcelInCheck() {
- NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
- NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
- new NotificationListenerService.Ranking[]{ranking});
-
+ NotificationRankingUpdate rankingUpdate = generateUpdate(getContext());
Parcel parceledRankingUpdate = Parcel.obtain();
rankingUpdate.writeToParcel(parceledRankingUpdate, 0);
@@ -163,37 +586,119 @@
NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate(
parceledRankingUpdate);
assertNull(retrievedRankingUpdate.getRankingMap());
+ parceledRankingUpdate.recycle();
}
@Test
public void testRankingUpdate_describeContents() {
- NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
- NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
- new NotificationListenerService.Ranking[]{ranking});
+ NotificationRankingUpdate rankingUpdate = generateUpdate(getContext());
assertEquals(0, rankingUpdate.describeContents());
}
@Test
public void testRankingUpdate_equals() {
- NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
+ NotificationListenerService.Ranking ranking = createEmptyTestRanking(TEST_KEY, 123, null);
NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
new NotificationListenerService.Ranking[]{ranking});
- // Reflexive equality.
- assertTrue(rankingUpdate.equals(rankingUpdate));
- // Null or wrong class inequality.
+ // Reflexive equality, including handling nulls properly
+ detailedAssertEquals(rankingUpdate, rankingUpdate);
+ // Null or wrong class inequality
assertFalse(rankingUpdate.equals(null));
assertFalse(rankingUpdate.equals(ranking));
- // Different ranking contents inequality.
- NotificationListenerService.Ranking ranking2 = createTestRanking(TEST_KEY, 456);
+ // Different rank inequality
+ NotificationListenerService.Ranking ranking2 = createEmptyTestRanking(TEST_KEY, 456, null);
NotificationRankingUpdate rankingUpdate2 = new NotificationRankingUpdate(
new NotificationListenerService.Ranking[]{ranking2});
assertFalse(rankingUpdate.equals(rankingUpdate2));
- // Same ranking contents equality.
- ranking2 = createTestRanking(TEST_KEY, 123);
+ // Different key inequality
+ ranking2 = createEmptyTestRanking(TEST_KEY + "DIFFERENT", 123, null);
rankingUpdate2 = new NotificationRankingUpdate(
new NotificationListenerService.Ranking[]{ranking2});
- assertTrue(rankingUpdate.equals(rankingUpdate2));
+ assertFalse(rankingUpdate.equals(rankingUpdate2));
+ }
+
+ @Test
+ public void testRankingUpdate_writesSmartActionToParcel() {
+ if (!mRankingUpdateAshmem) {
+ return;
+ }
+ ArrayList<Notification.Action> actions = new ArrayList<>();
+ PendingIntent intent = PendingIntent.getBroadcast(
+ getContext(),
+ 0 /*requestCode*/,
+ new Intent("ACTION_" + TEST_KEY),
+ PendingIntent.FLAG_IMMUTABLE /*flags*/);
+ actions.add(new Notification.Action.Builder(null /*icon*/, TEST_KEY, intent).build());
+
+ NotificationListenerService.Ranking ranking =
+ createEmptyTestRanking(TEST_KEY, 123, actions);
+ NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking});
+
+ Parcel parcel = Parcel.obtain();
+ rankingUpdate.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ SharedMemory fd = parcel.readParcelable(getClass().getClassLoader(), SharedMemory.class);
+ Bundle smartActionsBundle = parcel.readBundle(getClass().getClassLoader());
+
+ // Assert the file descriptor is valid
+ assertNotNull(fd);
+ assertFalse(fd.getFd() == -1);
+
+ // Assert that the smart action is in the parcel
+ assertNotNull(smartActionsBundle);
+ ArrayList<Notification.Action> recoveredActions =
+ smartActionsBundle.getParcelableArrayList(TEST_KEY, Notification.Action.class);
+ assertNotNull(recoveredActions);
+ assertEquals(actions.size(), recoveredActions.size());
+ assertEquals(actions.get(0).title.toString(), recoveredActions.get(0).title.toString());
+ parcel.recycle();
+ }
+
+ @Test
+ public void testRankingUpdate_handlesEmptySmartActionList() {
+ if (!mRankingUpdateAshmem) {
+ return;
+ }
+ ArrayList<Notification.Action> actions = new ArrayList<>();
+ NotificationListenerService.Ranking ranking =
+ createEmptyTestRanking(TEST_KEY, 123, actions);
+ NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking});
+
+ Parcel parcel = Parcel.obtain();
+ rankingUpdate.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ // Ensure that despite an empty actions list, we can still unparcel the update.
+ NotificationRankingUpdate newRankingUpdate = new NotificationRankingUpdate(parcel);
+ assertNotNull(newRankingUpdate);
+ assertNotNull(newRankingUpdate.getRankingMap());
+ detailedAssertEquals(rankingUpdate, newRankingUpdate);
+ parcel.recycle();
+ }
+
+ @Test
+ public void testRankingUpdate_handlesNullSmartActionList() {
+ if (!mRankingUpdateAshmem) {
+ return;
+ }
+ NotificationListenerService.Ranking ranking =
+ createEmptyTestRanking(TEST_KEY, 123, null);
+ NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking});
+
+ Parcel parcel = Parcel.obtain();
+ rankingUpdate.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ // Ensure that despite an empty actions list, we can still unparcel the update.
+ NotificationRankingUpdate newRankingUpdate = new NotificationRankingUpdate(parcel);
+ assertNotNull(newRankingUpdate);
+ assertNotNull(newRankingUpdate.getRankingMap());
+ detailedAssertEquals(rankingUpdate, newRankingUpdate);
+ parcel.recycle();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java
index 931cf0c..c6c9b35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java
@@ -94,6 +94,10 @@
mCurrentIcon = icon;
// Remove old image while waiting for the new one to load.
mIconImageView.setImageDrawable(null);
+ if (icon.getType() == Icon.TYPE_URI || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+ // Disallow loading icon from content URI
+ return;
+ }
icon.loadDrawableAsync(mContext, d -> {
// The image hasn't been set any other way and the drawable belongs to the most
// recently set Icon.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 5e2c61b..b294866 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -143,6 +143,8 @@
import com.android.wm.shell.util.TransitionUtil;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -3109,6 +3111,7 @@
null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER);
}
+ @NeverCompile
@Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
diff --git a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
index d1bcb57..4936f88 100644
--- a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
@@ -5,4 +5,11 @@
namespace: "media_solutions"
description: "Gates whether to use a MediaRouter2-based implementation of InfoMediaManager, instead of the legacy MediaRouter2Manager-based implementation."
bug: "192657812"
+}
+
+flag {
+ name: "enable_tv_media_output_dialog"
+ namespace: "tv_system_ui"
+ description: "Gates all the changes for the tv specific media output dialog"
+ bug: "303205631"
}
\ No newline at end of file
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 248c60c..f5d9475 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -101,5 +101,7 @@
Settings.System.CAMERA_FLASH_NOTIFICATION,
Settings.System.SCREEN_FLASH_NOTIFICATION,
Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
+ Settings.System.PEAK_REFRESH_RATE,
+ Settings.System.MIN_REFRESH_RATE,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 29f27f7..410269f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -21,6 +21,7 @@
import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_FLOAT_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.URI_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.VIBRATION_INTENSITY_VALIDATOR;
@@ -236,5 +237,7 @@
VALIDATORS.put(System.CAMERA_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION_COLOR, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(System.PEAK_REFRESH_RATE, NON_NEGATIVE_FLOAT_VALIDATOR);
+ VALIDATORS.put(System.MIN_REFRESH_RATE, NON_NEGATIVE_FLOAT_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 34d3d44..46cd725 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -121,6 +121,7 @@
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FrameworkStatsLog;
import com.android.providers.settings.SettingsState.Setting;
@@ -3878,7 +3879,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 222;
+ private static final int SETTINGS_VERSION = 223;
private final int mUserId;
@@ -5935,10 +5936,6 @@
if (currentVersion == 218) {
// Version 219: Removed
- // TODO(b/211737588): Back up the Smooth Display setting
- // Future upgrades to the `peak_refresh_rate` and `min_refresh_rate` settings
- // should account for the database in a non-upgraded and upgraded (change id:
- // Ib2cb2dd100f06f5452083b7606109a486e795a0e) state.
currentVersion = 219;
}
@@ -6004,6 +6001,56 @@
currentVersion = 222;
}
+ // Version 222: Set peak refresh rate and min refresh rate to infinity if it's
+ // meant to be the highest possible refresh rate. This is needed so that we can
+ // back up and restore those settings on other devices. Other devices might have
+ // different highest possible refresh rates.
+ if (currentVersion == 222) {
+ final SettingsState systemSettings = getSystemSettingsLocked(userId);
+ final Setting peakRefreshRateSetting =
+ systemSettings.getSettingLocked(Settings.System.PEAK_REFRESH_RATE);
+ final Setting minRefreshRateSetting =
+ systemSettings.getSettingLocked(Settings.System.MIN_REFRESH_RATE);
+ float highestRefreshRate = RefreshRateSettingsUtils
+ .findHighestRefreshRateForDefaultDisplay(getContext());
+
+ if (!peakRefreshRateSetting.isNull()) {
+ try {
+ float peakRefreshRate =
+ Float.parseFloat(peakRefreshRateSetting.getValue());
+ if (Math.round(peakRefreshRate) == Math.round(highestRefreshRate)) {
+ systemSettings.insertSettingLocked(
+ Settings.System.PEAK_REFRESH_RATE,
+ String.valueOf(Float.POSITIVE_INFINITY),
+ /* tag= */ null,
+ /* makeDefault= */ false,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ } catch (NumberFormatException e) {
+ // Do nothing. Leave the value as is.
+ }
+ }
+
+ if (!minRefreshRateSetting.isNull()) {
+ try {
+ float minRefreshRate =
+ Float.parseFloat(minRefreshRateSetting.getValue());
+ if (Math.round(minRefreshRate) == Math.round(highestRefreshRate)) {
+ systemSettings.insertSettingLocked(
+ Settings.System.MIN_REFRESH_RATE,
+ String.valueOf(Float.POSITIVE_INFINITY),
+ /* tag= */ null,
+ /* makeDefault= */ false,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ } catch (NumberFormatException e) {
+ // Do nothing. Leave the value as is.
+ }
+ }
+
+ currentVersion = 223;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 093bde7..7bca944 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -98,8 +98,6 @@
Settings.System.VOLUME_VOICE, // deprecated since API 2?
Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
- Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities
- Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities
Settings.System.SCREEN_BRIGHTNESS_FLOAT,
Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index b00908f..c1bac31 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -15,7 +15,7 @@
-->
<!-- Extends Framelayout -->
-<com.android.systemui.statusbar.notification.row.FooterView
+<com.android.systemui.statusbar.notification.footer.ui.view.FooterView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
@@ -76,4 +76,4 @@
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
-</com.android.systemui.statusbar.notification.row.FooterView>
+</com.android.systemui.statusbar.notification.footer.ui.view.FooterView>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0ee5da2..6d54058 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -846,7 +846,7 @@
<!-- Amount the button should shake when it's not long-pressed for long enough. -->
<dimen name="keyguard_affordance_shake_amplitude">8dp</dimen>
- <dimen name="keyguard_affordance_horizontal_offset">32dp</dimen>
+ <dimen name="keyguard_affordance_horizontal_offset">16dp</dimen>
<dimen name="keyguard_affordance_vertical_offset">32dp</dimen>
<!-- Value should be at least sum of 'keyguard_affordance_width' +
'keyguard_affordance_horizontal_offset' -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 3f5ec7d..3bf1482 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -189,6 +189,8 @@
import com.android.systemui.util.Assert;
import com.android.systemui.util.settings.SecureSettings;
+import dalvik.annotation.optimization.NeverCompile;
+
import com.google.android.collect.Lists;
import java.io.PrintWriter;
@@ -4430,6 +4432,7 @@
}
@SuppressLint("MissingPermission")
+ @NeverCompile
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("KeyguardUpdateMonitor state:");
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 4af2c74..54dbf72 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -93,6 +93,8 @@
import com.android.systemui.util.concurrency.ThreadFactory;
import com.android.systemui.util.settings.SecureSettings;
+import dalvik.annotation.optimization.NeverCompile;
+
import kotlin.Pair;
import java.io.PrintWriter;
@@ -1088,6 +1090,7 @@
}
}
+ @NeverCompile
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("ScreenDecorations state:");
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index f6e0296..53b6879 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -15,7 +15,10 @@
class CommunalRepositoryImpl
@Inject
constructor(
- featureFlags: FeatureFlagsClassic,
+ private val featureFlags: FeatureFlagsClassic,
) : CommunalRepository {
- override val isCommunalEnabled = featureFlags.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED)
+ override val isCommunalEnabled: Boolean
+ get() =
+ featureFlags.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) &&
+ featureFlags.isEnabled(Flags.COMMUNAL_HUB)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 9fb8da3..04bb6ae 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -28,11 +28,13 @@
class CommunalInteractor
@Inject
constructor(
- communalRepository: CommunalRepository,
+ private val communalRepository: CommunalRepository,
widgetRepository: CommunalWidgetRepository,
) {
+
/** Whether communal features are enabled. */
- val isCommunalEnabled: Boolean = communalRepository.isCommunalEnabled
+ val isCommunalEnabled: Boolean
+ get() = communalRepository.isCommunalEnabled
/** A flow of info about the widget to be displayed, or null if widget is unavailable. */
val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = widgetRepository.stopwatchAppWidgetInfo
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 4b6ad6d..d5f0e64 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -56,6 +56,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.FlagsModule;
import com.android.systemui.keyboard.KeyboardModule;
+import com.android.systemui.keyevent.data.repository.KeyEventRepositoryModule;
import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.log.dagger.MonitorLog;
@@ -181,6 +182,7 @@
FalsingModule.class,
FlagsModule.class,
FooterActionsModule.class,
+ KeyEventRepositoryModule.class,
KeyboardModule.class,
KeyguardBlueprintModule.class,
LetterboxModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepository.kt b/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepository.kt
new file mode 100644
index 0000000..5bc5d0b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepository.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyevent.data.repository
+
+import android.view.KeyEvent
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.CommandQueue
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** Defines interface for classes that encapsulate application state for key event presses. */
+interface KeyEventRepository {
+ /** Observable for whether the power button key is pressed/down or not. */
+ val isPowerButtonDown: Flow<Boolean>
+}
+
+@SysUISingleton
+class KeyEventRepositoryImpl
+@Inject
+constructor(
+ val commandQueue: CommandQueue,
+) : KeyEventRepository {
+ override val isPowerButtonDown: Flow<Boolean> = conflatedCallbackFlow {
+ val callback =
+ object : CommandQueue.Callbacks {
+ override fun handleSystemKey(event: KeyEvent) {
+ if (event.keyCode == KeyEvent.KEYCODE_POWER) {
+ trySendWithFailureLogging(event.isDown, TAG, "updated isPowerButtonDown")
+ }
+ }
+ }
+ commandQueue.addCallback(callback)
+ awaitClose { commandQueue.removeCallback(callback) }
+ }
+
+ companion object {
+ private const val TAG = "KeyEventRepositoryImpl"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepositoryModule.kt
new file mode 100644
index 0000000..afba5db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepositoryModule.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyevent.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface KeyEventRepositoryModule {
+ @Binds fun keyEventRepository(impl: KeyEventRepositoryImpl): KeyEventRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt
index 3f2f67d..9949fa5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt
@@ -13,55 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.systemui.keyevent.domain.interactor
-import android.view.KeyEvent
-import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor
+import com.android.systemui.keyevent.data.repository.KeyEventRepository
import javax.inject.Inject
/**
- * Sends key events to the appropriate interactors and then acts upon key events that haven't
- * already been handled but should be handled by SystemUI.
+ * Business logic for all key event state. This includes all key events, regardless of whether
+ * they've been handled or not by a consumer.
+ *
+ * For key events that SysUI wants to properly handle, see [SysUIKeyEventHandler].
*/
@SysUISingleton
class KeyEventInteractor
@Inject
constructor(
- private val backActionInteractor: BackActionInteractor,
- private val keyguardKeyEventInteractor: KeyguardKeyEventInteractor,
+ repository: KeyEventRepository,
) {
- fun dispatchKeyEvent(event: KeyEvent): Boolean {
- if (keyguardKeyEventInteractor.dispatchKeyEvent(event)) {
- return true
- }
-
- when (event.keyCode) {
- KeyEvent.KEYCODE_BACK -> {
- if (event.handleAction()) {
- backActionInteractor.onBackRequested()
- }
- return true
- }
- }
- return false
- }
-
- fun interceptMediaKey(event: KeyEvent): Boolean {
- return keyguardKeyEventInteractor.interceptMediaKey(event)
- }
-
- fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
- return keyguardKeyEventInteractor.dispatchKeyEventPreIme(event)
- }
-
- companion object {
- // Most actions shouldn't be handled on the down event and instead handled on subsequent
- // key events like ACTION_UP.
- fun KeyEvent.handleAction(): Boolean {
- return action != KeyEvent.ACTION_DOWN
- }
- }
+ val isPowerButtonDown = repository.isPowerButtonDown
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandler.kt b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandler.kt
new file mode 100644
index 0000000..1febc79
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandler.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyevent.domain.interactor
+
+import android.view.KeyEvent
+import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor
+import javax.inject.Inject
+
+/**
+ * Sends key events to the appropriate interactors and then acts upon key events that haven't
+ * already been handled but should be handled by SystemUI.
+ *
+ * To observe any key event states, see [KeyEventInteractor].
+ */
+@SysUISingleton
+class SysUIKeyEventHandler
+@Inject
+constructor(
+ private val backActionInteractor: BackActionInteractor,
+ private val keyguardKeyEventInteractor: KeyguardKeyEventInteractor,
+) {
+ fun dispatchKeyEvent(event: KeyEvent): Boolean {
+ if (keyguardKeyEventInteractor.dispatchKeyEvent(event)) {
+ return true
+ }
+
+ when (event.keyCode) {
+ KeyEvent.KEYCODE_BACK -> {
+ if (event.handleAction()) {
+ backActionInteractor.onBackRequested()
+ }
+ return true
+ }
+ }
+ return false
+ }
+
+ fun interceptMediaKey(event: KeyEvent): Boolean {
+ return keyguardKeyEventInteractor.interceptMediaKey(event)
+ }
+
+ fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
+ return keyguardKeyEventInteractor.dispatchKeyEventPreIme(event)
+ }
+
+ companion object {
+ // Most actions shouldn't be handled on the down event and instead handled on subsequent
+ // key events like ACTION_UP.
+ fun KeyEvent.handleAction(): Boolean {
+ return action != KeyEvent.ACTION_DOWN
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
index ac012f8..6d08456 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
@@ -21,7 +21,7 @@
import android.view.KeyEvent
import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor.Companion.handleAction
+import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler.Companion.handleAction
import com.android.systemui.media.controls.util.MediaSessionLegacyHelperWrapper
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -39,7 +39,6 @@
constructor(
private val context: Context,
private val statusBarStateController: StatusBarStateController,
- private val keyguardInteractor: KeyguardInteractor,
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
private val shadeController: ShadeController,
private val mediaSessionLegacyHelperWrapper: MediaSessionLegacyHelperWrapper,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index abc30ef..c5a8375 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -47,6 +47,7 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.util.doOnEnd
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -402,6 +403,9 @@
KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
shakeAnimator.interpolator =
CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles)
+ shakeAnimator.doOnEnd {
+ view.translationX = 0f
+ }
shakeAnimator.start()
vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index f0d118c..99025ace 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -41,6 +41,7 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.util.doOnEnd
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
@@ -240,6 +241,9 @@
KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
shakeAnimator.interpolator =
CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles)
+ shakeAnimator.doOnEnd {
+ view.translationX = 0f
+ }
shakeAnimator.start()
vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 72aea04..2217509 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -33,6 +33,8 @@
import com.android.systemui.mediaprojection.appselector.data.ShellRecentTaskListProvider
import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRecentsViewController
import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider
+import com.android.systemui.mediaprojection.appselector.view.WindowMetricsProvider
+import com.android.systemui.mediaprojection.appselector.view.WindowMetricsProviderImpl
import com.android.systemui.mediaprojection.devicepolicy.MediaProjectionDevicePolicyModule
import com.android.systemui.mediaprojection.devicepolicy.PersonalProfile
import com.android.systemui.mediaprojection.permission.MediaProjectionPermissionActivity
@@ -106,6 +108,8 @@
impl: TaskPreviewSizeProvider
): DefaultLifecycleObserver
+ @Binds fun windowMetricsProvider(impl: WindowMetricsProviderImpl): WindowMetricsProvider
+
companion object {
@Provides
@MediaProjectionAppSelector
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
index 864d35a..c829471 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
@@ -19,8 +19,6 @@
import android.content.Context
import android.content.res.Configuration
import android.graphics.Rect
-import android.view.WindowInsets.Type
-import android.view.WindowManager
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope
@@ -36,7 +34,7 @@
@Inject
constructor(
private val context: Context,
- private val windowManager: WindowManager,
+ private val windowMetricsProvider: WindowMetricsProvider,
private val configurationController: ConfigurationController,
) : CallbackController<TaskPreviewSizeListener>, ConfigurationListener, DefaultLifecycleObserver {
@@ -62,17 +60,14 @@
}
private fun calculateSize(): Rect {
- val windowMetrics = windowManager.maximumWindowMetrics
- val maximumWindowHeight = windowMetrics.bounds.height()
- val width = windowMetrics.bounds.width()
+ val maxWindowBounds = windowMetricsProvider.maximumWindowBounds
+ val maximumWindowHeight = maxWindowBounds.height()
+ val width = maxWindowBounds.width()
var height = maximumWindowHeight
val isLargeScreen = isLargeScreen(context)
if (isLargeScreen) {
- val taskbarSize =
- windowManager.currentWindowMetrics.windowInsets
- .getInsets(Type.tappableElement())
- .bottom
+ val taskbarSize = windowMetricsProvider.currentWindowInsets.bottom
height -= taskbarSize
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProvider.kt
new file mode 100644
index 0000000..1932920
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProvider.kt
@@ -0,0 +1,30 @@
+package com.android.systemui.mediaprojection.appselector.view
+
+import android.graphics.Insets
+import android.graphics.Rect
+import android.view.WindowInsets
+import android.view.WindowManager
+import javax.inject.Inject
+
+/** Provides values related to window metrics. */
+interface WindowMetricsProvider {
+
+ val maximumWindowBounds: Rect
+
+ val currentWindowInsets: Insets
+}
+
+class WindowMetricsProviderImpl
+@Inject
+constructor(
+ private val windowManager: WindowManager,
+) : WindowMetricsProvider {
+ override val maximumWindowBounds: Rect
+ get() = windowManager.maximumWindowMetrics.bounds
+
+ override val currentWindowInsets: Insets
+ get() =
+ windowManager.currentWindowMetrics.windowInsets.getInsets(
+ WindowInsets.Type.tappableElement()
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
index 07846b5..3cdcb2c 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
@@ -24,6 +24,8 @@
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shared.system.QuickStepContract;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -108,6 +110,7 @@
}
}
+ @NeverCompile
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println("SysUiState state:");
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 564e984..2928cce 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -65,6 +65,8 @@
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.PrintWriter;
import java.util.Optional;
@@ -476,6 +478,7 @@
return mNavigationBars.get(mDisplayTracker.getDefaultDisplayId());
}
+ @NeverCompile
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("mIsLargeScreen=" + mIsLargeScreen);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index 202254b..4aad6a0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -72,6 +72,8 @@
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.util.Utils;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.function.Consumer;
@@ -934,6 +936,7 @@
return mListeningAndVisibilityLifecycleOwner;
}
+ @NeverCompile
@Override
public void dump(PrintWriter pw, String[] args) {
IndentingPrintWriter indentingPw = new IndentingPrintWriter(pw, /* singleIndent= */ " ");
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 2ef83dd..ba0cf08 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -230,6 +230,8 @@
import com.android.systemui.util.time.SystemClock;
import com.android.wm.shell.animation.FlingAnimationUtils;
+import dalvik.annotation.optimization.NeverCompile;
+
import kotlin.Unit;
import java.io.PrintWriter;
@@ -3379,6 +3381,7 @@
mBlockingExpansionForCurrentTouch = isTracking();
}
+ @NeverCompile
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println(TAG + ":");
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 5414b3f..d05dfe2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -48,7 +48,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor;
+import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.TransitionState;
@@ -106,7 +106,7 @@
private final NotificationInsetsController mNotificationInsetsController;
private final boolean mIsTrackpadCommonEnabled;
private final FeatureFlags mFeatureFlags;
- private final KeyEventInteractor mKeyEventInteractor;
+ private final SysUIKeyEventHandler mSysUIKeyEventHandler;
private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
private GestureDetector mPulsingWakeupGestureHandler;
@@ -185,7 +185,7 @@
SystemClock clock,
BouncerMessageInteractor bouncerMessageInteractor,
BouncerLogger bouncerLogger,
- KeyEventInteractor keyEventInteractor,
+ SysUIKeyEventHandler sysUIKeyEventHandler,
PrimaryBouncerInteractor primaryBouncerInteractor,
AlternateBouncerInteractor alternateBouncerInteractor) {
mLockscreenShadeTransitionController = transitionController;
@@ -214,7 +214,7 @@
mNotificationInsetsController = notificationInsetsController;
mIsTrackpadCommonEnabled = featureFlags.isEnabled(TRACKPAD_GESTURE_COMMON);
mFeatureFlags = featureFlags;
- mKeyEventInteractor = keyEventInteractor;
+ mSysUIKeyEventHandler = sysUIKeyEventHandler;
mPrimaryBouncerInteractor = primaryBouncerInteractor;
mAlternateBouncerInteractor = alternateBouncerInteractor;
@@ -529,17 +529,17 @@
@Override
public boolean interceptMediaKey(KeyEvent event) {
- return mKeyEventInteractor.interceptMediaKey(event);
+ return mSysUIKeyEventHandler.interceptMediaKey(event);
}
@Override
public boolean dispatchKeyEventPreIme(KeyEvent event) {
- return mKeyEventInteractor.dispatchKeyEventPreIme(event);
+ return mSysUIKeyEventHandler.dispatchKeyEventPreIme(event);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- return mKeyEventInteractor.dispatchKeyEvent(event);
+ return mSysUIKeyEventHandler.dispatchKeyEvent(event);
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 9b74ac4..3bbb2cf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -105,6 +105,8 @@
import com.android.systemui.util.LargeScreenUtils;
import com.android.systemui.util.kotlin.JavaAdapter;
+import dalvik.annotation.optimization.NeverCompile;
+
import dagger.Lazy;
import java.io.PrintWriter;
@@ -2015,6 +2017,7 @@
(int) ((y - getInitialTouchY()) / displayDensity), (int) (vel / displayDensity));
}
+ @NeverCompile
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println(TAG + ":");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 9db61c6..fc84973 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -83,6 +83,8 @@
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.CarrierConfigTracker;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -1154,6 +1156,7 @@
}
/** */
+ @NeverCompile
public void dump(PrintWriter pw, String[] args) {
pw.println("NetworkController state:");
pw.println(" mUserSetup=" + mUserSetup);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index 26db5f2..e74b3fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -11,10 +11,10 @@
* 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
+ * limitations under the License.
*/
-package com.android.systemui.statusbar.notification.row;
+package com.android.systemui.statusbar.notification.footer.ui.view;
import static android.graphics.PorterDuff.Mode.SRC_ATOP;
@@ -35,6 +35,8 @@
import com.android.settingslib.Utils;
import com.android.systemui.res.R;
+import com.android.systemui.statusbar.notification.row.FooterViewButton;
+import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.util.DumpUtilsKt;
@@ -93,6 +95,7 @@
updateColors();
}
+ /** Show a message instead of the footer buttons. */
public void setFooterLabelVisible(boolean isVisible) {
if (isVisible) {
mManageButton.setVisibility(View.GONE);
@@ -105,14 +108,22 @@
}
}
+ /** Set onClickListener for the manage/history button. */
public void setManageButtonClickListener(OnClickListener listener) {
mManageButton.setOnClickListener(listener);
}
+ /** Set onClickListener for the clear all (end) button. */
public void setClearAllButtonClickListener(OnClickListener listener) {
mClearAllButton.setOnClickListener(listener);
}
+ /**
+ * Whether the touch is outside the Clear all button.
+ *
+ * TODO(b/293167744): This is an artifact from the time when we could press underneath the
+ * shade to dismiss it. Check if it's safe to remove.
+ */
public boolean isOnEmptySpace(float touchX, float touchY) {
return touchX < mContent.getX()
|| touchX > mContent.getX() + mContent.getWidth()
@@ -120,6 +131,7 @@
|| touchY > mContent.getY() + mContent.getHeight();
}
+ /** Show "History" instead of "Manage" on the start button. */
public void showHistory(boolean showHistory) {
if (mShowHistory == showHistory) {
return;
@@ -141,6 +153,7 @@
.setCompoundDrawablesRelative(mSeenNotifsFilteredIcon, null, null, null);
}
+ /** Whether the start button shows "History" (true) or "Manage" (false). */
public boolean isHistoryShown() {
return mShowHistory;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt
index 197ae1a..dc9028d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt
@@ -25,6 +25,7 @@
import com.android.systemui.dump.DumpsysTableLogger
import com.android.systemui.dump.Row
import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import dalvik.annotation.optimization.NeverCompile
import java.io.PrintWriter
import javax.inject.Inject
@@ -39,6 +40,7 @@
Log.i("NotificationMemory", "Registered dumpable.")
}
+ @NeverCompile
override fun dump(pw: PrintWriter, args: Array<out String>) {
val memoryUse =
NotificationMemoryMeter.notificationMemoryUse(notificationPipeline.allNotifs)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index 0c686be..e200b90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -167,7 +167,7 @@
}
@VisibleForTesting
- boolean isSecondaryVisible() {
+ public boolean isSecondaryVisible() {
return mIsSecondaryVisible;
}
@@ -179,7 +179,8 @@
return mIsVisible;
}
- void setDuration(int duration) {
+ @VisibleForTesting
+ public void setDuration(int duration) {
mDuration = duration;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 78d7558..10b1bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -86,12 +86,12 @@
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.ExpandHelper;
-import com.android.systemui.res.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.TouchLogger;
import com.android.systemui.statusbar.CommandQueue;
@@ -106,12 +106,12 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
-import com.android.systemui.statusbar.notification.row.FooterView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 8ca1852..80f98a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -27,16 +27,16 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.SystemBarUtils;
import com.android.keyguard.BouncerPanelExpansionCalculator;
-import com.android.systemui.res.R;
import com.android.systemui.animation.ShadeInterpolation;
+import com.android.systemui.res.R;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.SourceType;
+import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
-import com.android.systemui.statusbar.notification.row.FooterView;
import java.util.ArrayList;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 6e6318e..f380ce5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -239,6 +239,8 @@
import dagger.Lazy;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
@@ -1763,6 +1765,7 @@
}
}
+ @NeverCompile
@Override
public void dump(PrintWriter pwOriginal, String[] args) {
IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index ea4d31b..d65a69c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -77,6 +77,8 @@
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.concurrency.ThreadFactory;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
@@ -286,6 +288,7 @@
return new MediaSessions(context, looper, callbacks);
}
+ @NeverCompile
public void dump(PrintWriter pw, String[] args) {
pw.println(VolumeDialogControllerImpl.class.getSimpleName() + " state:");
pw.print(" mVolumePolicy: "); pw.println(mVolumePolicy);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
index a7e7dd0..2b51ac5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
@@ -19,6 +19,7 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.core.animation.doOnEnd
+import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.doOnEnd
@@ -30,6 +31,7 @@
@RunWith(AndroidTestingRunner::class)
@SmallTest
@RunWithLooper
+@FlakyTest(bugId = 302149604)
class AnimatorTestRuleOrderTest : SysuiTestCase() {
@get:Rule val animatorTestRule = AnimatorTestRule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt
index 632d149..f373062 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt
@@ -16,23 +16,17 @@
package com.android.systemui.keyevent.domain.interactor
-import android.view.KeyEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.back.domain.interactor.BackActionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyevent.data.repository.FakeKeyEventRepository
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@SmallTest
@@ -40,108 +34,27 @@
class KeyEventInteractorTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
- private lateinit var keyguardInteractorWithDependencies:
- KeyguardInteractorFactory.WithDependencies
- @Mock private lateinit var keyguardKeyEventInteractor: KeyguardKeyEventInteractor
- @Mock private lateinit var backActionInteractor: BackActionInteractor
+ private lateinit var repository: FakeKeyEventRepository
private lateinit var underTest: KeyEventInteractor
@Before
fun setup() {
- keyguardInteractorWithDependencies = KeyguardInteractorFactory.create()
+ repository = FakeKeyEventRepository()
underTest =
KeyEventInteractor(
- backActionInteractor,
- keyguardKeyEventInteractor,
+ repository,
)
}
@Test
- fun dispatchBackKey_notHandledByKeyguardKeyEventInteractor_handledByBackActionInteractor() {
- val backKeyEventActionDown = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK)
- val backKeyEventActionUp = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK)
+ fun dispatchBackKey_notHandledByKeyguardKeyEventInteractor_handledByBackActionInteractor() =
+ runTest {
+ val isPowerDown by collectLastValue(underTest.isPowerButtonDown)
+ repository.setPowerButtonDown(false)
+ assertThat(isPowerDown).isFalse()
- // GIVEN back key ACTION_DOWN and ACTION_UP aren't handled by the keyguardKeyEventInteractor
- whenever(keyguardKeyEventInteractor.dispatchKeyEvent(backKeyEventActionDown))
- .thenReturn(false)
- whenever(keyguardKeyEventInteractor.dispatchKeyEvent(backKeyEventActionUp))
- .thenReturn(false)
-
- // WHEN back key event ACTION_DOWN, the event is handled even though back isn't requested
- assertThat(underTest.dispatchKeyEvent(backKeyEventActionDown)).isTrue()
- // THEN back event isn't handled on ACTION_DOWN
- verify(backActionInteractor, never()).onBackRequested()
-
- // WHEN back key event ACTION_UP
- assertThat(underTest.dispatchKeyEvent(backKeyEventActionUp)).isTrue()
- // THEN back event is handled on ACTION_UP
- verify(backActionInteractor).onBackRequested()
- }
-
- @Test
- fun dispatchKeyEvent_isNotHandledByKeyguardKeyEventInteractor() {
- val keyEvent =
- KeyEvent(
- KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_SPACE,
- )
- whenever(keyguardKeyEventInteractor.dispatchKeyEvent(eq(keyEvent))).thenReturn(false)
- assertThat(underTest.dispatchKeyEvent(keyEvent)).isFalse()
- }
-
- @Test
- fun dispatchKeyEvent_handledByKeyguardKeyEventInteractor() {
- val keyEvent =
- KeyEvent(
- KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_SPACE,
- )
- whenever(keyguardKeyEventInteractor.dispatchKeyEvent(eq(keyEvent))).thenReturn(true)
- assertThat(underTest.dispatchKeyEvent(keyEvent)).isTrue()
- }
-
- @Test
- fun interceptMediaKey_isNotHandledByKeyguardKeyEventInteractor() {
- val keyEvent =
- KeyEvent(
- KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_SPACE,
- )
- whenever(keyguardKeyEventInteractor.interceptMediaKey(eq(keyEvent))).thenReturn(false)
- assertThat(underTest.interceptMediaKey(keyEvent)).isFalse()
- }
-
- @Test
- fun interceptMediaKey_handledByKeyguardKeyEventInteractor() {
- val keyEvent =
- KeyEvent(
- KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_SPACE,
- )
- whenever(keyguardKeyEventInteractor.interceptMediaKey(eq(keyEvent))).thenReturn(true)
- assertThat(underTest.interceptMediaKey(keyEvent)).isTrue()
- }
-
- @Test
- fun dispatchKeyEventPreIme_isNotHandledByKeyguardKeyEventInteractor() {
- val keyEvent =
- KeyEvent(
- KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_SPACE,
- )
- whenever(keyguardKeyEventInteractor.dispatchKeyEventPreIme(eq(keyEvent))).thenReturn(false)
- assertThat(underTest.dispatchKeyEventPreIme(keyEvent)).isFalse()
- }
-
- @Test
- fun dispatchKeyEventPreIme_handledByKeyguardKeyEventInteractor() {
- val keyEvent =
- KeyEvent(
- KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_SPACE,
- )
- whenever(keyguardKeyEventInteractor.dispatchKeyEventPreIme(eq(keyEvent))).thenReturn(true)
- assertThat(underTest.dispatchKeyEventPreIme(keyEvent)).isTrue()
- }
+ repository.setPowerButtonDown(true)
+ assertThat(isPowerDown).isTrue()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandlerTest.kt
new file mode 100644
index 0000000..af00a48
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandlerTest.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyevent.domain.interactor
+
+import android.view.KeyEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SysUIKeyEventHandlerTest : SysuiTestCase() {
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+ private lateinit var keyguardInteractorWithDependencies:
+ KeyguardInteractorFactory.WithDependencies
+ @Mock private lateinit var keyguardKeyEventInteractor: KeyguardKeyEventInteractor
+ @Mock private lateinit var backActionInteractor: BackActionInteractor
+
+ private lateinit var underTest: SysUIKeyEventHandler
+
+ @Before
+ fun setup() {
+ keyguardInteractorWithDependencies = KeyguardInteractorFactory.create()
+ underTest =
+ SysUIKeyEventHandler(
+ backActionInteractor,
+ keyguardKeyEventInteractor,
+ )
+ }
+
+ @Test
+ fun dispatchBackKey_notHandledByKeyguardKeyEventInteractor_handledByBackActionInteractor() {
+ val backKeyEventActionDown = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK)
+ val backKeyEventActionUp = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK)
+
+ // GIVEN back key ACTION_DOWN and ACTION_UP aren't handled by the keyguardKeyEventInteractor
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(backKeyEventActionDown))
+ .thenReturn(false)
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(backKeyEventActionUp))
+ .thenReturn(false)
+
+ // WHEN back key event ACTION_DOWN, the event is handled even though back isn't requested
+ assertThat(underTest.dispatchKeyEvent(backKeyEventActionDown)).isTrue()
+ // THEN back event isn't handled on ACTION_DOWN
+ verify(backActionInteractor, never()).onBackRequested()
+
+ // WHEN back key event ACTION_UP
+ assertThat(underTest.dispatchKeyEvent(backKeyEventActionUp)).isTrue()
+ // THEN back event is handled on ACTION_UP
+ verify(backActionInteractor).onBackRequested()
+ }
+
+ @Test
+ fun dispatchKeyEvent_isNotHandledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(eq(keyEvent))).thenReturn(false)
+ assertThat(underTest.dispatchKeyEvent(keyEvent)).isFalse()
+ }
+
+ @Test
+ fun dispatchKeyEvent_handledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(eq(keyEvent))).thenReturn(true)
+ assertThat(underTest.dispatchKeyEvent(keyEvent)).isTrue()
+ }
+
+ @Test
+ fun interceptMediaKey_isNotHandledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.interceptMediaKey(eq(keyEvent))).thenReturn(false)
+ assertThat(underTest.interceptMediaKey(keyEvent)).isFalse()
+ }
+
+ @Test
+ fun interceptMediaKey_handledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.interceptMediaKey(eq(keyEvent))).thenReturn(true)
+ assertThat(underTest.interceptMediaKey(keyEvent)).isTrue()
+ }
+
+ @Test
+ fun dispatchKeyEventPreIme_isNotHandledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEventPreIme(eq(keyEvent))).thenReturn(false)
+ assertThat(underTest.dispatchKeyEventPreIme(keyEvent)).isFalse()
+ }
+
+ @Test
+ fun dispatchKeyEventPreIme_handledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEventPreIme(eq(keyEvent))).thenReturn(true)
+ assertThat(underTest.dispatchKeyEventPreIme(keyEvent)).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
index 900413c..e10815d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
@@ -59,8 +59,6 @@
KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP)
private val backKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK)
- private lateinit var keyguardInteractorWithDependencies:
- KeyguardInteractorFactory.WithDependencies
private lateinit var powerInteractor: PowerInteractor
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@@ -75,14 +73,12 @@
fun setup() {
whenever(mediaSessionLegacyHelperWrapper.getHelper(any()))
.thenReturn(mediaSessionLegacyHelper)
- keyguardInteractorWithDependencies = KeyguardInteractorFactory.create()
powerInteractor = PowerInteractorFactory.create().powerInteractor
underTest =
KeyguardKeyEventInteractor(
context,
statusBarStateController,
- keyguardInteractorWithDependencies.keyguardInteractor,
statusBarKeyguardViewManager,
shadeController,
mediaSessionLegacyHelperWrapper,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
index 906420d..9630ee3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
@@ -46,6 +46,7 @@
private val mockContext = mock<Context>()
private val resources = mock<Resources>()
private val windowManager = mock<WindowManager>()
+ private val windowMetricsProvider = mock<WindowMetricsProvider>()
private val sizeUpdates = arrayListOf<Rect>()
private val testConfigurationController = FakeConfigurationController()
@@ -112,13 +113,12 @@
}
private fun givenTaskbarSize(size: Int) {
- val windowInsets =
- WindowInsets.Builder()
- .setInsets(Type.tappableElement(), Insets.of(Rect(0, 0, 0, size)))
- .build()
+ val insets = Insets.of(Rect(0, 0, 0, size))
+ val windowInsets = WindowInsets.Builder().setInsets(Type.tappableElement(), insets).build()
val windowMetrics = WindowMetrics(windowManager.maximumWindowMetrics.bounds, windowInsets)
whenever(windowManager.maximumWindowMetrics).thenReturn(windowMetrics)
whenever(windowManager.currentWindowMetrics).thenReturn(windowMetrics)
+ whenever(windowMetricsProvider.currentWindowInsets).thenReturn(insets)
}
private fun givenDisplay(width: Int, height: Int, isTablet: Boolean = false) {
@@ -126,6 +126,7 @@
val windowMetrics = WindowMetrics(bounds, { null }, 1.0f)
whenever(windowManager.maximumWindowMetrics).thenReturn(windowMetrics)
whenever(windowManager.currentWindowMetrics).thenReturn(windowMetrics)
+ whenever(windowMetricsProvider.maximumWindowBounds).thenReturn(bounds)
val minDimension = min(width, height)
@@ -147,7 +148,11 @@
}
}
- return TaskPreviewSizeProvider(mockContext, windowManager, testConfigurationController)
+ return TaskPreviewSizeProvider(
+ mockContext,
+ windowMetricsProvider,
+ testConfigurationController
+ )
.also { it.addCallback(listener) }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProviderImplTest.kt
new file mode 100644
index 0000000..fe18454
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProviderImplTest.kt
@@ -0,0 +1,44 @@
+package com.android.systemui.mediaprojection.appselector.view
+
+import android.graphics.Insets
+import android.graphics.Rect
+import android.view.WindowManager
+import android.view.WindowMetrics
+import androidx.core.view.WindowInsetsCompat
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+@SmallTest
+class WindowMetricsProviderImplTest : SysuiTestCase() {
+
+ private val windowManager = mock<WindowManager>()
+ private val windowMetricsProvider = WindowMetricsProviderImpl(windowManager)
+
+ @Test
+ fun getMaximumWindowBounds_returnsValueFromWMMaxWindowMetrics() {
+ val bounds = Rect(/* left= */ 100, /* top= */ 200, /* right= */ 300, /* bottom= */ 400)
+ val metrics =
+ WindowMetrics(bounds, /* windowInsetsSupplier= */ { null }, /* density= */ 1.0f)
+ whenever(windowManager.maximumWindowMetrics).thenReturn(metrics)
+
+ assertThat(windowMetricsProvider.maximumWindowBounds).isEqualTo(bounds)
+ }
+
+ @Test
+ fun getCurrentWindowInsets_returnsFromWMCurrentWindowMetrics() {
+ val bounds = Rect()
+ val insets =
+ Insets.of(Rect(/* left= */ 123, /* top= */ 456, /* right= */ 789, /* bottom= */ 1012))
+ val windowInsets =
+ android.view.WindowInsets.Builder()
+ .setInsets(WindowInsetsCompat.Type.tappableElement(), insets)
+ .build()
+ whenever(windowManager.currentWindowMetrics).thenReturn(WindowMetrics(bounds, windowInsets))
+
+ assertThat(windowMetricsProvider.currentWindowInsets).isEqualTo(insets)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index b4f9e8d..677d9db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -49,7 +49,7 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.flags.SystemPropertiesHelper
-import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
+import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
@@ -144,7 +144,7 @@
@Mock lateinit var dragDownHelper: DragDownHelper
@Mock
lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
- @Mock lateinit var keyEventInteractor: KeyEventInteractor
+ @Mock lateinit var sysUIKeyEventHandler: SysUIKeyEventHandler
@Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
@Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
private val notificationExpansionRepository = NotificationExpansionRepository()
@@ -251,7 +251,7 @@
securityModel = mock(KeyguardSecurityModel::class.java),
),
BouncerLogger(logcatLogBuffer("BouncerLog")),
- keyEventInteractor,
+ sysUIKeyEventHandler,
primaryBouncerInteractor,
alternateBouncerInteractor,
)
@@ -475,21 +475,21 @@
fun forwardsDispatchKeyEvent() {
val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)
interactionEventHandler.dispatchKeyEvent(keyEvent)
- verify(keyEventInteractor).dispatchKeyEvent(keyEvent)
+ verify(sysUIKeyEventHandler).dispatchKeyEvent(keyEvent)
}
@Test
fun forwardsDispatchKeyEventPreIme() {
val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)
interactionEventHandler.dispatchKeyEventPreIme(keyEvent)
- verify(keyEventInteractor).dispatchKeyEventPreIme(keyEvent)
+ verify(sysUIKeyEventHandler).dispatchKeyEventPreIme(keyEvent)
}
@Test
fun forwardsInterceptMediaKey() {
val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP)
interactionEventHandler.interceptMediaKey(keyEvent)
- verify(keyEventInteractor).interceptMediaKey(keyEvent)
+ verify(sysUIKeyEventHandler).interceptMediaKey(keyEvent)
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 189c9e2..a4a2ca0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -48,7 +48,7 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.flags.SystemPropertiesHelper
-import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
+import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
@@ -253,7 +253,7 @@
securityModel = Mockito.mock(KeyguardSecurityModel::class.java),
),
BouncerLogger(logcatLogBuffer("BouncerLog")),
- Mockito.mock(KeyEventInteractor::class.java),
+ Mockito.mock(SysUIKeyEventHandler::class.java),
primaryBouncerInteractor,
alternateBouncerInteractor,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
index b120c47..f72142f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
@@ -11,10 +11,10 @@
* 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
+ * limitations under the License.
*/
-package com.android.systemui.statusbar.notification.row;
+package com.android.systemui.statusbar.notification.footer.ui.view;
import static com.google.common.truth.Truth.assertThat;
@@ -31,8 +31,8 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 236bcb4..a2be8b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -65,12 +65,12 @@
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.ExpandHelper;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.EmptyShadeView;
@@ -81,9 +81,9 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.FooterView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 93faa77..49906dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -5,18 +5,18 @@
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation.getContentAlpha
import com.android.systemui.dump.DumpManager
+import com.android.systemui.res.R
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.EmptyShadeView
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
+import com.android.systemui.statusbar.notification.footer.ui.view.FooterView.FooterViewState
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
-import com.android.systemui.statusbar.notification.row.FooterView
-import com.android.systemui.statusbar.notification.row.FooterView.FooterViewState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Expect
diff --git a/packages/SystemUI/tests/utils/src/android/animation/PlatformAnimatorIsolationRule.kt b/packages/SystemUI/tests/utils/src/android/animation/PlatformAnimatorIsolationRule.kt
index 43a26f3..ca5e1d0 100644
--- a/packages/SystemUI/tests/utils/src/android/animation/PlatformAnimatorIsolationRule.kt
+++ b/packages/SystemUI/tests/utils/src/android/animation/PlatformAnimatorIsolationRule.kt
@@ -41,7 +41,7 @@
private fun onError() =
exceptionDeferrer.fail(
"Test's animations are not isolated! " +
- "Did you forget to add an AnimatorTestRule to your test class?"
+ "Did you forget to add an AnimatorTestRule as a @Rule?"
)
fun throwDeferred() = exceptionDeferrer.throwDeferred()
diff --git a/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt b/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt
index 7a97029..95335a6 100644
--- a/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt
+++ b/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt
@@ -37,7 +37,7 @@
private fun onError() =
exceptionDeferrer.fail(
"Test's animations are not isolated! " +
- "Did you forget to add an AnimatorTestRule to your test class?"
+ "Did you forget to add an AnimatorTestRule as a @Rule?"
)
fun throwDeferred() = exceptionDeferrer.throwDeferred()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index d6632a3..f7e0120 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -15,8 +15,6 @@
*/
package com.android.systemui;
-import static com.android.systemui.animation.FakeDialogLaunchAnimatorKt.fakeDialogLaunchAnimator;
-
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -37,19 +35,15 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.FakeBroadcastDispatcher;
import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
-import org.junit.ClassRule;
import org.junit.Rule;
import org.mockito.Mockito;
@@ -69,8 +63,8 @@
private Handler mHandler;
// set the lowest order so it's the outermost rule
- @ClassRule(order = Integer.MIN_VALUE)
- public static AndroidXAnimatorIsolationRule mAndroidXAnimatorIsolationRule =
+ @Rule(order = Integer.MIN_VALUE)
+ public AndroidXAnimatorIsolationRule mAndroidXAnimatorIsolationRule =
new AndroidXAnimatorIsolationRule();
@Rule
@@ -94,10 +88,7 @@
if (isRobolectricTest()) {
mContext = mContext.createDefaultDisplayContext();
}
- SystemUIInitializer initializer = new SystemUIInitializerImpl(mContext);
- initializer.init(true);
- mDependency = new TestableDependency(initializer.getSysUIComponent().createDependency());
- Dependency.setInstance(mDependency);
+ mDependency = SysuiTestDependencyKt.installSysuiTestDependency(mContext);
mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(
mContext,
mContext.getMainExecutor(),
@@ -124,13 +115,6 @@
// reference and are never sent to the Context. This will also prevent a real
// BroadcastDispatcher from actually registering receivers.
mDependency.injectTestDependency(BroadcastDispatcher.class, mFakeBroadcastDispatcher);
- mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
-
- // Make sure that all tests on any SystemUIDialog does not crash because this dependency
- // is missing (constructing the actual one would throw).
- // TODO(b/219008720): Remove this.
- mDependency.injectMockDependency(SystemUIDialogManager.class);
- mDependency.injectTestDependency(DialogLaunchAnimator.class, fakeDialogLaunchAnimator());
}
protected boolean shouldFailOnLeakedReceiver() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt
new file mode 100644
index 0000000..c791f4f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt
@@ -0,0 +1,26 @@
+package com.android.systemui
+
+import android.annotation.SuppressLint
+import android.content.Context
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.animation.fakeDialogLaunchAnimator
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
+
+@SuppressLint("VisibleForTests")
+fun installSysuiTestDependency(context: Context): TestableDependency {
+ val initializer: SystemUIInitializer = SystemUIInitializerImpl(context)
+ initializer.init(true)
+
+ val dependency = TestableDependency(initializer.sysUIComponent.createDependency())
+ Dependency.setInstance(dependency)
+
+ dependency.injectMockDependency(KeyguardUpdateMonitor::class.java)
+
+ // Make sure that all tests on any SystemUIDialog does not crash because this dependency
+ // is missing (constructing the actual one would throw).
+ // TODO(b/219008720): Remove this.
+ dependency.injectMockDependency(SystemUIDialogManager::class.java)
+ dependency.injectTestDependency(DialogLaunchAnimator::class.java, fakeDialogLaunchAnimator())
+ return dependency
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyevent/data/repository/FakeKeyEventRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyevent/data/repository/FakeKeyEventRepository.kt
new file mode 100644
index 0000000..95b5316
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyevent/data/repository/FakeKeyEventRepository.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyevent.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeKeyEventRepository() : KeyEventRepository {
+ private val _isPowerButtonDown = MutableStateFlow(false)
+ override val isPowerButtonDown: Flow<Boolean> = _isPowerButtonDown.asStateFlow()
+
+ fun setPowerButtonDown(isDown: Boolean) {
+ _isPowerButtonDown.value = isDown
+ }
+}
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index 123b65c..b37bbd6 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -8,6 +8,13 @@
}
flag {
+ name: "fill_fields_from_current_session_only"
+ namespace: "autofill"
+ description: "Only fill autofill fields that are part of the current session."
+ bug: "270722825"
+}
+
+flag {
name: "relayout"
namespace: "autofill"
description: "Mitigation for relayout issue"
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index c7b53c5..3e13499 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -58,6 +58,7 @@
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.autofill.FillEventHistory;
+import android.service.autofill.Flags;
import android.service.autofill.UserData;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
@@ -226,6 +227,9 @@
@GuardedBy("mFlagLock")
private int mMaxInputLengthForAutofill;
+ @GuardedBy("mFlagLock")
+ private boolean mIsFillFieldsFromCurrentSessionOnly;
+
// Default flag values for Autofill PCC
private static final String DEFAULT_PCC_FEATURE_PROVIDER_HINTS = "";
@@ -701,6 +705,7 @@
DeviceConfig.NAMESPACE_AUTOFILL,
AutofillFeatureFlags.DEVICE_CONFIG_MAX_INPUT_LENGTH_FOR_AUTOFILL,
AutofillFeatureFlags.DEFAULT_MAX_INPUT_LENGTH_FOR_AUTOFILL);
+ mIsFillFieldsFromCurrentSessionOnly = Flags.fillFieldsFromCurrentSessionOnly();
if (verbose) {
Slog.v(mTag, "setDeviceConfigProperties() for PCC: "
+ "mPccClassificationEnabled=" + mPccClassificationEnabled
@@ -1004,6 +1009,15 @@
}
}
+ /**
+ * Return if autofill should only fill in fields from current session.
+ */
+ public boolean getIsFillFieldsFromCurrentSessionOnly() {
+ synchronized (mFlagLock) {
+ return mIsFillFieldsFromCurrentSessionOnly;
+ }
+ }
+
@Nullable
@VisibleForTesting
static Map<String, String[]> getAllowedCompatModePackages(String setting) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 265ed46..ae14877 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -6142,9 +6142,17 @@
continue;
}
final AutofillId viewId = dataset.getFieldIds().get(i);
+ final ViewState viewState = mViewStates.get(viewId);
+ if (mService.getMaster().getIsFillFieldsFromCurrentSessionOnly()
+ && viewState != null && viewState.id.getSessionId() != id) {
+ if (sVerbose) {
+ Slog.v(TAG, "Skipping filling view: " +
+ viewId + " as it isn't part of the current session: " + id);
+ }
+ continue;
+ }
ids.add(viewId);
values.add(dataset.getFieldValues().get(i));
- final ViewState viewState = mViewStates.get(viewId);
if (viewState != null
&& (viewState.getState() & ViewState.STATE_WAITING_DATASET_AUTH) != 0) {
if (sVerbose) {
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index cd87908..8df5456 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1428,6 +1428,12 @@
@UserIdInt int userId);
/**
+ * Sends the PACKAGE_RESTARTED broadcast on the package manager handler thread.
+ */
+ public abstract void sendPackageRestartedBroadcast(@NonNull String packageName,
+ int uid, @Intent.Flags int flags);
+
+ /**
* Return a list of all historical install sessions for the given user.
*/
public abstract ParceledListSlice<PackageInstaller.SessionInfo> getHistoricalSessions(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d3ce47c..19879db 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4160,26 +4160,34 @@
@GuardedBy("this")
private void finishForceStopPackageLocked(final String packageName, int uid) {
- Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
- Uri.fromParts("package", packageName, null));
+ int flags = 0;
if (!mProcessesReady) {
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
+ flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND;
}
- final int userId = UserHandle.getUserId(uid);
- final int[] broadcastAllowList =
- getPackageManagerInternal().getVisibilityAllowList(packageName, userId);
- intent.putExtra(Intent.EXTRA_UID, uid);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- broadcastIntentLocked(null /* callerApp */, null /* callerPackage */,
- null /* callerFeatureId */, intent, null /* resolvedType */,
- null /* resultToApp */, null /* resultTo */,
- 0 /* resultCode */, null /* resultData */, null /* resultExtras */,
- null /* requiredPermissions */, null /* excludedPermissions */,
- null /* excludedPackages */, OP_NONE, null /* bOptions */, false /* ordered */,
- false /* sticky */, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
- Binder.getCallingPid(), userId, BackgroundStartPrivileges.NONE,
- broadcastAllowList, null /* filterExtrasForReceiver */);
+ if (android.content.pm.Flags.stayStopped()) {
+ // Sent async using the PM handler, to maintain ordering with PACKAGE_UNSTOPPED
+ mPackageManagerInt.sendPackageRestartedBroadcast(packageName,
+ uid, flags);
+ } else {
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
+ Uri.fromParts("package", packageName, null));
+ intent.addFlags(flags);
+ final int userId = UserHandle.getUserId(uid);
+ final int[] broadcastAllowList =
+ getPackageManagerInternal().getVisibilityAllowList(packageName, userId);
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ broadcastIntentLocked(null /* callerApp */, null /* callerPackage */,
+ null /* callerFeatureId */, intent, null /* resolvedType */,
+ null /* resultToApp */, null /* resultTo */,
+ 0 /* resultCode */, null /* resultData */, null /* resultExtras */,
+ null /* requiredPermissions */, null /* excludedPermissions */,
+ null /* excludedPackages */, OP_NONE, null /* bOptions */, false /* ordered */,
+ false /* sticky */, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
+ Binder.getCallingPid(), userId, BackgroundStartPrivileges.NONE,
+ broadcastAllowList, null /* filterExtrasForReceiver */);
+ }
}
private void cleanupDisabledPackageComponentsLocked(
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index e66fa5b..f7b9869 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -55,6 +55,10 @@
Flags.FLAG_ENABLE_MODE_LIMIT_FOR_EXTERNAL_DISPLAY,
Flags::enableModeLimitForExternalDisplay);
+ private final FlagState mBackUpSmoothDisplayAndForcePeakRefreshRateFlagState = new FlagState(
+ Flags.FLAG_BACK_UP_SMOOTH_DISPLAY_AND_FORCE_PEAK_REFRESH_RATE,
+ Flags::backUpSmoothDisplayAndForcePeakRefreshRate);
+
/** Returns whether connected display management is enabled or not. */
public boolean isConnectedDisplayManagementEnabled() {
return mConnectedDisplayManagementFlagState.isEnabled();
@@ -108,6 +112,10 @@
return mDisplayOffloadFlagState.isEnabled();
}
+ public boolean isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled() {
+ return mBackUpSmoothDisplayAndForcePeakRefreshRateFlagState.isEnabled();
+ }
+
private static class FlagState {
private final String mName;
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 542f26c..c24b3ca 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
@@ -72,3 +72,11 @@
bug: "299521647"
is_fixed_read_only: true
}
+
+flag {
+ name: "back_up_smooth_display_and_force_peak_refresh_rate"
+ namespace: "display_manager"
+ description: "Feature flag for backing up Smooth Display and Force Peak Refresh Rate"
+ bug: "211737588"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 71ea8cc..ca23844 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -22,6 +22,7 @@
import static android.view.Display.Mode.INVALID_MODE_ID;
import static com.android.server.display.DisplayDeviceConfig.DEFAULT_LOW_REFRESH_RATE;
+import static com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay;
import android.annotation.IntegerRes;
import android.annotation.NonNull;
@@ -176,6 +177,8 @@
private final boolean mIsDisplaysRefreshRatesSynchronizationEnabled;
+ private final boolean mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled;
+
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
@NonNull DisplayManagerFlags displayManagerFlags) {
this(context, handler, new RealInjector(context), displayManagerFlags);
@@ -191,6 +194,8 @@
.isExternalDisplayLimitModeEnabled();
mIsDisplaysRefreshRatesSynchronizationEnabled = displayManagerFlags
.isDisplaysRefreshRatesSynchronizationEnabled();
+ mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled = displayManagerFlags
+ .isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled();
mContext = context;
mHandler = new DisplayModeDirectorHandler(handler.getLooper());
mInjector = injector;
@@ -1193,8 +1198,7 @@
public void observe() {
final ContentResolver cr = mContext.getContentResolver();
mInjector.registerPeakRefreshRateObserver(cr, this);
- cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
- UserHandle.USER_SYSTEM);
+ mInjector.registerMinRefreshRateObserver(cr, this);
cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
UserHandle.USER_SYSTEM);
cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/,
@@ -1292,10 +1296,34 @@
private void updateRefreshRateSettingLocked() {
final ContentResolver cr = mContext.getContentResolver();
+ float highestRefreshRate = findHighestRefreshRateForDefaultDisplay(mContext);
+
float minRefreshRate = Settings.System.getFloatForUser(cr,
Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
+ if (Float.isInfinite(minRefreshRate)) {
+ // Infinity means that we want the highest possible refresh rate
+ minRefreshRate = highestRefreshRate;
+
+ if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) {
+ // The flag had been turned off, we need to restore the original value
+ Settings.System.putFloatForUser(cr,
+ Settings.System.MIN_REFRESH_RATE, minRefreshRate, cr.getUserId());
+ }
+ }
+
float peakRefreshRate = Settings.System.getFloatForUser(cr,
Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate, cr.getUserId());
+ if (Float.isInfinite(peakRefreshRate)) {
+ // Infinity means that we want the highest possible refresh rate
+ peakRefreshRate = highestRefreshRate;
+
+ if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) {
+ // The flag had been turned off, we need to restore the original value
+ Settings.System.putFloatForUser(cr,
+ Settings.System.PEAK_REFRESH_RATE, peakRefreshRate, cr.getUserId());
+ }
+ }
+
updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
}
@@ -3082,6 +3110,7 @@
interface Injector {
Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
+ Uri MIN_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE);
@NonNull
DeviceConfigInterface getDeviceConfig();
@@ -3089,6 +3118,9 @@
void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer);
+ void registerMinRefreshRateObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer);
+
void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
Handler handler);
@@ -3140,6 +3172,13 @@
}
@Override
+ public void registerMinRefreshRateObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ cr.registerContentObserver(MIN_REFRESH_RATE_URI, false /*notifyDescendants*/,
+ observer, UserHandle.USER_SYSTEM);
+ }
+
+ @Override
public void registerDisplayListener(DisplayManager.DisplayListener listener,
Handler handler) {
getDisplayManager().registerDisplayListener(listener, handler);
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index f987629..79cd2a0 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -489,6 +489,9 @@
boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId);
+ /** Check if the package is in a stopped state for a given user. */
+ boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId);
+
boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, @UserIdInt int userId);
@NonNull
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 5d2944e..7db7bf5 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -4938,7 +4938,7 @@
int userId) {
final int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
- false /* checkShell */, "isPackageSuspendedForUser for user " + userId);
+ false /* checkShell */, "when asking about packages for user " + userId);
final PackageStateInternal ps = mSettings.getPackage(packageName);
if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) {
throw new IllegalArgumentException("Unknown target package: " + packageName);
@@ -4957,6 +4957,11 @@
}
@Override
+ public boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId) {
+ return getUserStageOrDefaultForUser(packageName, userId).isStopped();
+ }
+
+ @Override
public boolean isSuspendingAnyPackages(@NonNull String suspendingPackage,
@UserIdInt int userId) {
for (final PackageStateInternal packageState : getPackageStates().values()) {
diff --git a/services/core/java/com/android/server/pm/IPackageManagerBase.java b/services/core/java/com/android/server/pm/IPackageManagerBase.java
index 76203ac..9a0306b 100644
--- a/services/core/java/com/android/server/pm/IPackageManagerBase.java
+++ b/services/core/java/com/android/server/pm/IPackageManagerBase.java
@@ -961,6 +961,12 @@
}
@Override
+ public final boolean isPackageStoppedForUser(@NonNull String packageName,
+ @UserIdInt int userId) {
+ return snapshot().isPackageStoppedForUser(packageName, userId);
+ }
+
+ @Override
@Deprecated
public final boolean isSafeMode() {
// allow instant applications
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a99a0c0..6260dd5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4498,6 +4498,7 @@
boolean stopped, @UserIdInt int userId) {
if (!mUserManager.exists(userId)) return;
final int callingUid = Binder.getCallingUid();
+ boolean wasStopped = false;
if (snapshot.getInstantAppPackageName(callingUid) == null) {
final int permission = mContext.checkCallingOrSelfPermission(
Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
@@ -4519,6 +4520,7 @@
? null : packageState.getUserStateOrDefault(userId);
if (packageState != null && packageUserState.isStopped() != stopped) {
boolean wasNotLaunched = packageUserState.isNotLaunched();
+ wasStopped = packageUserState.isStopped();
commitPackageStateMutation(null, packageName, state -> {
PackageUserStateWrite userState = state.userState(userId);
userState.setStopped(stopped);
@@ -4550,6 +4552,24 @@
ah.setHibernatingGlobally(packageName, false);
}
});
+ // Send UNSTOPPED broadcast if necessary
+ if (wasStopped && Flags.stayStopped()) {
+ final PackageManagerInternal pmi =
+ mInjector.getLocalService(PackageManagerInternal.class);
+ final int [] userIds = resolveUserIds(userId);
+ final SparseArray<int[]> broadcastAllowList =
+ snapshotComputer().getVisibilityAllowLists(packageName, userIds);
+ final Bundle extras = new Bundle();
+ extras.putInt(Intent.EXTRA_UID, pmi.getPackageUid(packageName, 0, userId));
+ extras.putInt(Intent.EXTRA_USER_HANDLE, userId);
+ mHandler.post(() -> {
+ mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_UNSTOPPED,
+ packageName, extras,
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
+ userIds, null, broadcastAllowList, null,
+ null);
+ });
+ }
}
}
@@ -6926,6 +6946,25 @@
public ParceledListSlice<PackageInstaller.SessionInfo> getHistoricalSessions(int userId) {
return mInstallerService.getHistoricalSessions(userId);
}
+
+ @Override
+ public void sendPackageRestartedBroadcast(@NonNull String packageName,
+ int uid, @Intent.Flags int flags) {
+ final int userId = UserHandle.getUserId(uid);
+ final int [] userIds = resolveUserIds(userId);
+ final SparseArray<int[]> broadcastAllowList =
+ snapshotComputer().getVisibilityAllowLists(packageName, userIds);
+ final Bundle extras = new Bundle();
+ extras.putInt(Intent.EXTRA_UID, uid);
+ extras.putInt(Intent.EXTRA_USER_HANDLE, userId);
+ mHandler.post(() -> {
+ mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED,
+ packageName, extras,
+ flags, null, null,
+ userIds, null, broadcastAllowList, null,
+ null);
+ });
+ }
}
private void setEnabledOverlayPackages(@UserIdInt int userId,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 68a8e40..0e98158 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -75,13 +75,13 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.multiuser.Flags;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
import android.os.FileUtils;
-import android.os.Flags;
import android.os.Handler;
import android.os.IBinder;
import android.os.IProgressListener;
@@ -1559,7 +1559,7 @@
logQuietModeEnabled(userId, enableQuietMode, callingPackage);
// Broadcast generic intents for all profiles
- if (Flags.allowPrivateProfile()) {
+ if (android.os.Flags.allowPrivateProfile()) {
broadcastProfileAvailabilityChanges(profile, parent.getUserHandle(),
enableQuietMode, false);
}
@@ -3785,6 +3785,8 @@
@GuardedBy({"mPackagesLock"})
private void readUserListLP() {
+ // Whether guest restrictions are present on userlist.xml
+ boolean guestRestrictionsArePresentOnUserListXml = false;
try (ResilientAtomicFile file = getUserListFile()) {
FileInputStream fin = null;
try {
@@ -3834,6 +3836,7 @@
}
}
} else if (name.equals(TAG_GUEST_RESTRICTIONS)) {
+ guestRestrictionsArePresentOnUserListXml = true;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.END_TAG) {
if (type == XmlPullParser.START_TAG) {
@@ -3852,6 +3855,7 @@
updateUserIds();
upgradeIfNecessaryLP();
+ updateUsersWithFeatureFlags(guestRestrictionsArePresentOnUserListXml);
} catch (Exception e) {
// Remove corrupted file and retry.
file.failRead(fin, e);
@@ -3877,6 +3881,24 @@
}
/**
+ * Update any user formats or Xml data that need to be updated based on the current user state
+ * and the feature flag settings.
+ */
+ @GuardedBy({"mPackagesLock"})
+ private void updateUsersWithFeatureFlags(boolean guestRestrictionsArePresentOnUserListXml) {
+ // User Xml re-writes are required when guest restrictions are saved on userlist.xml but
+ // as per the feature flag it should be on the SYSTEM user's xml or guest restrictions
+ // are saved on SYSTEM user's xml but as per the flags it should not be saved there.
+ if (guestRestrictionsArePresentOnUserListXml
+ == Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) {
+ for (int userId: getUserIds()) {
+ writeUserLP(getUserDataNoChecks(userId));
+ }
+ writeUserListLP();
+ }
+ }
+
+ /**
* Version of {@link #upgradeIfNecessaryLP()} that takes in the userVersion for testing
* purposes. For non-tests, use {@link #upgradeIfNecessaryLP()}.
*/
@@ -4393,9 +4415,24 @@
UserRestrictionsUtils.writeRestrictions(serializer,
mBaseUserRestrictions.getRestrictions(userInfo.id), TAG_RESTRICTIONS);
- UserRestrictionsUtils.writeRestrictions(serializer,
- mDevicePolicyUserRestrictions.getRestrictions(UserHandle.USER_ALL),
- TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS);
+ if (Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) {
+ if (userInfo.id == UserHandle.USER_SYSTEM) {
+ UserRestrictionsUtils.writeRestrictions(serializer,
+ mDevicePolicyUserRestrictions.getRestrictions(UserHandle.USER_ALL),
+ TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS);
+
+ serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
+ synchronized (mGuestRestrictions) {
+ UserRestrictionsUtils.writeRestrictions(serializer, mGuestRestrictions,
+ TAG_RESTRICTIONS);
+ }
+ serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
+ }
+ } else {
+ UserRestrictionsUtils.writeRestrictions(serializer,
+ mDevicePolicyUserRestrictions.getRestrictions(UserHandle.USER_ALL),
+ TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS);
+ }
UserRestrictionsUtils.writeRestrictions(serializer,
mDevicePolicyUserRestrictions.getRestrictions(userInfo.id),
@@ -4471,12 +4508,15 @@
serializer.attributeInt(null, ATTR_USER_VERSION, mUserVersion);
serializer.attributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
- serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
- synchronized (mGuestRestrictions) {
- UserRestrictionsUtils
- .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
+ if (!Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) {
+ serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
+ synchronized (mGuestRestrictions) {
+ UserRestrictionsUtils
+ .writeRestrictions(serializer, mGuestRestrictions,
+ TAG_RESTRICTIONS);
+ }
+ serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
}
- serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
int[] userIdsToWrite;
synchronized (mUsersLock) {
userIdsToWrite = new int[mUsers.size()];
@@ -4620,6 +4660,19 @@
localRestrictions = UserRestrictionsUtils.readRestrictions(parser);
} else if (TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS.equals(tag)) {
globalRestrictions = UserRestrictionsUtils.readRestrictions(parser);
+ } else if (TAG_GUEST_RESTRICTIONS.equals(tag)) {
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.END_TAG) {
+ if (type == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(TAG_RESTRICTIONS)) {
+ synchronized (mGuestRestrictions) {
+ UserRestrictionsUtils
+ .readRestrictions(parser, mGuestRestrictions);
+ }
+ }
+ break;
+ }
+ }
} else if (TAG_ACCOUNT.equals(tag)) {
type = parser.next();
if (type == XmlPullParser.TEXT) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 16876ac..eb893fc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -251,7 +251,14 @@
private int runSetDeviceOwner(PrintWriter pw) {
parseArgs();
- mService.setActiveAdmin(mComponent, /* refreshing= */ true, mUserId);
+ boolean isAdminAdded = false;
+ try {
+ mService.setActiveAdmin(mComponent, /* refreshing= */ false, mUserId);
+ isAdminAdded = true;
+ } catch (IllegalArgumentException e) {
+ pw.printf("%s was already an admin for user %d. No need to set it again.\n",
+ mComponent.flattenToShortString(), mUserId);
+ }
try {
if (!mService.setDeviceOwner(mComponent, mUserId,
@@ -260,8 +267,10 @@
"Can't set package " + mComponent + " as device owner.");
}
} catch (Exception e) {
- // Need to remove the admin that we just added.
- mService.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
+ if (isAdminAdded) {
+ // Need to remove the admin that we just added.
+ mService.removeActiveAdmin(mComponent, mUserId);
+ }
throw e;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
new file mode 100644
index 0000000..5c50acb
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import android.hardware.display.DisplayManager;
+import android.testing.TestableContext;
+import android.view.Display;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.display.RefreshRateSettingsUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RefreshRateSettingsUtilsTest {
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+
+ @Mock
+ private DisplayManager mDisplayManagerMock;
+ @Mock
+ private Display mDisplayMock;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext.addMockSystemService(DisplayManager.class, mDisplayManagerMock);
+
+ Display.Mode[] modes = new Display.Mode[]{
+ new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
+ /* refreshRate= */ 60),
+ new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
+ /* refreshRate= */ 120),
+ new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
+ /* refreshRate= */ 90)
+ };
+
+ when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
+ when(mDisplayMock.getSupportedModes()).thenReturn(modes);
+ }
+
+ @Test
+ public void testFindHighestRefreshRateForDefaultDisplay() {
+ when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null);
+ assertEquals(DEFAULT_REFRESH_RATE,
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
+ /* delta= */ 0);
+
+ when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
+ assertEquals(120,
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
+ /* delta= */ 0);
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index b8c18e07..c4f72b3 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -27,6 +27,7 @@
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.server.display.mode.Vote.INVALID_SIZE;
import static com.google.common.truth.Truth.assertThat;
@@ -85,10 +86,12 @@
import com.android.internal.R;
import com.android.internal.display.BrightnessSynchronizer;
+import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
+import com.android.modules.utils.testing.ExtendedMockitoRule;
import com.android.server.display.DisplayDeviceConfig;
import com.android.server.display.TestUtils;
import com.android.server.display.feature.DisplayManagerFlags;
@@ -106,7 +109,7 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
+import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
@@ -252,9 +255,15 @@
@Mock
private DisplayManagerFlags mDisplayManagerFlags;
+ @Rule
+ public final ExtendedMockitoRule mExtendedMockitoRule =
+ new ExtendedMockitoRule.Builder(this)
+ .setStrictness(Strictness.LENIENT)
+ .spyStatic(RefreshRateSettingsUtils.class)
+ .build();
+
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
when(mContext.getContentResolver()).thenReturn(resolver);
@@ -1470,6 +1479,94 @@
}
@Test
+ public void testPeakRefreshRate_FlagEnabled() {
+ when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
+ .thenReturn(true);
+ float highestRefreshRate = 130;
+ doReturn(highestRefreshRate).when(() ->
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+ director.start(sensorManager);
+
+ setPeakRefreshRate(Float.POSITIVE_INFINITY);
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ highestRefreshRate);
+ }
+
+ @Test
+ public void testPeakRefreshRate_FlagDisabled() {
+ when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
+ .thenReturn(false);
+ float peakRefreshRate = 130;
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+ director.start(sensorManager);
+
+ setPeakRefreshRate(peakRefreshRate);
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ peakRefreshRate);
+ }
+
+ @Test
+ public void testMinRefreshRate_FlagEnabled() {
+ when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
+ .thenReturn(true);
+ float highestRefreshRate = 130;
+ doReturn(highestRefreshRate).when(() ->
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+ director.start(sensorManager);
+
+ setMinRefreshRate(Float.POSITIVE_INFINITY);
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate,
+ /* frameRateHigh= */ Float.POSITIVE_INFINITY);
+ }
+
+ @Test
+ public void testMinRefreshRate_FlagDisabled() {
+ when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
+ .thenReturn(false);
+ float minRefreshRate = 130;
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+ director.start(sensorManager);
+
+ setMinRefreshRate(minRefreshRate);
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ minRefreshRate,
+ /* frameRateHigh= */ Float.POSITIVE_INFINITY);
+ }
+
+ @Test
public void testSensorRegistration() {
// First, configure brightness zones or DMD won't register for sensor data.
final FakeDeviceConfig config = mInjector.getDeviceConfig();
@@ -3167,6 +3264,13 @@
waitForIdleSync();
}
+ private void setMinRefreshRate(float fps) {
+ Settings.System.putFloat(mContext.getContentResolver(), Settings.System.MIN_REFRESH_RATE,
+ fps);
+ mInjector.notifyMinRefreshRateChanged();
+ waitForIdleSync();
+ }
+
private static SensorManager createMockSensorManager(Sensor... sensors) {
SensorManager sensorManager = mock(SensorManager.class);
when(sensorManager.getSensorList(anyInt())).then((invocation) -> {
@@ -3216,6 +3320,7 @@
private final SensorManagerInternal mSensorManagerInternal;
private ContentObserver mPeakRefreshRateObserver;
+ private ContentObserver mMinRefreshRateObserver;
FakesInjector() {
this(null, null, null);
@@ -3247,6 +3352,12 @@
}
@Override
+ public void registerMinRefreshRateObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ mMinRefreshRateObserver = observer;
+ }
+
+ @Override
public void registerDisplayListener(DisplayListener listener, Handler handler) {}
@Override
@@ -3318,5 +3429,12 @@
PEAK_REFRESH_RATE_URI);
}
}
+
+ void notifyMinRefreshRateChanged() {
+ if (mMinRefreshRateObserver != null) {
+ mMinRefreshRateObserver.dispatchChange(false /*selfChange*/,
+ MIN_REFRESH_RATE_URI);
+ }
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 9f75cf8..253592c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -43,6 +43,7 @@
import android.app.PropertyInvalidatedCache;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo.UserInfoFlag;
+import android.multiuser.Flags;
import android.os.Looper;
import android.os.Parcel;
import android.os.UserHandle;
@@ -124,18 +125,34 @@
mUserManagerService.putUserInfo(data.info);
- // Set a global and user restriction so they get written out to the user file.
+ //Local restrictions are written to the user specific files and global restrictions
+ // are written to the SYSTEM user file.
setUserRestrictions(data.info.id, globalRestriction, localRestriction, true);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
mUserManagerService.writeUserLP(data, out);
- byte[] bytes = baos.toByteArray();
+ byte[] secondaryUserBytes = baos.toByteArray();
+ baos.reset();
+
+ byte[] systemUserBytes = new byte[0];
+ if (Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) {
+ UserData systemUserData = new UserData();
+ systemUserData.info = mUserManagerService.getUserInfo(UserHandle.USER_SYSTEM);
+ mUserManagerService.writeUserLP(systemUserData, baos);
+ systemUserBytes = baos.toByteArray();
+ }
// Clear the restrictions to see if they are properly read in from the user file.
setUserRestrictions(data.info.id, globalRestriction, localRestriction, false);
- mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(bytes));
+ //read the secondary and SYSTEM user file to fetch local/global device policy restrictions.
+ mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(secondaryUserBytes));
+ if (Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) {
+ mUserManagerService.readUserLP(UserHandle.USER_SYSTEM,
+ new ByteArrayInputStream(systemUserBytes));
+ }
+
assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(globalRestriction));
assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(localRestriction));
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index c05f814..53ca704 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -23,9 +23,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
@@ -49,12 +47,10 @@
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
-import android.os.Parcel;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
@@ -158,81 +154,6 @@
}
}
- // Tests parceling of NotificationRankingUpdate, and by extension, RankingMap and Ranking.
- @Test
- public void testRankingUpdate_parcel() {
- NotificationRankingUpdate nru = generateUpdate();
- Parcel parcel = Parcel.obtain();
- nru.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- NotificationRankingUpdate nru1 = NotificationRankingUpdate.CREATOR.createFromParcel(parcel);
- assertEquals(nru, nru1);
- }
-
- // Tests parceling of RankingMap and RankingMap.equals
- @Test
- public void testRankingMap_parcel() {
- RankingMap rmap = generateUpdate().getRankingMap();
- Parcel parcel = Parcel.obtain();
- rmap.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- RankingMap rmap1 = RankingMap.CREATOR.createFromParcel(parcel);
-
- detailedAssertEquals(rmap, rmap1);
- assertEquals(rmap, rmap1);
- }
-
- // Tests parceling of Ranking and Ranking.equals
- @Test
- public void testRanking_parcel() {
- Ranking ranking = generateUpdate().getRankingMap().getRawRankingObject(mKeys[0]);
- Parcel parcel = Parcel.obtain();
- ranking.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- Ranking ranking1 = new Ranking(parcel);
- detailedAssertEquals("rankings differ: ", ranking, ranking1);
- assertEquals(ranking, ranking1);
- }
-
- // Tests NotificationRankingUpdate.equals(), and by extension, RankingMap and Ranking.
- @Test
- public void testRankingUpdate_equals() {
- NotificationRankingUpdate nru = generateUpdate();
- NotificationRankingUpdate nru2 = generateUpdate();
- detailedAssertEquals(nru, nru2);
- assertEquals(nru, nru2);
- Ranking tweak = nru2.getRankingMap().getRawRankingObject(mKeys[0]);
- tweak.populate(
- tweak.getKey(),
- tweak.getRank(),
- !tweak.matchesInterruptionFilter(), // note the inversion here!
- tweak.getLockscreenVisibilityOverride(),
- tweak.getSuppressedVisualEffects(),
- tweak.getImportance(),
- tweak.getImportanceExplanation(),
- tweak.getOverrideGroupKey(),
- tweak.getChannel(),
- (ArrayList) tweak.getAdditionalPeople(),
- (ArrayList) tweak.getSnoozeCriteria(),
- tweak.canShowBadge(),
- tweak.getUserSentiment(),
- tweak.isSuspended(),
- tweak.getLastAudiblyAlertedMillis(),
- tweak.isNoisy(),
- (ArrayList) tweak.getSmartActions(),
- (ArrayList) tweak.getSmartReplies(),
- tweak.canBubble(),
- tweak.isTextChanged(),
- tweak.isConversation(),
- tweak.getConversationShortcutInfo(),
- tweak.getRankingAdjustment(),
- tweak.isBubble(),
- tweak.getProposedImportance(),
- tweak.hasSensitiveContent()
- );
- assertNotEquals(nru, nru2);
- }
-
@Test
public void testLegacyIcons_preM() {
TestListenerService service = new TestListenerService();
@@ -275,7 +196,6 @@
assertNull(n.largeIcon);
}
-
// Test data
private String[] mKeys = new String[] { "key", "key1", "key2", "key3", "key4"};
@@ -461,48 +381,6 @@
}
}
- private void detailedAssertEquals(NotificationRankingUpdate a, NotificationRankingUpdate b) {
- detailedAssertEquals(a.getRankingMap(), b.getRankingMap());
- }
-
- private void detailedAssertEquals(String comment, Ranking a, Ranking b) {
- assertEquals(comment, a.getKey(), b.getKey());
- assertEquals(comment, a.getRank(), b.getRank());
- assertEquals(comment, a.matchesInterruptionFilter(), b.matchesInterruptionFilter());
- assertEquals(comment, a.getLockscreenVisibilityOverride(), b.getLockscreenVisibilityOverride());
- assertEquals(comment, a.getSuppressedVisualEffects(), b.getSuppressedVisualEffects());
- assertEquals(comment, a.getImportance(), b.getImportance());
- assertEquals(comment, a.getImportanceExplanation(), b.getImportanceExplanation());
- assertEquals(comment, a.getOverrideGroupKey(), b.getOverrideGroupKey());
- assertEquals(comment, a.getChannel(), b.getChannel());
- assertEquals(comment, a.getAdditionalPeople(), b.getAdditionalPeople());
- assertEquals(comment, a.getSnoozeCriteria(), b.getSnoozeCriteria());
- assertEquals(comment, a.canShowBadge(), b.canShowBadge());
- assertEquals(comment, a.getUserSentiment(), b.getUserSentiment());
- assertEquals(comment, a.isSuspended(), b.isSuspended());
- assertEquals(comment, a.getLastAudiblyAlertedMillis(), b.getLastAudiblyAlertedMillis());
- assertEquals(comment, a.isNoisy(), b.isNoisy());
- assertEquals(comment, a.getSmartReplies(), b.getSmartReplies());
- assertEquals(comment, a.canBubble(), b.canBubble());
- assertEquals(comment, a.isConversation(), b.isConversation());
- assertEquals(comment, a.getConversationShortcutInfo().getId(),
- b.getConversationShortcutInfo().getId());
- assertActionsEqual(a.getSmartActions(), b.getSmartActions());
- assertEquals(a.getProposedImportance(), b.getProposedImportance());
- assertEquals(a.hasSensitiveContent(), b.hasSensitiveContent());
- }
-
- private void detailedAssertEquals(RankingMap a, RankingMap b) {
- Ranking arank = new Ranking();
- Ranking brank = new Ranking();
- assertArrayEquals(a.getOrderedKeys(), b.getOrderedKeys());
- for (String key : a.getOrderedKeys()) {
- a.getRanking(key, arank);
- b.getRanking(key, brank);
- detailedAssertEquals("ranking for key <" + key + ">", arank, brank);
- }
- }
-
public static class TestListenerService extends NotificationListenerService {
private final IBinder binder = new LocalBinder();
public int targetSdk = 0;