Merge "Fix 3-finger swipe unlock not working" into udc-qpr-dev
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 9bd2970..9f90425 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2958,6 +2958,10 @@
method @Deprecated public boolean isBound();
}
+ public class NotificationRankingUpdate implements android.os.Parcelable {
+ method public final boolean isFdNotNullAndClosed();
+ }
+
}
package android.service.quickaccesswallet {
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 09450f5..b71cb4a 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -117,6 +117,17 @@
"android.app.action.CONFIRM_REMOTE_DEVICE_CREDENTIAL";
/**
+ * Intent used to prompt user for device credential for entering repair
+ * mode. If the credential is verified successfully, then the information
+ * needed to verify the credential again will be written to a location that
+ * is available to repair mode. This makes it possible for repair mode to
+ * require that the same credential be provided to exit repair mode.
+ * @hide
+ */
+ public static final String ACTION_PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL =
+ "android.app.action.PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL";
+
+ /**
* A CharSequence dialog title to show to the user when used with a
* {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
* @hide
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 4b8cfd5..c4e4995 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -334,6 +334,12 @@
public boolean isVisible;
/**
+ * Whether this task is request visible.
+ * @hide
+ */
+ public boolean isVisibleRequested;
+
+ /**
* Whether this task is sleeping due to sleeping display.
* @hide
*/
@@ -518,6 +524,7 @@
&& Objects.equals(taskDescription, that.taskDescription)
&& isFocused == that.isFocused
&& isVisible == that.isVisible
+ && isVisibleRequested == that.isVisibleRequested
&& isSleeping == that.isSleeping
&& Objects.equals(mTopActivityLocusId, that.mTopActivityLocusId)
&& parentTaskId == that.parentTaskId
@@ -591,6 +598,7 @@
parentTaskId = source.readInt();
isFocused = source.readBoolean();
isVisible = source.readBoolean();
+ isVisibleRequested = source.readBoolean();
isSleeping = source.readBoolean();
topActivityInSizeCompat = source.readBoolean();
topActivityEligibleForLetterboxEducation = source.readBoolean();
@@ -644,6 +652,7 @@
dest.writeInt(parentTaskId);
dest.writeBoolean(isFocused);
dest.writeBoolean(isVisible);
+ dest.writeBoolean(isVisibleRequested);
dest.writeBoolean(isSleeping);
dest.writeBoolean(topActivityInSizeCompat);
dest.writeBoolean(topActivityEligibleForLetterboxEducation);
@@ -687,6 +696,7 @@
+ " parentTaskId=" + parentTaskId
+ " isFocused=" + isFocused
+ " isVisible=" + isVisible
+ + " isVisibleRequested=" + isVisibleRequested
+ " isSleeping=" + isSleeping
+ " topActivityInSizeCompat=" + topActivityInSizeCompat
+ " topActivityEligibleForLetterboxEducation= "
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index d96a9d1..34f0964 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -39,7 +39,6 @@
import android.os.UserHandle;
import android.permission.IPermissionManager;
import android.util.Log;
-import android.util.Pair;
import android.view.IWindowManager;
import android.view.InputDevice;
import android.view.InputEvent;
@@ -52,7 +51,8 @@
import android.view.accessibility.IAccessibilityManager;
import android.window.ScreenCapture;
import android.window.ScreenCapture.CaptureArgs;
-import android.window.ScreenCapture.ScreenCaptureListener;
+import android.window.ScreenCapture.ScreenshotHardwareBuffer;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import libcore.io.IoUtils;
@@ -235,12 +235,12 @@
final CaptureArgs captureArgs = new CaptureArgs.Builder<>()
.setSourceCrop(crop)
.build();
- Pair<ScreenCaptureListener, ScreenCapture.ScreenshotSync> syncScreenCapture =
+ SynchronousScreenCaptureListener syncScreenCapture =
ScreenCapture.createSyncCaptureListener();
mWindowManager.captureDisplay(DEFAULT_DISPLAY, captureArgs,
- syncScreenCapture.first);
- final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
- syncScreenCapture.second.get();
+ syncScreenCapture);
+ final ScreenshotHardwareBuffer screenshotBuffer =
+ syncScreenCapture.getBuffer();
return screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
} catch (RemoteException re) {
re.rethrowAsRuntimeException();
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 66c24ff..1c5332a 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -913,7 +913,7 @@
* instead the default system wallpaper is returned
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
+ * and will always throw a {@code SecurityException}.</li>
* <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
* can still access the real wallpaper on all versions. </li>
* </ul>
@@ -935,18 +935,9 @@
}
/**
- * <strong> Important note: </strong>
- * <ul>
- * <li>Up to version S, this method requires the
- * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
- * <li>Starting in T, directly accessing the wallpaper is not possible anymore,
- * instead the default system wallpaper is returned
- * (some versions of T may throw a {@code SecurityException}).</li>
- * <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
- * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
- * can still access the real wallpaper on all versions. </li>
- * </ul>
+ * <strong> Important note: </strong> only apps with
+ * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} should use this method.
+ * Otherwise, a {@code SecurityException} will be thrown.
*
* <p>
* Retrieve the requested wallpaper for the specified wallpaper type if the wallpaper is not
@@ -1206,7 +1197,7 @@
* instead the default system wallpaper is returned
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
+ * and will always throw a {@code SecurityException}.</li>
* <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
* can still access the real wallpaper on all versions. </li>
* </ul>
@@ -1228,18 +1219,9 @@
}
/**
- * <strong> Important note: </strong>
- * <ul>
- * <li>Up to version S, this method requires the
- * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
- * <li>Starting in T, directly accessing the wallpaper is not possible anymore,
- * instead the default system wallpaper is returned
- * (some versions of T may throw a {@code SecurityException}).</li>
- * <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
- * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
- * can still access the real wallpaper on all versions. </li>
- * </ul>
+ * <strong> Important note: </strong> only apps with
+ * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} should use this method.
+ * Otherwise, a {@code SecurityException} will be thrown.
*
* <p>
* Equivalent to {@link #getDrawable(int)}.
@@ -1268,7 +1250,7 @@
* instead the default wallpaper is returned
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
+ * and will always throw a {@code SecurityException}.</li>
* <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
* can still access the real wallpaper on all versions. </li>
* </ul>
@@ -1290,19 +1272,9 @@
}
/**
- * <strong> Important note: </strong>
- * <ul>
- * <li>Up to version S, this method requires the
- * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
- * <li>Starting in T, directly accessing the wallpaper is not possible anymore,
- * instead the default system wallpaper is returned
- * (some versions of T may throw a {@code SecurityException}).</li>
- * <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
- * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
- * can still access the real wallpaper on all versions. </li>
- * </ul>
- * <br>
+ * <strong> Important note: </strong> only apps with
+ * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} should use this method.
+ * Otherwise, a {@code SecurityException} will be thrown.
*
* Like {@link #getDrawable(int)}, but the returned Drawable has a number
* of limitations to reduce its overhead as much as possible. It will
@@ -1334,18 +1306,9 @@
}
/**
- * <strong> Important note: </strong>
- * <ul>
- * <li>Up to version S, this method requires the
- * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
- * <li>Starting in T, directly accessing the wallpaper is not possible anymore,
- * instead the default system wallpaper is returned
- * (some versions of T may throw a {@code SecurityException}).</li>
- * <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
- * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
- * can still access the real wallpaper on all versions. </li>
- * </ul>
+ * <strong> Important note: </strong> only apps with
+ * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} should use this method.
+ * Otherwise, a {@code SecurityException} will be thrown.
*
* <p>
* Equivalent to {@link #getFastDrawable()}.
@@ -1364,18 +1327,9 @@
}
/**
- * <strong> Important note: </strong>
- * <ul>
- * <li>Up to version S, this method requires the
- * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.</li>
- * <li>Starting in T, directly accessing the wallpaper is not possible anymore,
- * instead the default system wallpaper is returned
- * (some versions of T may throw a {@code SecurityException}).</li>
- * <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
- * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
- * can still access the real wallpaper on all versions. </li>
- * </ul>
+ * <strong> Important note: </strong> only apps with
+ * {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
+ * should use this method. Otherwise, a {@code SecurityException} will be thrown.
*
* <p>
* Equivalent to {@link #getFastDrawable(int)}.
@@ -1562,7 +1516,7 @@
* instead the default system wallpaper is returned
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
- * and will always throw a @code SecurityException}.</li>
+ * and will always throw a {@code SecurityException}.</li>
* <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
* can still access the real wallpaper on all versions. </li>
* </ul>
@@ -2476,19 +2430,38 @@
}
/**
- * Reset all wallpaper to the factory default.
+ * Reset all wallpaper to the factory default. As opposed to {@link #clear()}, if the device
+ * is configured to have a live wallpaper by default, apply it.
*
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#SET_WALLPAPER}.
*/
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
public void clearWallpaper() {
+ if (isLockscreenLiveWallpaperEnabled()) {
+ clearWallpaper(FLAG_LOCK | FLAG_SYSTEM, mContext.getUserId());
+ return;
+ }
clearWallpaper(FLAG_LOCK, mContext.getUserId());
clearWallpaper(FLAG_SYSTEM, mContext.getUserId());
}
/**
- * Clear the wallpaper for a specific user. The caller must hold the
+ * Clear the wallpaper for a specific user.
+ * <ul>
+ * <li> When called with {@code which=}{@link #FLAG_LOCK}, clear the lockscreen wallpaper.
+ * The home screen wallpaper will become visible on the lock screen. </li>
+ *
+ * <li> When called with {@code which=}{@link #FLAG_SYSTEM}, revert the home screen
+ * wallpaper to default. The lockscreen wallpaper will be unchanged: if the previous
+ * wallpaper was shared between home and lock screen, it will become lock screen only. </li>
+ *
+ * <li> When called with {@code which=}({@link #FLAG_LOCK} | {@link #FLAG_SYSTEM}), put the
+ * default wallpaper on both home and lock screen, removing any user defined wallpaper.</li>
+ * </ul>
+ * </p>
+ *
+ * The caller must hold the
* INTERACT_ACROSS_USERS_FULL permission to clear another user's
* wallpaper, and must hold the SET_WALLPAPER permission in all
* circumstances.
@@ -2793,8 +2766,9 @@
/**
* Remove any currently set system wallpaper, reverting to the system's built-in
- * wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
- * is broadcast.
+ * wallpaper. As opposed to {@link #clearWallpaper()}, this method always set a static wallpaper
+ * with the default image, even if the device is configured to have a live wallpaper by default.
+ * On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
*
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#SET_WALLPAPER}.
@@ -2809,9 +2783,14 @@
/**
* Remove one or more currently set wallpapers, reverting to the system default
- * display for each one. If {@link #FLAG_SYSTEM} is set in the {@code which}
- * parameter, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} will be broadcast
- * upon success.
+ * display for each one. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
+ * is broadcast.
+ * <ul>
+ * <li> If {@link #FLAG_SYSTEM} is set in the {@code which} parameter, put the default
+ * wallpaper on both home and lock screen, removing any user defined wallpaper. </li>
+ * <li> When called with {@code which=}{@link #FLAG_LOCK}, clear the lockscreen wallpaper.
+ * The home screen wallpaper will become visible on the lock screen. </li>
+ * </ul>
*
* @param which A bitwise combination of {@link #FLAG_SYSTEM} or
* {@link #FLAG_LOCK}
@@ -2821,6 +2800,7 @@
public void clear(@SetWallpaperFlags int which) throws IOException {
if ((which & FLAG_SYSTEM) != 0) {
clear();
+ if (isLockscreenLiveWallpaperEnabled()) return;
}
if ((which & FLAG_LOCK) != 0) {
clearWallpaper(FLAG_LOCK, mContext.getUserId());
diff --git a/core/java/android/app/admin/WifiSsidPolicy.java b/core/java/android/app/admin/WifiSsidPolicy.java
index 3fefe4b..ed53967 100644
--- a/core/java/android/app/admin/WifiSsidPolicy.java
+++ b/core/java/android/app/admin/WifiSsidPolicy.java
@@ -135,6 +135,10 @@
dest.writeArraySet(mSsids);
}
+ /**
+ * Two instances of WifiSsidPolicy is considered equal if they have
+ * the same WifiSsidPolicyType and the same set of WifiSsids
+ */
@Override
public boolean equals(Object thatObject) {
if (this == thatObject) {
@@ -147,6 +151,9 @@
return mPolicyType == that.mPolicyType && Objects.equals(mSsids, that.mSsids);
}
+ /**
+ * Returns the hash code value of WifiSsidPolicyType and WifiSsid set
+ */
@Override
public int hashCode() {
return Objects.hash(mPolicyType, mSsids);
diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java
index 0311da4..4403251 100644
--- a/core/java/android/hardware/input/KeyboardLayout.java
+++ b/core/java/android/hardware/input/KeyboardLayout.java
@@ -73,7 +73,7 @@
private final int mProductId;
/** Currently supported Layout types in the KCM files */
- private enum LayoutType {
+ public enum LayoutType {
UNDEFINED(0, LAYOUT_TYPE_UNDEFINED),
QWERTY(1, LAYOUT_TYPE_QWERTY),
QWERTZ(2, LAYOUT_TYPE_QWERTZ),
@@ -88,9 +88,11 @@
private final int mValue;
private final String mName;
private static final Map<Integer, LayoutType> VALUE_TO_ENUM_MAP = new HashMap<>();
+ private static final Map<String, LayoutType> NAME_TO_ENUM_MAP = new HashMap<>();
static {
for (LayoutType type : LayoutType.values()) {
VALUE_TO_ENUM_MAP.put(type.mValue, type);
+ NAME_TO_ENUM_MAP.put(type.mName, type);
}
}
@@ -110,6 +112,25 @@
private String getName() {
return mName;
}
+
+ /**
+ * Returns enum value for provided layout type
+ * @param layoutName name of the layout type
+ * @return int value corresponding to the LayoutType enum that matches the layout name.
+ * (LayoutType.UNDEFINED if no match found)
+ */
+ public static int getLayoutTypeEnumValue(String layoutName) {
+ return NAME_TO_ENUM_MAP.getOrDefault(layoutName, UNDEFINED).getValue();
+ }
+
+ /**
+ * Returns name for provided layout type enum value
+ * @param enumValue value representation for LayoutType enum
+ * @return Layout name corresponding to the enum value (LAYOUT_TYPE_UNDEFINED if not found)
+ */
+ public static String getLayoutNameFromValue(int enumValue) {
+ return VALUE_TO_ENUM_MAP.getOrDefault(enumValue, UNDEFINED).getName();
+ }
}
@NonNull
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index d1063f6..d1c10fa 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -190,8 +190,7 @@
/**
* Wake lock flag: Turn the screen on when the wake lock is acquired.
* <p>
- * This flag requires {@link android.Manifest.permission#TURN_SCREEN_ON} for apps targeting
- * Android version {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and higher.
+ * This flag will require {@link android.Manifest.permission#TURN_SCREEN_ON} in future releases.
* </p><p>
* Normally wake locks don't actually wake the device, they just cause the screen to remain on
* once it's already on. This flag will cause the device to wake up when the wake lock is
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java
index 5469916..9a02b74b 100644
--- a/core/java/android/service/dreams/DreamOverlayService.java
+++ b/core/java/android/service/dreams/DreamOverlayService.java
@@ -71,13 +71,7 @@
@Override
public void wakeUp() {
- mService.wakeUp(this, () -> {
- try {
- mDreamOverlayCallback.onWakeUpComplete();
- } catch (RemoteException e) {
- Log.e(TAG, "Could not notify dream of wakeUp", e);
- }
- });
+ mService.wakeUp(this);
}
@Override
@@ -125,14 +119,14 @@
mCurrentClient = null;
}
- private void wakeUp(OverlayClient client, Runnable callback) {
+ private void wakeUp(OverlayClient client) {
// Run on executor as this is a binder call from OverlayClient.
mExecutor.execute(() -> {
if (mCurrentClient != client) {
return;
}
- onWakeUp(callback);
+ onWakeUp();
});
}
@@ -190,19 +184,10 @@
/**
* This method is overridden by implementations to handle when the dream has been requested
- * to wakeup. This allows any overlay animations to run. By default, the method will invoke
- * the callback immediately.
- *
- * This callback will be run on the {@link Executor} provided in the constructor if provided, or
- * on the main executor if none was provided.
- *
- * @param onCompleteCallback The callback to trigger to notify the dream service that the
- * overlay has completed waking up.
+ * to wakeup.
* @hide
*/
- public void onWakeUp(@NonNull Runnable onCompleteCallback) {
- onCompleteCallback.run();
- }
+ public void onWakeUp() {}
/**
* This method is overridden by implementations to handle when the dream has ended. There may
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 3a32352..cd57de5 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -249,13 +249,6 @@
// Simply finish dream when exit is requested.
mHandler.post(() -> finish());
}
-
- @Override
- public void onWakeUpComplete() {
- // Finish the dream once overlay animations are complete. Execute on handler since
- // this is coming in on the overlay binder.
- mHandler.post(() -> finish());
- }
};
@@ -923,6 +916,7 @@
overlay.wakeUp();
} catch (RemoteException e) {
Slog.e(TAG, "Error waking the overlay service", e);
+ } finally {
finish();
}
});
diff --git a/core/java/android/service/dreams/IDreamOverlayCallback.aidl b/core/java/android/service/dreams/IDreamOverlayCallback.aidl
index 4ad63f1..ec76a33 100644
--- a/core/java/android/service/dreams/IDreamOverlayCallback.aidl
+++ b/core/java/android/service/dreams/IDreamOverlayCallback.aidl
@@ -28,7 +28,4 @@
* Invoked to request the dream exit.
*/
void onExitRequested();
-
- /** Invoked when the dream overlay wakeUp animation is complete. */
- void onWakeUpComplete();
}
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index a853714..75640bd 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -16,32 +16,117 @@
package android.service.notification;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SharedMemory;
+import android.system.ErrnoException;
+import android.system.OsConstants;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+
+import java.nio.ByteBuffer;
/**
+ * Represents an update to notification rankings.
* @hide
*/
+@SuppressLint({"ParcelNotFinal", "ParcelCreator"})
+@TestApi
public class NotificationRankingUpdate implements Parcelable {
private final NotificationListenerService.RankingMap mRankingMap;
+ // The ranking map is stored in shared memory when parceled, for sending across the binder.
+ // This is done because the ranking map can grow large if there are many notifications.
+ private SharedMemory mRankingMapFd = null;
+ private final String mSharedMemoryName = "NotificationRankingUpdatedSharedMemory";
+
+ /**
+ * @hide
+ */
public NotificationRankingUpdate(NotificationListenerService.Ranking[] rankings) {
mRankingMap = new NotificationListenerService.RankingMap(rankings);
}
+ /**
+ * @hide
+ */
public NotificationRankingUpdate(Parcel in) {
- mRankingMap = in.readParcelable(getClass().getClassLoader(), android.service.notification.NotificationListenerService.RankingMap.class);
+ if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(
+ SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) {
+ // Recover the ranking map from the SharedMemory and store it in mapParcel.
+ final Parcel mapParcel = Parcel.obtain();
+ ByteBuffer buffer = null;
+ try {
+ // 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);
+
+ // In the case that the ranking map can't be read, readParcelable may return null.
+ // In this case, we set mRankingMap to null;
+ if (mRankingMapFd == null) {
+ mRankingMap = null;
+ return;
+ }
+ // We only need read-only access to the shared memory region.
+ buffer = mRankingMapFd.mapReadOnly();
+ if (buffer == null) {
+ mRankingMap = null;
+ return;
+ }
+ byte[] payload = new byte[buffer.remaining()];
+ buffer.get(payload);
+ mapParcel.unmarshall(payload, 0, payload.length);
+ mapParcel.setDataPosition(0);
+
+ mRankingMap = mapParcel.readParcelable(getClass().getClassLoader(),
+ android.service.notification.NotificationListenerService.RankingMap.class);
+ } catch (ErrnoException e) {
+ // TODO(b/284297289): remove throw when associated flag is moved to droidfood, to
+ // avoid crashes; change to Log.wtf.
+ throw new RuntimeException(e);
+ } finally {
+ mapParcel.recycle();
+ if (buffer != null) {
+ mRankingMapFd.unmap(buffer);
+ }
+ }
+ } else {
+ mRankingMap = in.readParcelable(getClass().getClassLoader(),
+ android.service.notification.NotificationListenerService.RankingMap.class);
+ }
}
+ /**
+ * Confirms that the SharedMemory file descriptor is closed. Should only be used for testing.
+ * @hide
+ */
+ @TestApi
+ public final boolean isFdNotNullAndClosed() {
+ return mRankingMapFd != null && mRankingMapFd.getFd() == -1;
+ }
+
+ /**
+ * @hide
+ */
public NotificationListenerService.RankingMap getRankingMap() {
return mRankingMap;
}
+ /**
+ * @hide
+ */
@Override
public int describeContents() {
return 0;
}
+ /**
+ * @hide
+ */
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
@@ -51,11 +136,51 @@
return mRankingMap.equals(other.mRankingMap);
}
+ /**
+ * @hide
+ */
@Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeParcelable(mRankingMap, flags);
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(
+ SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) {
+ final Parcel mapParcel = Parcel.obtain();
+ try {
+ // Parcels the ranking map and measures its size.
+ mapParcel.writeParcelable(mRankingMap, flags);
+ int mapSize = mapParcel.dataSize();
+
+ // Creates a new SharedMemory object with enough space to hold the ranking map.
+ SharedMemory mRankingMapFd = SharedMemory.create(mSharedMemoryName, mapSize);
+ if (mRankingMapFd == null) {
+ return;
+ }
+
+ // 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);
+ } catch (ErrnoException e) {
+ // TODO(b/284297289): remove throw when associated flag is moved to droidfood, to
+ // avoid crashes; change to Log.wtf.
+ throw new RuntimeException(e);
+ } finally {
+ mapParcel.recycle();
+ }
+ } else {
+ out.writeParcelable(mRankingMap, flags);
+ }
}
+ /**
+ * @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/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index d87198a0..ff7d8bb 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -238,7 +238,7 @@
DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_ENROLLMENT, "false");
DEFAULT_FLAGS.put(SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE, "true");
DEFAULT_FLAGS.put(SETTINGS_PREFER_ACCESSIBILITY_MENU_IN_SYSTEM, "false");
- DEFAULT_FLAGS.put(SETTINGS_AUDIO_ROUTING, "true");
+ DEFAULT_FLAGS.put(SETTINGS_AUDIO_ROUTING, "false");
DEFAULT_FLAGS.put(SETTINGS_FLASH_NOTIFICATIONS, "true");
DEFAULT_FLAGS.put(SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "true");
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index ea75076..739c1bf 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -2065,7 +2065,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (SyncResultReceiver.TimeoutException e) {
- throw new RuntimeException("Fail to get enabled autofill services status.");
+ throw new RuntimeException("Fail to get enabled autofill services status. " + e);
}
}
@@ -2084,7 +2084,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (SyncResultReceiver.TimeoutException e) {
- throw new RuntimeException("Fail to get autofill services component name.");
+ throw new RuntimeException("Fail to get autofill services component name. " + e);
}
}
@@ -2111,7 +2111,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (SyncResultReceiver.TimeoutException e) {
- throw new RuntimeException("Fail to get user data id for field classification.");
+ throw new RuntimeException("Fail to get user data id for field classification. " + e);
}
}
@@ -2134,7 +2134,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (SyncResultReceiver.TimeoutException e) {
- throw new RuntimeException("Fail to get user data for field classification.");
+ throw new RuntimeException("Fail to get user data for field classification. " + e);
}
}
@@ -2174,7 +2174,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (SyncResultReceiver.TimeoutException e) {
- throw new RuntimeException("Fail to get field classification enabled status.");
+ throw new RuntimeException("Fail to get field classification enabled status. " + e);
}
}
@@ -2198,7 +2198,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (SyncResultReceiver.TimeoutException e) {
- throw new RuntimeException("Fail to get default field classification algorithm.");
+ throw new RuntimeException("Fail to get default field classification algorithm. " + e);
}
}
@@ -2220,7 +2220,8 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (SyncResultReceiver.TimeoutException e) {
- throw new RuntimeException("Fail to get available field classification algorithms.");
+ throw new
+ RuntimeException("Fail to get available field classification algorithms. " + e);
}
}
@@ -2244,7 +2245,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (SyncResultReceiver.TimeoutException e) {
- throw new RuntimeException("Fail to get autofill supported status.");
+ throw new RuntimeException("Fail to get autofill supported status. " + e);
}
}
diff --git a/core/java/android/window/ScreenCapture.java b/core/java/android/window/ScreenCapture.java
index fa7f577..0cc9c64 100644
--- a/core/java/android/window/ScreenCapture.java
+++ b/core/java/android/window/ScreenCapture.java
@@ -27,7 +27,6 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
-import android.util.Pair;
import android.view.SurfaceControl;
import libcore.util.NativeAllocationRegistry;
@@ -73,14 +72,14 @@
*/
public static ScreenshotHardwareBuffer captureDisplay(
DisplayCaptureArgs captureArgs) {
- Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture = createSyncCaptureListener();
- int status = captureDisplay(captureArgs, syncScreenCapture.first);
+ SynchronousScreenCaptureListener syncScreenCapture = createSyncCaptureListener();
+ int status = captureDisplay(captureArgs, syncScreenCapture);
if (status != 0) {
return null;
}
try {
- return syncScreenCapture.second.get();
+ return syncScreenCapture.getBuffer();
} catch (Exception e) {
return null;
}
@@ -133,14 +132,14 @@
* @hide
*/
public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) {
- Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture = createSyncCaptureListener();
- int status = captureLayers(captureArgs, syncScreenCapture.first);
+ SynchronousScreenCaptureListener syncScreenCapture = createSyncCaptureListener();
+ int status = captureLayers(captureArgs, syncScreenCapture);
if (status != 0) {
return null;
}
try {
- return syncScreenCapture.second.get();
+ return syncScreenCapture.getBuffer();
} catch (Exception e) {
return null;
}
@@ -743,14 +742,35 @@
* A helper method to handle the async screencapture callbacks synchronously. This should only
* be used if the screencapture caller doesn't care that it blocks waiting for a screenshot.
*
- * @return a Pair that holds the {@link ScreenCaptureListener} that should be used for capture
- * calls into SurfaceFlinger and a {@link ScreenshotSync} object to retrieve the results.
+ * @return a {@link SynchronousScreenCaptureListener} that should be used for capture
+ * calls into SurfaceFlinger.
*/
- public static Pair<ScreenCaptureListener, ScreenshotSync> createSyncCaptureListener() {
- final ScreenshotSync screenshotSync = new ScreenshotSync();
- final ScreenCaptureListener screenCaptureListener = new ScreenCaptureListener(
- screenshotSync::setScreenshotHardwareBuffer);
- return new Pair<>(screenCaptureListener, screenshotSync);
+ public static SynchronousScreenCaptureListener createSyncCaptureListener() {
+ ScreenshotHardwareBuffer[] bufferRef = new ScreenshotHardwareBuffer[1];
+ CountDownLatch latch = new CountDownLatch(1);
+ Consumer<ScreenshotHardwareBuffer> consumer = buffer -> {
+ bufferRef[0] = buffer;
+ latch.countDown();
+ };
+
+ return new SynchronousScreenCaptureListener(consumer) {
+ // In order to avoid requiring two GC cycles to clean up the consumer and the buffer
+ // it references, the underlying JNI listener holds a weak reference to the consumer.
+ // This property exists to ensure the consumer stays alive during the listener's
+ // lifetime.
+ private Consumer<ScreenshotHardwareBuffer> mConsumer = consumer;
+
+ @Override
+ public ScreenshotHardwareBuffer getBuffer() {
+ try {
+ latch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
+ return bufferRef[0];
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to wait for screen capture result", e);
+ return null;
+ }
+ }
+ };
}
/**
@@ -758,28 +778,15 @@
* {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)} or
* {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)}
*/
- public static class ScreenshotSync {
- private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
- private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;
-
- private void setScreenshotHardwareBuffer(
- ScreenshotHardwareBuffer screenshotHardwareBuffer) {
- mScreenshotHardwareBuffer = screenshotHardwareBuffer;
- mCountDownLatch.countDown();
+ public abstract static class SynchronousScreenCaptureListener extends ScreenCaptureListener {
+ SynchronousScreenCaptureListener(Consumer<ScreenshotHardwareBuffer> consumer) {
+ super(consumer);
}
/**
* Get the {@link ScreenshotHardwareBuffer} synchronously. This can be null if the
* screenshot failed or if there was no callback in {@link #SCREENSHOT_WAIT_TIME_S} seconds.
*/
- public ScreenshotHardwareBuffer get() {
- try {
- mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
- return mScreenshotHardwareBuffer;
- } catch (Exception e) {
- Log.e(TAG, "Failed to wait for screen capture result", e);
- return null;
- }
- }
+ public abstract ScreenshotHardwareBuffer getBuffer();
}
}
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index bf26568..4e7bfe5 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -16,42 +16,50 @@
package com.android.internal.app;
-import static android.graphics.PixelFormat.TRANSLUCENT;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_SPIN;
import android.animation.ObjectAnimator;
+import android.animation.TimeAnimator;
+import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
-import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
+import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.os.CombinedVibration;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.VibrationEffect;
+import android.os.VibratorManager;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.OvershootInterpolator;
-import android.widget.AnalogClock;
+import android.view.WindowInsets;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.internal.R;
import org.json.JSONObject;
-import java.time.Clock;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
+import java.util.Random;
/**
* @hide
@@ -59,30 +67,160 @@
public class PlatLogoActivity extends Activity {
private static final String TAG = "PlatLogoActivity";
- private static final String S_EGG_UNLOCK_SETTING = "egg_mode_s";
+ private static final long LAUNCH_TIME = 5000L;
- private SettableAnalogClock mClock;
+ private static final String U_EGG_UNLOCK_SETTING = "egg_mode_u";
+
+ private static final float MIN_WARP = 1f;
+ private static final float MAX_WARP = 10f; // after all these years
+ private static final boolean FINISH_AFTER_NEXT_STAGE_LAUNCH = false;
+
private ImageView mLogo;
- private BubblesDrawable mBg;
+ private Starfield mStarfield;
+
+ private FrameLayout mLayout;
+
+ private TimeAnimator mAnim;
+ private ObjectAnimator mWarpAnim;
+ private Random mRandom;
+ private float mDp;
+
+ private RumblePack mRumble;
+
+ private boolean mAnimationsEnabled = true;
+
+ private final View.OnTouchListener mTouchListener = new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ measureTouchPressure(event);
+ startWarp();
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ stopWarp();
+ break;
+ }
+ return true;
+ }
+
+ };
+
+ private final Runnable mLaunchNextStage = () -> {
+ stopWarp();
+ launchNextStage(false);
+ };
+
+ private final TimeAnimator.TimeListener mTimeListener = new TimeAnimator.TimeListener() {
+ @Override
+ public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
+ mStarfield.update(deltaTime);
+ final float warpFrac = (mStarfield.getWarp() - MIN_WARP) / (MAX_WARP - MIN_WARP);
+ if (mAnimationsEnabled) {
+ mLogo.setTranslationX(mRandom.nextFloat() * warpFrac * 5 * mDp);
+ mLogo.setTranslationY(mRandom.nextFloat() * warpFrac * 5 * mDp);
+ }
+ if (warpFrac > 0f) {
+ mRumble.rumble(warpFrac);
+ }
+ mLayout.postInvalidate();
+ }
+ };
+
+ private class RumblePack implements Handler.Callback {
+ private static final int MSG = 6464;
+ private static final int INTERVAL = 50;
+
+ private final VibratorManager mVibeMan;
+ private final HandlerThread mVibeThread;
+ private final Handler mVibeHandler;
+ private boolean mSpinPrimitiveSupported;
+
+ private long mLastVibe = 0;
+
+ @SuppressLint("MissingPermission")
+ @Override
+ public boolean handleMessage(Message msg) {
+ final float warpFrac = msg.arg1 / 100f;
+ if (mSpinPrimitiveSupported) {
+ if (msg.getWhen() > mLastVibe + INTERVAL) {
+ mLastVibe = msg.getWhen();
+ mVibeMan.vibrate(CombinedVibration.createParallel(
+ VibrationEffect.startComposition()
+ .addPrimitive(PRIMITIVE_SPIN, (float) Math.pow(warpFrac, 3.0))
+ .compose()
+ ));
+ }
+ } else {
+ if (mRandom.nextFloat() < warpFrac) {
+ mLogo.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
+ }
+ }
+ return false;
+ }
+ RumblePack() {
+ mVibeMan = getSystemService(VibratorManager.class);
+ mSpinPrimitiveSupported = mVibeMan.getDefaultVibrator()
+ .areAllPrimitivesSupported(PRIMITIVE_SPIN);
+
+ mVibeThread = new HandlerThread("VibratorThread");
+ mVibeThread.start();
+ mVibeHandler = Handler.createAsync(mVibeThread.getLooper(), this);
+ }
+
+ public void destroy() {
+ mVibeThread.quit();
+ }
+
+ private void rumble(float warpFrac) {
+ if (!mVibeThread.isAlive()) return;
+
+ final Message msg = Message.obtain();
+ msg.what = MSG;
+ msg.arg1 = (int) (warpFrac * 100);
+ mVibeHandler.removeMessages(MSG);
+ mVibeHandler.sendMessage(msg);
+ }
+
+ }
@Override
- protected void onPause() {
- super.onPause();
+ protected void onDestroy() {
+ mRumble.destroy();
+
+ super.onDestroy();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ getWindow().setDecorFitsSystemWindows(false);
getWindow().setNavigationBarColor(0);
getWindow().setStatusBarColor(0);
+ getWindow().getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
final ActionBar ab = getActionBar();
if (ab != null) ab.hide();
- final FrameLayout layout = new FrameLayout(this);
+ try {
+ mAnimationsEnabled = Settings.Global.getFloat(getContentResolver(),
+ Settings.Global.ANIMATOR_DURATION_SCALE) > 0f;
+ } catch (Settings.SettingNotFoundException e) {
+ mAnimationsEnabled = true;
+ }
- mClock = new SettableAnalogClock(this);
+ mRumble = new RumblePack();
+
+ mLayout = new FrameLayout(this);
+ mRandom = new Random();
+ mDp = getResources().getDisplayMetrics().density;
+ mStarfield = new Starfield(mRandom, mDp * 2f);
+ mStarfield.setVelocity(
+ 200f * (mRandom.nextFloat() - 0.5f),
+ 200f * (mRandom.nextFloat() - 0.5f));
+ mLayout.setBackground(mStarfield);
final DisplayMetrics dm = getResources().getDisplayMetrics();
final float dp = dm.density;
@@ -90,22 +228,79 @@
final int widgetSize = (int) (minSide * 0.75);
final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(widgetSize, widgetSize);
lp.gravity = Gravity.CENTER;
- layout.addView(mClock, lp);
mLogo = new ImageView(this);
- mLogo.setVisibility(View.GONE);
mLogo.setImageResource(R.drawable.platlogo);
- layout.addView(mLogo, lp);
+ mLogo.setOnTouchListener(mTouchListener);
+ mLogo.requestFocus();
+ mLayout.addView(mLogo, lp);
- mBg = new BubblesDrawable();
- mBg.setLevel(0);
- mBg.avoid = widgetSize / 2;
- mBg.padding = 0.5f * dp;
- mBg.minR = 1 * dp;
- layout.setBackground(mBg);
- layout.setOnLongClickListener(mBg);
+ Log.v(TAG, "Hello");
- setContentView(layout);
+ setContentView(mLayout);
+ }
+
+ private void startAnimating() {
+ mAnim = new TimeAnimator();
+ mAnim.setTimeListener(mTimeListener);
+ mAnim.start();
+ }
+
+ private void stopAnimating() {
+ mAnim.cancel();
+ mAnim = null;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_SPACE) {
+ if (event.getRepeatCount() == 0) {
+ startWarp();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_SPACE) {
+ stopWarp();
+ return true;
+ }
+ return false;
+ }
+
+ private void startWarp() {
+ stopWarp();
+ mWarpAnim = ObjectAnimator.ofFloat(mStarfield, "warp", MIN_WARP, MAX_WARP)
+ .setDuration(LAUNCH_TIME);
+ mWarpAnim.start();
+
+ mLogo.postDelayed(mLaunchNextStage, LAUNCH_TIME + 1000L);
+ }
+
+ private void stopWarp() {
+ if (mWarpAnim != null) {
+ mWarpAnim.cancel();
+ mWarpAnim.removeAllListeners();
+ mWarpAnim = null;
+ }
+ mStarfield.setWarp(1f);
+ mLogo.removeCallbacks(mLaunchNextStage);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ startAnimating();
+ }
+
+ @Override
+ public void onPause() {
+ stopWarp();
+ stopAnimating();
+ super.onPause();
}
private boolean shouldWriteSettings() {
@@ -113,38 +308,14 @@
}
private void launchNextStage(boolean locked) {
- mClock.animate()
- .alpha(0f).scaleX(0.5f).scaleY(0.5f)
- .withEndAction(() -> mClock.setVisibility(View.GONE))
- .start();
-
- mLogo.setAlpha(0f);
- mLogo.setScaleX(0.5f);
- mLogo.setScaleY(0.5f);
- mLogo.setVisibility(View.VISIBLE);
- mLogo.animate()
- .alpha(1f)
- .scaleX(1f)
- .scaleY(1f)
- .setInterpolator(new OvershootInterpolator())
- .start();
-
- mLogo.postDelayed(() -> {
- final ObjectAnimator anim = ObjectAnimator.ofInt(mBg, "level", 0, 10000);
- anim.setInterpolator(new DecelerateInterpolator(1f));
- anim.start();
- },
- 500
- );
-
final ContentResolver cr = getContentResolver();
try {
if (shouldWriteSettings()) {
- Log.v(TAG, "Saving egg unlock=" + locked);
+ Log.v(TAG, "Saving egg locked=" + locked);
syncTouchPressure();
Settings.System.putLong(cr,
- S_EGG_UNLOCK_SETTING,
+ U_EGG_UNLOCK_SETTING,
locked ? 0 : System.currentTimeMillis());
}
} catch (RuntimeException e) {
@@ -152,14 +323,18 @@
}
try {
- startActivity(new Intent(Intent.ACTION_MAIN)
+ final Intent eggActivity = new Intent(Intent.ACTION_MAIN)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK)
- .addCategory("com.android.internal.category.PLATLOGO"));
+ .addCategory("com.android.internal.category.PLATLOGO");
+ Log.v(TAG, "launching: " + eggActivity);
+ startActivity(eggActivity);
} catch (ActivityNotFoundException ex) {
Log.e("com.android.internal.app.PlatLogoActivity", "No more eggs.");
}
- //finish(); // no longer finish upon unlock; it's fun to frob the dial
+ if (FINISH_AFTER_NEXT_STAGE_LAUNCH) {
+ finish(); // we're done here.
+ }
}
static final String TOUCH_STATS = "touch.stats";
@@ -217,266 +392,111 @@
super.onStop();
}
- /**
- * Subclass of AnalogClock that allows the user to flip up the glass and adjust the hands.
- */
- public class SettableAnalogClock extends AnalogClock {
- private int mOverrideHour = -1;
- private int mOverrideMinute = -1;
- private boolean mOverride = false;
+ private static class Starfield extends Drawable {
+ private static final int NUM_STARS = 34; // Build.VERSION_CODES.UPSIDE_DOWN_CAKE
- public SettableAnalogClock(Context context) {
- super(context);
+ private static final int NUM_PLANES = 2;
+ private final float[] mStars = new float[NUM_STARS * 4];
+ private float mVx, mVy;
+ private long mDt = 0;
+ private final Paint mStarPaint;
+
+ private final Random mRng;
+ private final float mSize;
+
+ private final Rect mSpace = new Rect();
+ private float mWarp = 1f;
+
+ private float mBuffer;
+
+ public void setWarp(float warp) {
+ mWarp = warp;
+ }
+
+ public float getWarp() {
+ return mWarp;
+ }
+
+ Starfield(Random rng, float size) {
+ mRng = rng;
+ mSize = size;
+ mStarPaint = new Paint();
+ mStarPaint.setStyle(Paint.Style.STROKE);
+ mStarPaint.setColor(Color.WHITE);
}
@Override
- protected Instant now() {
- final Instant realNow = super.now();
- final ZoneId tz = Clock.systemDefaultZone().getZone();
- final ZonedDateTime zdTime = realNow.atZone(tz);
- if (mOverride) {
- if (mOverrideHour < 0) {
- mOverrideHour = zdTime.getHour();
- }
- return Clock.fixed(zdTime
- .withHour(mOverrideHour)
- .withMinute(mOverrideMinute)
- .withSecond(0)
- .toInstant(), tz).instant();
- } else {
- return realNow;
+ public void onBoundsChange(Rect bounds) {
+ mSpace.set(bounds);
+ mBuffer = mSize * NUM_PLANES * 2 * MAX_WARP;
+ mSpace.inset(-(int) mBuffer, -(int) mBuffer);
+ final float w = mSpace.width();
+ final float h = mSpace.height();
+ for (int i = 0; i < NUM_STARS; i++) {
+ mStars[4 * i] = mRng.nextFloat() * w;
+ mStars[4 * i + 1] = mRng.nextFloat() * h;
+ mStars[4 * i + 2] = mStars[4 * i];
+ mStars[4 * i + 3] = mStars[4 * i + 1];
}
}
- double toPositiveDegrees(double rad) {
- return (Math.toDegrees(rad) + 360 - 90) % 360;
+ public void setVelocity(float x, float y) {
+ mVx = x;
+ mVy = y;
}
@Override
- public boolean onTouchEvent(MotionEvent ev) {
- switch (ev.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- mOverride = true;
- // pass through
- case MotionEvent.ACTION_MOVE:
- measureTouchPressure(ev);
+ public void draw(@NonNull Canvas canvas) {
+ final float dtSec = mDt / 1000f;
+ final float dx = (mVx * dtSec * mWarp);
+ final float dy = (mVy * dtSec * mWarp);
- float x = ev.getX();
- float y = ev.getY();
- float cx = getWidth() / 2f;
- float cy = getHeight() / 2f;
- float angle = (float) toPositiveDegrees(Math.atan2(x - cx, y - cy));
+ final boolean inWarp = mWarp > 1f;
- int minutes = (75 - (int) (angle / 6)) % 60;
- int minuteDelta = minutes - mOverrideMinute;
- if (minuteDelta != 0) {
- if (Math.abs(minuteDelta) > 45 && mOverrideHour >= 0) {
- int hourDelta = (minuteDelta < 0) ? 1 : -1;
- mOverrideHour = (mOverrideHour + 24 + hourDelta) % 24;
- }
- mOverrideMinute = minutes;
- if (mOverrideMinute == 0) {
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- if (getScaleX() == 1f) {
- setScaleX(1.05f);
- setScaleY(1.05f);
- animate().scaleX(1f).scaleY(1f).setDuration(150).start();
- }
- } else {
- performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
- }
+ canvas.drawColor(Color.BLACK); // 0xFF16161D);
- onTimeChanged();
- postInvalidate();
- }
-
- return true;
- case MotionEvent.ACTION_UP:
- if (mOverrideMinute == 0 && (mOverrideHour % 12) == 1) {
- Log.v(TAG, "13:00");
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- launchNextStage(false);
- }
- return true;
- }
- return false;
- }
- }
-
- private static final String[][] EMOJI_SETS = {
- {"🍇", "🍈", "🍉", "🍊", "🍋", "🍌", "🍍", "🥭", "🍎", "🍏", "🍐", "🍑",
- "🍒", "🍓", "🫐", "🥝"},
- {"😺", "😸", "😹", "😻", "😼", "😽", "🙀", "😿", "😾"},
- {"😀", "😃", "😄", "😁", "😆", "😅", "🤣", "😂", "🙂", "🙃", "🫠", "😉", "😊",
- "😇", "🥰", "😍", "🤩", "😘", "😗", "☺️", "😚", "😙", "🥲", "😋", "😛", "😜",
- "🤪", "😝", "🤑", "🤗", "🤭", "🫢", "🫣", "🤫", "🤔", "🫡", "🤐", "🤨", "😐",
- "😑", "😶", "🫥", "😏", "😒", "🙄", "😬", "🤥", "😌", "😔", "😪", "🤤", "😴",
- "😷"},
- { "🤩", "😍", "🥰", "😘", "🥳", "🥲", "🥹" },
- { "🫠" },
- {"💘", "💝", "💖", "💗", "💓", "💞", "💕", "❣", "💔", "❤", "🧡", "💛",
- "💚", "💙", "💜", "🤎", "🖤", "🤍"},
- // {"👁", "️🫦", "👁️"}, // this one is too much
- {"👽", "🛸", "✨", "🌟", "💫", "🚀", "🪐", "🌙", "⭐", "🌍"},
- {"🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘"},
- {"🐙", "🪸", "🦑", "🦀", "🦐", "🐡", "🦞", "🐠", "🐟", "🐳", "🐋", "🐬", "🫧", "🌊",
- "🦈"},
- {"🙈", "🙉", "🙊", "🐵", "🐒"},
- {"♈", "♉", "♊", "♋", "♌", "♍", "♎", "♏", "♐", "♑", "♒", "♓"},
- {"🕛", "🕧", "🕐", "🕜", "🕑", "🕝", "🕒", "🕞", "🕓", "🕟", "🕔", "🕠", "🕕", "🕡",
- "🕖", "🕢", "🕗", "🕣", "🕘", "🕤", "🕙", "🕥", "🕚", "🕦"},
- {"🌺", "🌸", "💮", "🏵️", "🌼", "🌿"},
- {"🐢", "✨", "🌟", "👑"}
- };
-
- static class Bubble {
- public float x, y, r;
- public int color;
- public String text = null;
- }
-
- class BubblesDrawable extends Drawable implements View.OnLongClickListener {
- private static final int MAX_BUBBS = 2000;
-
- private final int[] mColorIds = {
- android.R.color.system_accent3_400,
- android.R.color.system_accent3_500,
- android.R.color.system_accent3_600,
-
- android.R.color.system_accent2_400,
- android.R.color.system_accent2_500,
- android.R.color.system_accent2_600,
- };
-
- private int[] mColors = new int[mColorIds.length];
-
- private int mEmojiSet = -1;
-
- private final Bubble[] mBubbs = new Bubble[MAX_BUBBS];
- private int mNumBubbs;
-
- private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-
- public float avoid = 0f;
- public float padding = 0f;
- public float minR = 0f;
-
- BubblesDrawable() {
- for (int i = 0; i < mColorIds.length; i++) {
- mColors[i] = getColor(mColorIds[i]);
- }
- for (int j = 0; j < mBubbs.length; j++) {
- mBubbs[j] = new Bubble();
- }
- }
-
- @Override
- public void draw(Canvas canvas) {
- if (getLevel() == 0) return;
- final float f = getLevel() / 10000f;
- mPaint.setStyle(Paint.Style.FILL);
- mPaint.setTextAlign(Paint.Align.CENTER);
- int drawn = 0;
- for (int j = 0; j < mNumBubbs; j++) {
- if (mBubbs[j].color == 0 || mBubbs[j].r == 0) continue;
- if (mBubbs[j].text != null) {
- mPaint.setTextSize(mBubbs[j].r * 1.75f);
- canvas.drawText(mBubbs[j].text, mBubbs[j].x,
- mBubbs[j].y + mBubbs[j].r * f * 0.6f, mPaint);
- } else {
- mPaint.setColor(mBubbs[j].color);
- canvas.drawCircle(mBubbs[j].x, mBubbs[j].y, mBubbs[j].r * f, mPaint);
- }
- drawn++;
- }
- }
-
- public void chooseEmojiSet() {
- mEmojiSet = (int) (Math.random() * EMOJI_SETS.length);
- final String[] emojiSet = EMOJI_SETS[mEmojiSet];
- for (int j = 0; j < mBubbs.length; j++) {
- mBubbs[j].text = emojiSet[(int) (Math.random() * emojiSet.length)];
- }
- invalidateSelf();
- }
-
- @Override
- protected boolean onLevelChange(int level) {
- invalidateSelf();
- return true;
- }
-
- @Override
- protected void onBoundsChange(Rect bounds) {
- super.onBoundsChange(bounds);
- randomize();
- }
-
- private void randomize() {
- final float w = getBounds().width();
- final float h = getBounds().height();
- final float maxR = Math.min(w, h) / 3f;
- mNumBubbs = 0;
- if (avoid > 0f) {
- mBubbs[mNumBubbs].x = w / 2f;
- mBubbs[mNumBubbs].y = h / 2f;
- mBubbs[mNumBubbs].r = avoid;
- mBubbs[mNumBubbs].color = 0;
- mNumBubbs++;
- }
- for (int j = 0; j < MAX_BUBBS; j++) {
- // a simple but time-tested bubble-packing algorithm:
- // 1. pick a spot
- // 2. shrink the bubble until it is no longer overlapping any other bubble
- // 3. if the bubble hasn't popped, keep it
- int tries = 5;
- while (tries-- > 0) {
- float x = (float) Math.random() * w;
- float y = (float) Math.random() * h;
- float r = Math.min(Math.min(x, w - x), Math.min(y, h - y));
-
- // shrink radius to fit other bubbs
- for (int i = 0; i < mNumBubbs; i++) {
- r = (float) Math.min(r,
- Math.hypot(x - mBubbs[i].x, y - mBubbs[i].y) - mBubbs[i].r
- - padding);
- if (r < minR) break;
- }
-
- if (r >= minR) {
- // we have found a spot for this bubble to live, let's save it and move on
- r = Math.min(maxR, r);
-
- mBubbs[mNumBubbs].x = x;
- mBubbs[mNumBubbs].y = y;
- mBubbs[mNumBubbs].r = r;
- mBubbs[mNumBubbs].color = mColors[(int) (Math.random() * mColors.length)];
- mNumBubbs++;
- break;
- }
+ if (mDt > 0 && mDt < 1000) {
+ canvas.translate(
+ -(mBuffer) + mRng.nextFloat() * (mWarp - 1f),
+ -(mBuffer) + mRng.nextFloat() * (mWarp - 1f)
+ );
+ final float w = mSpace.width();
+ final float h = mSpace.height();
+ for (int i = 0; i < NUM_STARS; i++) {
+ final int plane = (int) ((((float) i) / NUM_STARS) * NUM_PLANES) + 1;
+ mStars[4 * i + 2] = (mStars[4 * i + 2] + dx * plane + w) % w;
+ mStars[4 * i + 3] = (mStars[4 * i + 3] + dy * plane + h) % h;
+ mStars[4 * i + 0] = inWarp ? mStars[4 * i + 2] - dx * mWarp * 2 * plane : -100;
+ mStars[4 * i + 1] = inWarp ? mStars[4 * i + 3] - dy * mWarp * 2 * plane : -100;
}
}
- Log.v(TAG, String.format("successfully placed %d bubbles (%d%%)",
- mNumBubbs, (int) (100f * mNumBubbs / MAX_BUBBS)));
+ final int slice = (mStars.length / NUM_PLANES / 4) * 4;
+ for (int p = 0; p < NUM_PLANES; p++) {
+ mStarPaint.setStrokeWidth(mSize * (p + 1));
+ if (inWarp) {
+ canvas.drawLines(mStars, p * slice, slice, mStarPaint);
+ }
+ canvas.drawPoints(mStars, p * slice, slice, mStarPaint);
+ }
}
@Override
- public void setAlpha(int alpha) { }
+ public void setAlpha(int alpha) {
+
+ }
@Override
- public void setColorFilter(ColorFilter colorFilter) { }
+ public void setColorFilter(@Nullable ColorFilter colorFilter) {
+
+ }
@Override
public int getOpacity() {
- return TRANSLUCENT;
+ return PixelFormat.OPAQUE;
}
- @Override
- public boolean onLongClick(View v) {
- if (getLevel() == 0) return false;
- chooseEmojiSet();
- return true;
+ public void update(long dt) {
+ mDt = dt;
}
}
-
-}
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index c9e7600..6b8ae94 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -85,6 +85,10 @@
/** Gating the holding of WakeLocks until NLSes are told about a new notification. */
public static final Flag WAKE_LOCK_FOR_POSTING_NOTIFICATION =
devFlag("persist.sysui.notification.wake_lock_for_posting_notification");
+
+ /** Gating storing NotificationRankingUpdate ranking map in shared memory. */
+ public static final Flag RANKING_UPDATE_ASHMEM = devFlag(
+ "persist.sysui.notification.ranking_update_ashmem");
}
//// == End of flags. Everything below this line is the implementation. == ////
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 92cfa67..66b69c5 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -47,6 +47,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -160,9 +161,17 @@
*/
public static final int VERIFY_FLAG_REQUEST_GK_PW_HANDLE = 1 << 0;
+ /**
+ * Flag provided to {@link #verifyCredential(LockscreenCredential, int, int)} . If set, the
+ * method writes the password data to the repair mode file after the credential is verified
+ * successfully.
+ */
+ public static final int VERIFY_FLAG_WRITE_REPAIR_MODE_PW = 1 << 1;
+
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {
- VERIFY_FLAG_REQUEST_GK_PW_HANDLE
+ VERIFY_FLAG_REQUEST_GK_PW_HANDLE,
+ VERIFY_FLAG_WRITE_REPAIR_MODE_PW
})
public @interface VerifyFlag {}
@@ -201,6 +210,8 @@
public static final String CURRENT_LSKF_BASED_PROTECTOR_ID_KEY = "sp-handle";
public static final String PASSWORD_HISTORY_DELIMITER = ",";
+ private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
+
/**
* drives the pin auto confirmation feature availability in code logic.
*/
@@ -1847,6 +1858,37 @@
}
/**
+ * Return {@code true} if repair mode is supported by the device.
+ */
+ public static boolean isRepairModeSupported(Context context) {
+ return context.getResources().getBoolean(
+ com.android.internal.R.bool.config_repairModeSupported);
+ }
+
+ /**
+ * Return {@code true} if repair mode is active on the device.
+ */
+ public static boolean isRepairModeActive(Context context) {
+ return Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.REPAIR_MODE_ACTIVE, /* def= */ 0) > 0;
+ }
+
+ /**
+ * Return {@code true} if repair mode is supported by the device and the user has been granted
+ * admin privileges.
+ */
+ public static boolean canUserEnterRepairMode(Context context, UserInfo info) {
+ return info != null && info.isAdmin() && isRepairModeSupported(context);
+ }
+
+ /**
+ * Return {@code true} if GSI is running on the device.
+ */
+ public static boolean isGsiRunning() {
+ return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
+ }
+
+ /**
* Attempt to rederive the unified work challenge for the specified profile user and unlock the
* user. If successful, this would allow the user to leave quiet mode automatically without
* additional user authentication.
diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp
index 986dbe9..e729750 100644
--- a/core/jni/android_window_ScreenCapture.cpp
+++ b/core/jni/android_window_ScreenCapture.cpp
@@ -81,22 +81,28 @@
public:
explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) {
env->GetJavaVM(&mVm);
- mConsumerObject = env->NewGlobalRef(jobject);
- LOG_ALWAYS_FATAL_IF(!mConsumerObject, "Failed to make global ref");
+ mConsumerWeak = env->NewWeakGlobalRef(jobject);
}
~ScreenCaptureListenerWrapper() {
- if (mConsumerObject) {
- getenv()->DeleteGlobalRef(mConsumerObject);
- mConsumerObject = nullptr;
+ if (mConsumerWeak) {
+ getenv()->DeleteWeakGlobalRef(mConsumerWeak);
+ mConsumerWeak = nullptr;
}
}
binder::Status onScreenCaptureCompleted(
const gui::ScreenCaptureResults& captureResults) override {
JNIEnv* env = getenv();
+
+ ScopedLocalRef<jobject> consumer{env, env->NewLocalRef(mConsumerWeak)};
+ if (consumer == nullptr) {
+ ALOGE("ScreenCaptureListenerWrapper consumer not alive.");
+ return binder::Status::ok();
+ }
+
if (!captureResults.fenceResult.ok() || captureResults.buffer == nullptr) {
- env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, nullptr);
+ env->CallVoidMethod(consumer.get(), gConsumerClassInfo.accept, nullptr);
checkAndClearException(env, "accept");
return binder::Status::ok();
}
@@ -111,7 +117,7 @@
captureResults.capturedSecureLayers,
captureResults.capturedHdrLayers);
checkAndClearException(env, "builder");
- env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, screenshotHardwareBuffer);
+ env->CallVoidMethod(consumer.get(), gConsumerClassInfo.accept, screenshotHardwareBuffer);
checkAndClearException(env, "accept");
env->DeleteLocalRef(jhardwareBuffer);
env->DeleteLocalRef(screenshotHardwareBuffer);
@@ -119,7 +125,7 @@
}
private:
- jobject mConsumerObject;
+ jweak mConsumerWeak;
JavaVM* mVm;
JNIEnv* getenv() {
diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml
index da21486..f3acab0 100644
--- a/core/res/res/drawable-nodpi/platlogo.xml
+++ b/core/res/res/drawable-nodpi/platlogo.xml
@@ -13,33 +13,186 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector android:height="128dp"
- android:width="128dp"
- android:viewportHeight="24"
- android:viewportWidth="24"
- xmlns:android="http://schemas.android.com/apk/res/android">
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="512dp"
+ android:height="512dp"
+ android:viewportWidth="512"
+ android:viewportHeight="512">
+ <path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:startX="256"
+ android:startY="21.81"
+ android:endX="256"
+ android:endY="350.42"
+ android:type="linear">
+ <item android:offset="0" android:color="#FF073042"/>
+ <item android:offset="1" android:color="#FF073042"/>
+ </gradient>
+ </aapt:attr>
+ </path>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
- android:fillColor="@android:color/system_accent1_400"
- android:pathData="M11,0.3c0.6,-0.3 1.4,-0.3 2,0l0.6,0.4c0.7,0.4 1.4,0.6 2.2,0.6l0.7,-0.1c0.7,0 1.4,0.3 1.8,0.9l0.3,0.6c0.4,0.7 1,1.2 1.7,1.5L21,4.5c0.7,0.3 1.1,0.9 1.2,1.7v0.7C22.2,7.7 22.5,8.4 23,9l0.4,0.5c0.4,0.6 0.5,1.3 0.2,2l-0.3,0.6c-0.3,0.7 -0.4,1.5 -0.3,2.3l0.1,0.7c0.1,0.7 -0.2,1.4 -0.7,1.9L22,17.5c-0.6,0.5 -1.1,1.1 -1.3,1.9L20.5,20c-0.2,0.7 -0.8,1.2 -1.5,1.4l-0.7,0.1c-0.8,0.2 -1.4,0.5 -2,1.1l-0.5,0.5c-0.5,0.5 -1.3,0.7 -2,0.5l-0.6,-0.2c-0.8,-0.2 -1.5,-0.2 -2.3,0l-0.6,0.2c-0.7,0.2 -1.5,0 -2,-0.5l-0.5,-0.5c-0.5,-0.5 -1.2,-0.9 -2,-1.1L5,21.4c-0.7,-0.2 -1.3,-0.7 -1.5,-1.4l-0.2,-0.7C3.1,18.6 2.6,18 2,17.5l-0.6,-0.4c-0.6,-0.5 -0.8,-1.2 -0.7,-1.9l0.1,-0.7c0.1,-0.8 0,-1.6 -0.3,-2.3l-0.3,-0.6c-0.3,-0.7 -0.2,-1.4 0.2,-2L1,9c0.5,-0.6 0.7,-1.4 0.8,-2.2V6.2C1.9,5.5 2.3,4.8 3,4.5l0.6,-0.3c0.7,-0.3 1.3,-0.9 1.7,-1.5l0.3,-0.6c0.4,-0.6 1.1,-1 1.8,-0.9l0.7,0.1c0.8,0 1.6,-0.2 2.2,-0.6L11,0.3z"
- />
+ android:pathData="m195.27,187.64l2.25,-6.69c13.91,78.13 50.84,284.39 50.84,50.33 0,-0.97 0.72,-1.81 1.62,-1.81h12.69c0.9,0 1.62,0.83 1.62,1.8 -0.2,409.91 -69.03,-43.64 -69.03,-43.64Z"
+ android:fillColor="#3ddc84"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
- android:pathData="M6.3,6.5l3,0l0,12.2"
- android:strokeWidth="2.22"
- android:strokeColor="@android:color/system_accent3_800"
- />
+ android:pathData="m158.77,180.68l-33.17,57.45c-1.9,3.3 -0.77,7.52 2.53,9.42 3.3,1.9 7.52,0.77 9.42,-2.53l33.59,-58.17c54.27,24.33 116.34,24.33 170.61,0l33.59,58.17c1.97,3.26 6.21,4.3 9.47,2.33 3.17,-1.91 4.26,-5.99 2.47,-9.23l-33.16,-57.45c56.95,-30.97 95.91,-88.64 101.61,-156.76H57.17c5.7,68.12 44.65,125.79 101.61,156.76Z"
+ android:fillColor="#fff"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
- android:pathData="M12.3,6.5h4l-2,4c2.2,0.3 3.6,2.4 3.3,4.5c-0.3,1.9 -1.9,3.3 -3.8,3.3c-0.5,0 -1,-0.1 -1.4,-0.3"
- android:strokeWidth="2.22"
- android:strokeColor="@android:color/system_accent3_800"
- />
+ android:pathData="M250.26,187.66L262.17,187.66A2.13,2.13 0,0 1,264.3 189.78L264.3,217.85A2.13,2.13 0,0 1,262.17 219.98L250.26,219.98A2.13,2.13 0,0 1,248.14 217.85L248.14,189.78A2.13,2.13 0,0 1,250.26 187.66z"
+ android:fillColor="#3ddc84"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
- android:pathData="M6.3,6.5l3,0l0,12.2"
- android:strokeWidth="0.56"
- android:strokeColor="@android:color/system_neutral1_100"
- />
+ android:pathData="M250.12,170.29L262.32,170.29A1.98,1.98 0,0 1,264.3 172.26L264.3,176.39A1.98,1.98 0,0 1,262.32 178.37L250.12,178.37A1.98,1.98 0,0 1,248.14 176.39L248.14,172.26A1.98,1.98 0,0 1,250.12 170.29z"
+ android:fillColor="#3ddc84"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
- android:pathData="M12.3,6.5h4l-2,4c2.2,0.3 3.6,2.4 3.3,4.5c-0.3,1.9 -1.9,3.3 -3.8,3.3c-0.5,0 -1,-0.1 -1.4,-0.3"
- android:strokeWidth="0.56"
- android:strokeColor="@android:color/system_neutral1_100"
- />
+ android:pathData="M171.92,216.82h4.04v4.04h-4.04z"
+ android:fillColor="#fff"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
+ <path
+ android:pathData="M188.8,275.73h4.04v4.04h-4.04z"
+ android:fillColor="#fff"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
+ <path
+ android:pathData="M369.04,337.63h4.04v4.04h-4.04z"
+ android:fillColor="#fff"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
+ <path
+ android:pathData="M285.93,252.22h4.04v4.04h-4.04z"
+ android:fillColor="#fff"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
+ <path
+ android:pathData="M318.96,218.84h4.04v4.04h-4.04z"
+ android:fillColor="#fff"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
+ <path
+ android:pathData="M294.05,288.55h4.04v4.04h-4.04z"
+ android:fillColor="#fff"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
+ <path
+ android:pathData="M330.82,273.31h8.08v8.08h-8.08z"
+ android:fillColor="#fff"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
+ <path
+ android:pathData="M188.8,298.95h4.04v4.04h-4.04z"
+ android:fillColor="#fff"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
+ <path
+ android:pathData="M220.14,238.94h8.08v8.08h-8.08z"
+ android:fillColor="#fff"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
+ <path
+ android:pathData="M272.1,318.9h8.08v8.08h-8.08z"
+ android:fillColor="#fff"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
+ <path
+ android:pathData="M293.34,349.25h8.08v8.08h-8.08z"
+ android:fillColor="#fff"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
+ <path
+ android:pathData="M161.05,254.24h8.08v8.08h-8.08z"
+ android:fillColor="#fff"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
+ <path
+ android:pathData="M378.92,192h8.08v8.08h-8.08z"
+ android:fillColor="#fff"/>
+ </group>
+ <group>
+ <clip-path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
+ <path
+ android:pathData="M137.87,323.7h8.08v8.08h-8.08z"
+ android:fillColor="#fff"/>
+ </group>
+ <path
+ android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"
+ android:strokeWidth="56.561"
+ android:fillColor="#00000000"
+ android:strokeColor="#f86734"/>
+ <path
+ android:pathData="m256.22,126.57c-6.69,0 -12.12,5.27 -12.12,11.77v14.52c0,1.25 1.02,2.27 2.27,2.27h0c1.25,0 2.27,-1.02 2.27,-2.27v-3.91c0,-2.51 2.04,-4.55 4.55,-4.55h6.06c2.51,0 4.55,2.04 4.55,4.55v3.91c0,1.25 1.02,2.27 2.27,2.27s2.27,-1.02 2.27,-2.27v-14.52c0,-6.5 -5.43,-11.77 -12.12,-11.77Z"
+ android:fillColor="#3ddc84"/>
+ <path
+ android:pathData="m93.34,116.36l3.85,-4.36 29.64,9.76 -4.44,5.03 -6.23,-2.1 -7.86,8.91 2.86,5.92 -4.43,5.03 -13.39,-28.18ZM110.43,122.76l-8.86,-3.02 4.11,8.41 4.76,-5.39Z"
+ android:fillColor="#fff"/>
+ <path
+ android:pathData="m153.62,100.85l-21.71,-6.2 10.38,14.38 -5.21,3.76 -16.78,-23.26 4.49,-3.24 21.65,6.19 -10.35,-14.35 5.24,-3.78 16.78,23.26 -4.49,3.24Z"
+ android:fillColor="#fff"/>
+ <path
+ android:pathData="m161.46,63.15l8.99,-3.84c7.43,-3.18 15.96,0.12 19.09,7.44 3.13,7.32 -0.38,15.76 -7.81,18.94l-8.99,3.84 -11.28,-26.38ZM179.41,80.26c4.46,-1.91 5.96,-6.72 4.15,-10.96 -1.81,-4.24 -6.33,-6.48 -10.79,-4.57l-3.08,1.32 6.64,15.53 3.08,-1.32Z"
+ android:fillColor="#fff"/>
+ <path
+ android:pathData="m204.23,47.57l11.1,-2.2c5.31,-1.05 9.47,2.08 10.4,6.76 0.72,3.65 -0.76,6.37 -4.07,8.34l12.4,10.44 -7.57,1.5 -11.65,-9.76 -1.03,0.2 2.3,11.61 -6.3,1.25 -5.57,-28.14ZM216.78,56.7c1.86,-0.37 3,-1.71 2.68,-3.33 -0.34,-1.7 -1.88,-2.43 -3.74,-2.06l-4.04,0.8 1.07,5.39 4.04,-0.8Z"
+ android:fillColor="#fff"/>
+ <path
+ android:pathData="m244.29,55.6c0.13,-8.16 6.86,-14.72 15.06,-14.58 8.16,0.13 14.72,6.9 14.58,15.06s-6.91,14.72 -15.06,14.58c-8.2,-0.13 -14.71,-6.9 -14.58,-15.06ZM267.44,55.98c0.08,-4.64 -3.54,-8.66 -8.18,-8.74 -4.68,-0.08 -8.42,3.82 -8.5,8.47 -0.08,4.65 3.54,8.66 8.22,8.74 4.64,0.08 8.39,-3.82 8.46,-8.47Z"
+ android:fillColor="#fff"/>
+ <path
+ android:pathData="m294.39,44.84l6.31,1.23 -5.49,28.16 -6.31,-1.23 5.49,-28.16Z"
+ android:fillColor="#fff"/>
+ <path
+ android:pathData="m321.94,51.41l9.14,3.48c7.55,2.88 11.39,11.17 8.56,18.61 -2.83,7.44 -11.22,11.07 -18.77,8.19l-9.14,-3.48 10.22,-26.8ZM322.96,76.19c4.53,1.73 8.95,-0.69 10.6,-5 1.64,-4.3 -0.05,-9.06 -4.58,-10.78l-3.13,-1.19 -6.01,15.78 3.13,1.19Z"
+ android:fillColor="#fff"/>
+ <path
+ android:pathData="m381.41,89.24l-4.21,-3.21 3.65,-4.78 9.06,6.91 -17.4,22.81 -4.85,-3.7 13.75,-18.02Z"
+ android:fillColor="#fff"/>
+ <path
+ android:pathData="m397.96,126.37l-9.56,-10.26 3.61,-3.36 22.8,-1.25 3.74,4.02 -12.35,11.51 2.51,2.69 -4.08,3.8 -2.51,-2.69 -4.55,4.24 -4.16,-4.46 4.55,-4.24ZM407.83,117.17l-10.28,0.58 4.49,4.82 5.79,-5.4Z"
+ android:fillColor="#fff"/>
</vector>
+
diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
new file mode 100644
index 0000000..a84ac55
--- /dev/null
+++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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 android.service.notification;
+
+import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@SmallTest
+@RunWith(Parameterized.class)
+public class NotificationRankingUpdateTest {
+
+ private static final String NOTIFICATION_CHANNEL_ID = "test_channel_id";
+ private static final String TEST_KEY = "key";
+
+ private NotificationChannel mNotificationChannel;
+
+ // TODO(b/284297289): remove this flag set once resolved.
+ @Parameterized.Parameters(name = "rankingUpdateAshmem={0}")
+ public static Boolean[] getRankingUpdateAshmem() {
+ return new Boolean[] { true, false };
+ }
+
+ @Parameterized.Parameter
+ public boolean mRankingUpdateAshmem;
+
+ @Before
+ public void setUp() {
+ mNotificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "test channel",
+ NotificationManager.IMPORTANCE_DEFAULT);
+
+ SystemUiSystemPropertiesFlags.TEST_RESOLVER = flag -> {
+ if (flag.mSysPropKey.equals(RANKING_UPDATE_ASHMEM.mSysPropKey)) {
+ return mRankingUpdateAshmem;
+ }
+ return new SystemUiSystemPropertiesFlags.DebugResolver().isEnabled(flag);
+ };
+ }
+
+ @After
+ public void tearDown() {
+ SystemUiSystemPropertiesFlags.TEST_RESOLVER = null;
+ }
+
+ public NotificationListenerService.Ranking createTestRanking(String key, int rank) {
+ NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
+ ranking.populate(
+ /* key= */ key,
+ /* rank= */ rank,
+ /* matchesInterruptionFilter= */ false,
+ /* visibilityOverride= */ 0,
+ /* suppressedVisualEffects= */ 0,
+ mNotificationChannel.getImportance(),
+ /* explanation= */ null,
+ /* overrideGroupKey= */ null,
+ mNotificationChannel,
+ /* overridePeople= */ null,
+ /* snoozeCriteria= */ null,
+ /* showBadge= */ true,
+ /* userSentiment= */ 0,
+ /* hidden= */ false,
+ /* lastAudiblyAlertedMs= */ -1,
+ /* noisy= */ false,
+ /* smartActions= */ null,
+ /* smartReplies= */ null,
+ /* canBubble= */ false,
+ /* isTextChanged= */ false,
+ /* isConversation= */ false,
+ /* shortcutInfo= */ null,
+ /* rankingAdjustment= */ 0,
+ /* isBubble= */ false,
+ /* proposedImportance= */ 0,
+ /* sensitiveContent= */ false
+ );
+ return 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);
+ assertTrue(retrievedRankingUpdate.isFdNotNullAndClosed());
+ NotificationListenerService.Ranking retrievedRanking =
+ new NotificationListenerService.Ranking();
+ assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking));
+ assertEquals(123, retrievedRanking.getRank());
+ assertTrue(retrievedRankingUpdate.equals(rankingUpdate));
+ parceledRankingUpdate.recycle();
+ }
+
+ @Test
+ public void testRankingUpdate_emptyParcelInCheck() {
+ NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
+ NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking});
+
+ Parcel parceledRankingUpdate = Parcel.obtain();
+ rankingUpdate.writeToParcel(parceledRankingUpdate, 0);
+
+ // This will fail to read the parceledRankingUpdate, because the data position hasn't
+ // been reset, so it'll find no data to read.
+ NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate(
+ parceledRankingUpdate);
+ assertNull(retrievedRankingUpdate.getRankingMap());
+ }
+
+ @Test
+ public void testRankingUpdate_describeContents() {
+ NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
+ NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking});
+ assertEquals(0, rankingUpdate.describeContents());
+ }
+
+ @Test
+ public void testRankingUpdate_equals() {
+ NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
+ NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking});
+ // Reflexive equality.
+ assertTrue(rankingUpdate.equals(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);
+ NotificationRankingUpdate rankingUpdate2 = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking2});
+ assertFalse(rankingUpdate.equals(rankingUpdate2));
+
+ // Same ranking contents equality.
+ ranking2 = createTestRanking(TEST_KEY, 123);
+ rankingUpdate2 = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking2});
+ assertTrue(rankingUpdate.equals(rankingUpdate2));
+ }
+}
diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp
index 96811be..741e535 100644
--- a/core/tests/mockingcoretests/Android.bp
+++ b/core/tests/mockingcoretests/Android.bp
@@ -56,7 +56,10 @@
],
platform_apis: true,
- test_suites: ["device-tests"],
+ test_suites: [
+ "device-tests",
+ "automotive-tests",
+ ],
certificate: "platform",
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index 7571e44..d129891 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -24,6 +24,7 @@
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyStoreCryptoOperation;
+import android.system.keystore2.Authorization;
import libcore.util.EmptyArray;
@@ -119,6 +120,14 @@
mCipher = null;
}
+ private Authorization[] getKeyCharacteristics(Key key) {
+ if (!(key instanceof AndroidKeyStoreKey)) {
+ return new Authorization[] {};
+ }
+
+ return ((AndroidKeyStoreKey) key).getAuthorizations();
+ }
+
@Override
protected final void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
@@ -173,7 +182,7 @@
init(opmode, key, random);
initAlgorithmSpecificParameters();
try {
- ensureKeystoreOperationInitialized();
+ ensureKeystoreOperationInitialized(getKeyCharacteristics(key));
} catch (InvalidAlgorithmParameterException e) {
throw new InvalidKeyException(e);
}
@@ -206,7 +215,7 @@
try {
init(opmode, key, random);
initAlgorithmSpecificParameters(params);
- ensureKeystoreOperationInitialized();
+ ensureKeystoreOperationInitialized(getKeyCharacteristics(key));
success = true;
} finally {
if (!success) {
@@ -236,7 +245,7 @@
try {
init(opmode, key, random);
initAlgorithmSpecificParameters(params);
- ensureKeystoreOperationInitialized();
+ ensureKeystoreOperationInitialized(getKeyCharacteristics(key));
success = true;
} finally {
if (!success) {
@@ -310,7 +319,8 @@
mCachedException = null;
}
- private void ensureKeystoreOperationInitialized() throws InvalidKeyException,
+ private void ensureKeystoreOperationInitialized(Authorization[] keyCharacteristics)
+ throws InvalidKeyException,
InvalidAlgorithmParameterException {
if (mMainDataStreamer != null) {
return;
@@ -323,7 +333,7 @@
}
List<KeyParameter> parameters = new ArrayList<>();
- addAlgorithmSpecificParametersToBegin(parameters);
+ addAlgorithmSpecificParametersToBegin(parameters, keyCharacteristics);
int purpose;
if (mKeymasterPurposeOverride != -1) {
@@ -404,7 +414,7 @@
return null;
}
try {
- ensureKeystoreOperationInitialized();
+ ensureKeystoreOperationInitialized(getKeyCharacteristics(mKey));
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
mCachedException = e;
return null;
@@ -520,7 +530,7 @@
}
try {
- ensureKeystoreOperationInitialized();
+ ensureKeystoreOperationInitialized(getKeyCharacteristics(mKey));
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
mCachedException = e;
return;
@@ -597,7 +607,7 @@
}
try {
- ensureKeystoreOperationInitialized();
+ ensureKeystoreOperationInitialized(getKeyCharacteristics(mKey));
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
}
@@ -1012,6 +1022,22 @@
@NonNull List<KeyParameter> parameters);
/**
+ * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation,
+ * including the key characteristics. This is useful in case the parameters to {@code begin}
+ * depend on how the key was generated.
+ * The default implementation provided here simply ignores these key characteristics because
+ * they are not be needed for most engines.
+ *
+ * @param parameters keystore/keymaster arguments to be populated with algorithm-specific
+ * parameters.
+ * @param keyCharacteristics The key's characteristics.
+ */
+ protected void addAlgorithmSpecificParametersToBegin(
+ @NonNull List<KeyParameter> parameters, Authorization[] keyCharacteristics) {
+ addAlgorithmSpecificParametersToBegin(parameters);
+ }
+
+ /**
* Invoked to obtain algorithm-specific parameters from the result of the KeyStore's
* {@code begin} operation.
*
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
index e9b66aa..3bb2564 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
@@ -288,16 +288,34 @@
}
}
+ private static boolean isMgfDigestTagPresentInKeyProperties(
+ Authorization[] keyCharacteristics) {
+ for (Authorization authorization : keyCharacteristics) {
+ if (authorization.keyParameter.tag == KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
@Override
protected final void addAlgorithmSpecificParametersToBegin(
- @NonNull List<KeyParameter> parameters) {
- super.addAlgorithmSpecificParametersToBegin(parameters);
+ @NonNull List<KeyParameter> parameters, Authorization[] keyCharacteristics) {
+ super.addAlgorithmSpecificParametersToBegin(parameters, keyCharacteristics);
parameters.add(KeyStore2ParameterUtils.makeEnum(
KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
));
- parameters.add(KeyStore2ParameterUtils.makeEnum(
- KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mKeymasterMgf1Digest
- ));
+ // Only add the KM_TAG_RSA_OAEP_MGF_DIGEST tag to begin() if the MGF Digest is
+ // present in the key properties. Keys generated prior to Android 14 did not have
+ // this tag (Keystore didn't add it) so specifying any MGF digest tag would cause
+ // a begin() operation (on an Android 14 device) to fail (with a key that was generated
+ // on Android 13 or below).
+ if (isMgfDigestTagPresentInKeyProperties(keyCharacteristics)) {
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mKeymasterMgf1Digest
+ ));
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 2141259..2e3f604 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -416,7 +416,7 @@
<!-- The radius of the caption menu shadow. -->
<dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen>
- <dimen name="freeform_resize_handle">30dp</dimen>
+ <dimen name="freeform_resize_handle">15dp</dimen>
<dimen name="freeform_resize_corner">44dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 1467741..c48f2fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -80,8 +80,7 @@
import android.view.WindowInsets;
import android.view.WindowManager;
import android.window.ScreenCapture;
-import android.window.ScreenCapture.ScreenCaptureListener;
-import android.window.ScreenCapture.ScreenshotSync;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
@@ -1230,10 +1229,11 @@
/**
* Performs a screenshot that may exclude the bubble layer, if one is present. The screenshot
- * can be access via the supplied {@link ScreenshotSync#get()} asynchronously.
+ * can be access via the supplied {@link SynchronousScreenCaptureListener#getBuffer()}
+ * asynchronously.
*/
public void getScreenshotExcludingBubble(int displayId,
- Pair<ScreenCaptureListener, ScreenshotSync> screenCaptureListener) {
+ SynchronousScreenCaptureListener screenCaptureListener) {
try {
ScreenCapture.CaptureArgs args = null;
if (mStackView != null) {
@@ -1248,7 +1248,7 @@
}
}
- mWmService.captureDisplay(displayId, args, screenCaptureListener.first);
+ mWmService.captureDisplay(displayId, args, screenCaptureListener);
} catch (RemoteException e) {
Log.e(TAG, "Failed to capture screenshot");
}
@@ -2219,15 +2219,15 @@
@Override
@Nullable
- public ScreenshotSync getScreenshotExcludingBubble(int displayId) {
- Pair<ScreenCaptureListener, ScreenshotSync> screenCaptureListener =
+ public SynchronousScreenCaptureListener getScreenshotExcludingBubble(int displayId) {
+ SynchronousScreenCaptureListener screenCaptureListener =
ScreenCapture.createSyncCaptureListener();
mMainExecutor.execute(
() -> BubbleController.this.getScreenshotExcludingBubble(displayId,
screenCaptureListener));
- return screenCaptureListener.second;
+ return screenCaptureListener;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 259f692..4d329dd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.bubbles;
-import static android.window.ScreenCapture.ScreenshotSync;
-
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.PARAMETER;
@@ -34,6 +32,7 @@
import android.util.Pair;
import android.util.SparseArray;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
@@ -150,13 +149,14 @@
boolean isAppBubbleTaskId(int taskId);
/**
- * @return a {@link ScreenshotSync} after performing a screenshot that may exclude the bubble
- * layer, if one is present. The underlying {@link ScreenshotHardwareBuffer} can be access via
- * {@link ScreenshotSync#get()} asynchronously and care should be taken to
- * {@link HardwareBuffer#close()} the associated
- * {@link ScreenshotHardwareBuffer#getHardwareBuffer()} when no longer required.
+` * @return a {@link SynchronousScreenCaptureListener} after performing a screenshot that may
+ * exclude the bubble layer, if one is present. The underlying
+ * {@link ScreenshotHardwareBuffer} can be accessed via
+ * {@link SynchronousScreenCaptureListener#getBuffer()} asynchronously and care should be taken
+ * to {@link HardwareBuffer#close()} the associated
+ * {@link ScreenshotHardwareBuffer#getHardwareBuffer()} when no longer required.`
*/
- ScreenshotSync getScreenshotExcludingBubble(int displayId);
+ SynchronousScreenCaptureListener getScreenshotExcludingBubble(int displayId);
/**
* @return a bubble that matches the provided shortcutId, if one exists.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
index ae1f433..72702e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
@@ -79,7 +79,7 @@
}
/** Query all listeners for changes that should happen on display change. */
- public void dispatchOnDisplayChange(WindowContainerTransaction outWct, int displayId,
+ void dispatchOnDisplayChange(WindowContainerTransaction outWct, int displayId,
int fromRotation, int toRotation, DisplayAreaInfo newDisplayAreaInfo) {
for (OnDisplayChangingListener c : mDisplayChangeListener) {
c.onDisplayChange(displayId, fromRotation, toRotation, newDisplayAreaInfo, outWct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index f07ea75..8353900 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -29,6 +29,7 @@
import android.view.IDisplayWindowListener;
import android.view.IWindowManager;
import android.view.InsetsState;
+import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
@@ -85,11 +86,6 @@
}
}
- /** Get the DisplayChangeController. */
- public DisplayChangeController getChangeController() {
- return mChangeController;
- }
-
/**
* Gets a display by id from DisplayManager.
*/
@@ -195,6 +191,26 @@
}
}
+
+ /** Called when a display rotate requested. */
+ public void onDisplayRotateRequested(WindowContainerTransaction wct, int displayId,
+ int fromRotation, int toRotation) {
+ synchronized (mDisplays) {
+ final DisplayRecord dr = mDisplays.get(displayId);
+ if (dr == null) {
+ Slog.w(TAG, "Skipping Display rotate on non-added display.");
+ return;
+ }
+
+ if (dr.mDisplayLayout != null) {
+ dr.mDisplayLayout.rotateTo(dr.mContext.getResources(), toRotation);
+ }
+
+ mChangeController.dispatchOnDisplayChange(
+ wct, displayId, fromRotation, toRotation, null /* newDisplayAreaInfo */);
+ }
+ }
+
private void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
synchronized (mDisplays) {
final DisplayRecord dr = mDisplays.get(displayId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 467e9c7..1959eb0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -300,9 +300,12 @@
return mAllowSeamlessRotationDespiteNavBarMoving;
}
- /** @return whether the navigation bar will change sides during rotation. */
+ /**
+ * Returns {@code true} if the navigation bar will change sides during rotation and the display
+ * is not square.
+ */
public boolean navigationBarCanMove() {
- return mNavigationBarCanMove;
+ return mNavigationBarCanMove && mWidth != mHeight;
}
/** @return the rotation that would make the physical display "upside down". */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index d016e73c..7b1db54 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -500,36 +500,62 @@
request
)
// Check if we should skip handling this transition
+ var reason = ""
val shouldHandleRequest =
when {
// Only handle open or to front transitions
- request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> false
+ request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> {
+ reason = "transition type not handled (${request.type})"
+ false
+ }
// Only handle when it is a task transition
- request.triggerTask == null -> false
+ request.triggerTask == null -> {
+ reason = "triggerTask is null"
+ false
+ }
// Only handle standard type tasks
- request.triggerTask.activityType != ACTIVITY_TYPE_STANDARD -> false
+ request.triggerTask.activityType != ACTIVITY_TYPE_STANDARD -> {
+ reason = "activityType not handled (${request.triggerTask.activityType})"
+ false
+ }
// Only handle fullscreen or freeform tasks
request.triggerTask.windowingMode != WINDOWING_MODE_FULLSCREEN &&
- request.triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> false
+ request.triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> {
+ reason = "windowingMode not handled (${request.triggerTask.windowingMode})"
+ false
+ }
// Otherwise process it
else -> true
}
if (!shouldHandleRequest) {
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: skipping handleRequest reason=%s",
+ reason
+ )
return null
}
val task: RunningTaskInfo = request.triggerTask
- return when {
+ val result = when {
// If display has tasks stashed, handle as stashed launch
desktopModeTaskRepository.isStashed(task.displayId) -> handleStashedTaskLaunch(task)
// Check if fullscreen task should be updated
task.windowingMode == WINDOWING_MODE_FULLSCREEN -> handleFullscreenTaskLaunch(task)
// Check if freeform task should be updated
task.windowingMode == WINDOWING_MODE_FREEFORM -> handleFreeformTaskLaunch(task)
- else -> null
+ else -> {
+ null
+ }
}
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: handleRequest result=%s",
+ result ?: "null"
+ )
+ return result
}
/**
@@ -552,6 +578,7 @@
}
private fun handleFreeformTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
+ KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFreeformTaskLaunch")
val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId)
if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) {
KtProtoLog.d(
@@ -568,6 +595,7 @@
}
private fun handleFullscreenTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
+ KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFullscreenTaskLaunch")
val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId)
if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) {
KtProtoLog.d(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index fb966c7..86b0f33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -1018,24 +1018,17 @@
private void resetPrevPip(@NonNull TransitionInfo.Change prevPipTaskChange,
@NonNull SurfaceControl.Transaction startTransaction) {
final SurfaceControl leash = prevPipTaskChange.getLeash();
- final Rect bounds = prevPipTaskChange.getEndAbsBounds();
- final Point offset = prevPipTaskChange.getEndRelOffset();
- bounds.offset(-offset.x, -offset.y);
+ startTransaction.remove(leash);
- startTransaction.setWindowCrop(leash, null);
- startTransaction.setMatrix(leash, 1, 0, 0, 1);
- startTransaction.setCornerRadius(leash, 0);
- startTransaction.setPosition(leash, bounds.left, bounds.top);
-
- if (mHasFadeOut && prevPipTaskChange.getTaskInfo().isVisible()) {
- if (mPipAnimationController.getCurrentAnimator() != null) {
- mPipAnimationController.getCurrentAnimator().cancel();
- }
- startTransaction.setAlpha(leash, 1);
- }
mHasFadeOut = false;
mCurrentPipTaskToken = null;
- mPipOrganizer.onExitPipFinished(prevPipTaskChange.getTaskInfo());
+
+ // clean-up the state in PipTaskOrganizer if the PipTaskOrganizer#onTaskAppeared() hasn't
+ // been called yet with its leash reference now pointing to a new SurfaceControl not
+ // matching the leash of the pip we are removing.
+ if (mPipOrganizer.getSurfaceControl() == leash) {
+ mPipOrganizer.onExitPipFinished(prevPipTaskChange.getTaskInfo());
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
index fc674a8..f9332e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
@@ -63,6 +63,15 @@
if (pipBoundsState.isImeShowing()) {
insets.bottom -= pipBoundsState.getImeHeight();
}
+ // if PiP is stashed we only adjust the vertical position if it's outside of insets and
+ // ignore all keep clear areas, since it's already on the side
+ if (pipBoundsState.isStashed()) {
+ if (startingBounds.bottom > insets.bottom || startingBounds.top < insets.top) {
+ // bring PiP back to be aligned by bottom inset
+ startingBounds.offset(0, insets.bottom - startingBounds.bottom);
+ }
+ return startingBounds;
+ }
Rect pipBounds = new Rect(startingBounds);
boolean shouldApplyGravity = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 2a61445..b0fa993 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -49,7 +49,7 @@
Consts.TAG_WM_SPLIT_SCREEN),
WM_SHELL_SYSUI_EVENTS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
- WM_SHELL_DESKTOP_MODE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ WM_SHELL_DESKTOP_MODE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
WM_SHELL_FLOATING_APPS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 9863099..d21f8a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -199,19 +199,7 @@
boolean isOpening = TransitionUtil.isOpeningType(info.getType());
if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) {
// fade out
- if (change.getSnapshot() != null) {
- // This case is happened if task is going to reparent to TDA, the origin leash
- // doesn't rendor so we use snapshot to replace it animating.
- t.reparent(change.getSnapshot(), info.getRoot(rootIdx).getLeash());
- // Use origin leash layer.
- t.setLayer(change.getSnapshot(), info.getChanges().size() - i);
- t.setPosition(change.getSnapshot(), change.getStartAbsBounds().left,
- change.getStartAbsBounds().top);
- t.show(change.getSnapshot());
- startFadeAnimation(change.getSnapshot(), false /* show */);
- } else {
- startFadeAnimation(leash, false /* show */);
- }
+ startFadeAnimation(leash, false /* show */);
} else if (mode == TRANSIT_CHANGE && change.getSnapshot() != null) {
t.reparent(change.getSnapshot(), info.getRoot(rootIdx).getLeash());
// Ensure snapshot it on the top of all transition surfaces
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 67cf835..b5d4a9d 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
@@ -1516,6 +1516,10 @@
// Legacy transition we need to create divider here, shell transition case we will
// create it on #finishEnterSplitScreen
mSplitLayout.init();
+ } else {
+ // We handle split visibility itself on shell transition, but sometimes we didn't
+ // reset it correctly after dismiss by some reason, so just set invisible before active.
+ setSplitsVisible(false);
}
if (taskInfo != null) {
setSideStagePosition(startPosition, wct);
@@ -1650,7 +1654,7 @@
mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLandscape());
}
- if (present && visible) {
+ if (present) {
updateRecentTasksSplitPair();
}
@@ -2332,7 +2336,6 @@
} else if (!isSplitScreenVisible() && isOpening) {
// If split is running in the background and the trigger task is appearing into
// split, prepare to enter split screen.
- setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, out);
prepareEnterSplitScreen(out);
mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering);
@@ -2358,7 +2361,6 @@
if (isOpening && getStageOfTask(triggerTask) != null) {
// One task is appearing into split, prepare to enter split screen.
out = new WindowContainerTransaction();
- setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, out);
prepareEnterSplitScreen(out);
mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering);
@@ -2515,6 +2517,7 @@
if (mMixedHandler.animatePendingSplitWithDisplayChange(transition, info,
startTransaction, finishTransaction, finishCallback)) {
mSplitLayout.update(startTransaction);
+ startTransaction.apply();
return true;
}
}
@@ -2653,11 +2656,11 @@
if (mSplitTransitions.mPendingEnter.mExtraTransitType
== TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) {
+ // Open to side should only be used when split already active and foregorund.
if (mainChild == null && sideChild == null) {
Log.w(TAG, "Launched a task in split, but didn't receive any task in transition.");
- mSplitTransitions.mPendingEnter.cancel((cancelWct, cancelT)
- -> prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, cancelWct));
- mSplitUnsupportedToast.show();
+ // This should happen when the target app is already on front, so just cancel.
+ mSplitTransitions.mPendingEnter.cancel(null);
return true;
}
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index e3ed5dd..3ef4f02 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -132,7 +132,8 @@
* Returns the top visible child task's id.
*/
int getTopVisibleChildTaskId() {
- final ActivityManager.RunningTaskInfo taskInfo = getChildTaskInfo(t -> t.isVisible);
+ final ActivityManager.RunningTaskInfo taskInfo = getChildTaskInfo(t -> t.isVisible
+ && t.isVisibleRequested);
return taskInfo != null ? taskInfo.taskId : INVALID_TASK_ID;
}
@@ -188,7 +189,8 @@
final int taskId = taskInfo.taskId;
mChildrenLeashes.put(taskId, leash);
mChildrenTaskInfo.put(taskId, taskInfo);
- mCallbacks.onChildTaskStatusChanged(taskId, true /* present */, taskInfo.isVisible);
+ mCallbacks.onChildTaskStatusChanged(taskId, true /* present */,
+ taskInfo.isVisible && taskInfo.isVisibleRequested);
if (ENABLE_SHELL_TRANSITIONS) {
// Status is managed/synchronized by the transition lifecycle.
return;
@@ -229,7 +231,7 @@
}
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */,
- taskInfo.isVisible);
+ taskInfo.isVisible && taskInfo.isVisibleRequested);
if (!ENABLE_SHELL_TRANSITIONS) {
updateChildTaskSurface(
taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java
index ae72220..4cfbbd9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java
@@ -20,6 +20,7 @@
import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -370,8 +371,11 @@
mStartingWindowRecordManager.addRecord(taskId, tView);
}
- private void removeWindowInner(View decorView, boolean hideView) {
+ private void removeWindowInner(@NonNull View decorView, boolean hideView) {
requestTopUi(false);
+ if (!decorView.isAttachedToWindow()) {
+ return;
+ }
if (hideView) {
decorView.setVisibility(View.GONE);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 4176fe8..bda25d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -24,6 +24,7 @@
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.util.TransitionUtil.isOpeningType;
@@ -172,7 +173,9 @@
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request) {
- if (mPipHandler.requestHasPipEnter(request) && mSplitHandler.isSplitActive()) {
+ if (mPipHandler.requestHasPipEnter(request) && mSplitHandler.isSplitActive()
+ && request.getTriggerTask() != null && mSplitHandler.getSplitItemPosition(
+ request.getTriggerTask().token) != SPLIT_POSITION_UNDEFINED) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a PiP-enter request while "
+ "Split-Screen is active, so treat it as Mixed.");
if (request.getRemoteTransition() != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 3bf278c..e52fd00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -260,6 +260,12 @@
// This is the only way to get display-id currently, so check display capabilities here.
final DisplayLayout displayLayout = displayController.getDisplayLayout(
topTaskInfo.displayId);
+ // This condition should be true when using gesture navigation or the screen size is large
+ // (>600dp) because the bar is small relative to screen.
+ if (displayLayout.allowSeamlessRotationDespiteNavBarMoving()) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " nav bar allows seamless.");
+ return ROTATION_ANIMATION_SEAMLESS;
+ }
// For the upside down rotation we don't rotate seamlessly as the navigation bar moves
// position. Note most apps (using orientation:sensor or user as opposed to fullSensor)
// will not enter the reverse portrait orientation, so actually the orientation won't
@@ -272,13 +278,9 @@
return animationHint;
}
- // If the navigation bar can't change sides, then it will jump when we change orientations
- // and we don't rotate seamlessly - unless that is allowed, e.g. with gesture navigation
- // where the navbar is low-profile enough that this isn't very noticeable.
- if (!displayLayout.allowSeamlessRotationDespiteNavBarMoving()
- && (!(displayLayout.navigationBarCanMove()
- && (displayChange.getStartAbsBounds().width()
- != displayChange.getStartAbsBounds().height())))) {
+ // If the navigation bar cannot change sides, then it will jump when changing orientation
+ // so do not use seamless rotation.
+ if (!displayLayout.navigationBarCanMove()) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
" nav bar changes sides, so not seamless.");
return animationHint;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index e42c662..d8a8877 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -813,6 +813,9 @@
track.mReadyTransitions.remove(0);
track.mActiveTransition = ready;
if (ready.mAborted) {
+ if (ready.mStartT != null) {
+ ready.mStartT.apply();
+ }
// finish now since there's nothing to animate. Calls back into processReadyQueue
onFinish(ready, null, null);
return;
@@ -940,10 +943,6 @@
/** Aborts a transition. This will still queue it up to maintain order. */
private void onAbort(ActiveTransition transition) {
final Track track = mTracks.get(transition.getTrack());
- // apply immediately since they may be "parallel" operations: We currently we use abort for
- // thing which are independent to other transitions (like starting-window transfer).
- transition.mStartT.apply();
- transition.mFinishT.apply();
transition.mAborted = true;
mTracer.logAborted(transition.mInfo.getDebugId());
@@ -1090,9 +1089,8 @@
if (wct == null) {
wct = new WindowContainerTransaction();
}
- mDisplayController.getChangeController().dispatchOnDisplayChange(wct,
- change.getDisplayId(), change.getStartRotation(),
- change.getEndRotation(), null /* newDisplayAreaInfo */);
+ mDisplayController.onDisplayRotateRequested(wct, change.getDisplayId(),
+ change.getStartRotation(), change.getEndRotation());
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
index c33a633..936faa3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
@@ -170,6 +170,9 @@
if (isOpeningType(mode)) {
t.setAlpha(leash, 0.f);
}
+ // Set the transition leash position to 0 in case the divider leash position being
+ // taking down.
+ t.setPosition(leash, 0, 0);
t.setLayer(leash, Integer.MAX_VALUE);
return;
}
@@ -228,7 +231,11 @@
t.reparent(change.getLeash(), leashSurface);
t.setAlpha(change.getLeash(), 1.0f);
t.show(change.getLeash());
- t.setPosition(change.getLeash(), 0, 0);
+ if (!isDividerBar(change)) {
+ // For divider, don't modify its inner leash position when creating the outer leash
+ // for the transition. In case the position being wrong after the transition finished.
+ t.setPosition(change.getLeash(), 0, 0);
+ }
t.setLayer(change.getLeash(), 0);
return leashSurface;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index cea0fcb..b217bd3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -143,6 +143,7 @@
mHandler,
mChoreographer,
mDisplay.getDisplayId(),
+ 0 /* taskCornerRadius */,
mDecorationContainerSurface,
mDragPositioningCallback);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 3ccb938..e3cb8af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -235,6 +235,7 @@
mHandler,
mChoreographer,
mDisplay.getDisplayId(),
+ mRelayoutParams.mCornerRadius,
mDecorationContainerSurface,
mDragPositioningCallback);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 05a7588..68b63e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -56,7 +56,7 @@
*/
class DragResizeInputListener implements AutoCloseable {
private static final String TAG = "DragResizeInputListener";
-
+ private static final float TOP_CORNER_PADDING = 1.5f;
private final IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession();
private final Handler mHandler;
private final Choreographer mChoreographer;
@@ -74,6 +74,7 @@
private int mTaskHeight;
private int mResizeHandleThickness;
private int mCornerSize;
+ private int mTaskCornerRadius;
private Rect mLeftTopCornerBounds;
private Rect mRightTopCornerBounds;
@@ -88,12 +89,14 @@
Handler handler,
Choreographer choreographer,
int displayId,
+ int taskCornerRadius,
SurfaceControl decorationSurface,
DragPositioningCallback callback) {
mInputManager = context.getSystemService(InputManager.class);
mHandler = handler;
mChoreographer = choreographer;
mDisplayId = displayId;
+ mTaskCornerRadius = taskCornerRadius;
mDecorationSurface = decorationSurface;
// Use a fake window as the backing surface is a container layer and we don't want to create
// a buffer layer for it so we can't use ViewRootImpl.
@@ -384,19 +387,67 @@
@DragPositioningCallback.CtrlType
private int calculateResizeHandlesCtrlType(float x, float y) {
int ctrlType = 0;
- if (x < 0) {
+ // mTaskCornerRadius is only used in comparing with corner regions. Comparisons with
+ // sides will use the bounds specified in setGeometry and not go into task bounds.
+ if (x < mTaskCornerRadius) {
ctrlType |= CTRL_TYPE_LEFT;
}
- if (x > mTaskWidth) {
+ if (x > mTaskWidth - mTaskCornerRadius) {
ctrlType |= CTRL_TYPE_RIGHT;
}
- if (y < 0) {
+ if (y < mTaskCornerRadius) {
ctrlType |= CTRL_TYPE_TOP;
}
- if (y > mTaskHeight) {
+ if (y > mTaskHeight - mTaskCornerRadius) {
ctrlType |= CTRL_TYPE_BOTTOM;
}
- return ctrlType;
+ return checkDistanceFromCenter(ctrlType, x, y);
+ }
+
+ // If corner input is not within appropriate distance of corner radius, do not use it.
+ // If input is not on a corner or is within valid distance, return ctrlType.
+ @DragPositioningCallback.CtrlType
+ private int checkDistanceFromCenter(@DragPositioningCallback.CtrlType int ctrlType,
+ float x, float y) {
+ int centerX;
+ int centerY;
+
+ // Determine center of rounded corner circle; this is simply the corner if radius is 0.
+ switch (ctrlType) {
+ case CTRL_TYPE_LEFT | CTRL_TYPE_TOP: {
+ centerX = mTaskCornerRadius;
+ centerY = mTaskCornerRadius;
+ break;
+ }
+ case CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM: {
+ centerX = mTaskCornerRadius;
+ centerY = mTaskHeight - mTaskCornerRadius;
+ break;
+ }
+ case CTRL_TYPE_RIGHT | CTRL_TYPE_TOP: {
+ centerX = mTaskWidth - mTaskCornerRadius;
+ centerY = mTaskCornerRadius;
+ break;
+ }
+ case CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM: {
+ centerX = mTaskWidth - mTaskCornerRadius;
+ centerY = mTaskHeight - mTaskCornerRadius;
+ break;
+ }
+ default: {
+ return ctrlType;
+ }
+ }
+ double distanceFromCenter = Math.hypot(x - centerX, y - centerY);
+
+ // TODO(b/286461778): Remove this when input in top corner gap no longer goes to header
+ float cornerPadding = (ctrlType & CTRL_TYPE_TOP) != 0 ? TOP_CORNER_PADDING : 1;
+
+ if (distanceFromCenter < mTaskCornerRadius + mResizeHandleThickness * cornerPadding
+ && distanceFromCenter >= mTaskCornerRadius) {
+ return ctrlType;
+ }
+ return 0;
}
@DragPositioningCallback.CtrlType
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java
index 4d7e9e4..cc9e26b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java
@@ -18,6 +18,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -26,10 +29,13 @@
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipBoundsState;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import java.util.Set;
@@ -42,6 +48,10 @@
public class PhonePipKeepClearAlgorithmTest extends ShellTestCase {
private PhonePipKeepClearAlgorithm mPipKeepClearAlgorithm;
+
+ @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
+ @Mock private PipBoundsState mMockPipBoundsState;
+
private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 1000, 1000);
@Before
@@ -73,7 +83,6 @@
@Test
public void findUnoccludedPosition_withCollidingUnrestrictedKeepClearArea_moveBounds() {
- // TODO(b/183746978): update this test to accommodate for the updated algorithm
final Rect inBounds = new Rect(0, 0, 100, 100);
final Rect keepClearRect = new Rect(50, 50, 150, 150);
@@ -93,4 +102,202 @@
assertEquals(inBounds, outBounds);
}
+
+ @Test
+ public void adjust_withCollidingRestrictedKeepClearArea_moveBounds() {
+ final Rect pipBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(50, 50, 150, 150);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ when(mMockPipBoundsState.getRestrictedKeepClearAreas()).thenReturn(Set.of(keepClearRect));
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertFalse(outBounds.contains(keepClearRect));
+ }
+
+ @Test
+ public void adjust_withNonCollidingRestrictedKeepClearArea_boundsUnchanged() {
+ final Rect pipBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(100, 100, 150, 150);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ when(mMockPipBoundsState.getRestrictedKeepClearAreas()).thenReturn(Set.of(keepClearRect));
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertFalse(outBounds.contains(keepClearRect));
+ }
+
+ @Test
+ public void adjust_withCollidingRestrictedKeepClearArea_whileStashed_boundsUnchanged() {
+ final Rect pipBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(50, 50, 150, 150);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ when(mMockPipBoundsState.isStashed()).thenReturn(true);
+ when(mMockPipBoundsState.getRestrictedKeepClearAreas()).thenReturn(Set.of(keepClearRect));
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(pipBounds, outBounds);
+ }
+
+ @Test
+ public void adjust_withNonCollidingRestrictedKeepClearArea_whileStashed_boundsUnchanged() {
+ final Rect pipBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(100, 100, 150, 150);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ when(mMockPipBoundsState.isStashed()).thenReturn(true);
+ when(mMockPipBoundsState.getRestrictedKeepClearAreas()).thenReturn(Set.of(keepClearRect));
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(pipBounds, outBounds);
+ }
+
+ @Test
+ public void adjust_aboveDisplayBounds_onLeftEdge_appliesBottomLeftGravity() {
+ final Rect pipBounds = new Rect(
+ 0, DISPLAY_BOUNDS.top - 50, 100, DISPLAY_BOUNDS.top + 50);
+ final Rect expected = new Rect(
+ 0, DISPLAY_BOUNDS.bottom - 100, 100, DISPLAY_BOUNDS.bottom);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+ when(mMockPipBoundsAlgorithm.getSnapFraction(any(Rect.class))).thenReturn(0f);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(expected, outBounds);
+ }
+
+ @Test
+ public void adjust_belowDisplayBounds_onLeftEdge_appliesBottomLeftGravity() {
+ final Rect pipBounds = new Rect(
+ 0, DISPLAY_BOUNDS.bottom - 50, 100, DISPLAY_BOUNDS.bottom + 50);
+ final Rect expected = new Rect(
+ 0, DISPLAY_BOUNDS.bottom - 100, 100, DISPLAY_BOUNDS.bottom);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+ when(mMockPipBoundsAlgorithm.getSnapFraction(any(Rect.class))).thenReturn(3f);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(expected, outBounds);
+ }
+
+ @Test
+ public void adjust_aboveDisplayBounds_onRightEdge_appliesBottomRightGravity() {
+ final Rect pipBounds = new Rect(
+ DISPLAY_BOUNDS.right - 100, DISPLAY_BOUNDS.top - 50,
+ DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.top + 50);
+ final Rect expected = new Rect(
+ DISPLAY_BOUNDS.right - 100, DISPLAY_BOUNDS.bottom - 100,
+ DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+ when(mMockPipBoundsAlgorithm.getSnapFraction(any(Rect.class))).thenReturn(1f);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(expected, outBounds);
+ }
+
+ @Test
+ public void adjust_belowDisplayBounds_onRightEdge_appliesBottomRightGravity() {
+ final Rect pipBounds = new Rect(
+ DISPLAY_BOUNDS.right - 100, DISPLAY_BOUNDS.bottom - 50,
+ DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom + 50);
+ final Rect expected = new Rect(
+ DISPLAY_BOUNDS.right - 100, DISPLAY_BOUNDS.bottom - 100,
+ DISPLAY_BOUNDS.right, DISPLAY_BOUNDS.bottom);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+ when(mMockPipBoundsAlgorithm.getSnapFraction(any(Rect.class))).thenReturn(2f);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(expected, outBounds);
+ }
+
+ @Test
+ public void adjust_whileStashed_aboveDisplayBounds_alignsToBottomInset() {
+ final Rect pipBounds = new Rect(
+ 0, DISPLAY_BOUNDS.top - 50, 100, DISPLAY_BOUNDS.top + 50);
+ final Rect expected = new Rect(
+ 0, DISPLAY_BOUNDS.bottom - 100, 100, DISPLAY_BOUNDS.bottom);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ when(mMockPipBoundsState.isStashed()).thenReturn(true);
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(expected, outBounds);
+ }
+
+ @Test
+ public void adjust_whileStashed_belowDisplayBounds_alignsToBottomInset() {
+ final Rect pipBounds = new Rect(
+ 0, DISPLAY_BOUNDS.bottom - 50, 100, DISPLAY_BOUNDS.bottom + 50);
+ final Rect expected = new Rect(
+ 0, DISPLAY_BOUNDS.bottom - 100, 100, DISPLAY_BOUNDS.bottom);
+ when(mMockPipBoundsState.getBounds()).thenReturn(pipBounds);
+ when(mMockPipBoundsState.isStashed()).thenReturn(true);
+ doAnswer(invocation -> {
+ Rect arg0 = invocation.getArgument(0);
+ arg0.set(DISPLAY_BOUNDS);
+ return null;
+ }).when(mMockPipBoundsAlgorithm).getInsetBounds(any(Rect.class));
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(
+ mMockPipBoundsState, mMockPipBoundsAlgorithm);
+
+ assertEquals(expected, outBounds);
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 961e3e9..ff380e9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -702,8 +702,8 @@
createTaskInfo(1, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
final DisplayController displays = createTestDisplayController();
- final @Surface.Rotation int upsideDown = displays
- .getDisplayLayout(DEFAULT_DISPLAY).getUpsideDownRotation();
+ final DisplayLayout displayLayout = displays.getDisplayLayout(DEFAULT_DISPLAY);
+ final @Surface.Rotation int upsideDown = displayLayout.getUpsideDownRotation();
TransitionInfo.Change displayChange = new ChangeBuilder(TRANSIT_CHANGE)
.setFlags(FLAG_IS_DISPLAY).setRotate().build();
@@ -743,7 +743,8 @@
assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint(
displayChange, noTask, displays));
- // Not seamless if one of rotations is upside-down
+ // Not seamless if the nav bar cares rotation and one of rotations is upside-down.
+ doReturn(false).when(displayLayout).allowSeamlessRotationDespiteNavBarMoving();
displayChange = new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY)
.setRotate(upsideDown, ROTATION_ANIMATION_UNSPECIFIED).build();
final TransitionInfo seamlessUpsideDown = new TransitionInfoBuilder(TRANSIT_CHANGE)
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 16b35ff..a5518eb 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -405,8 +405,17 @@
// If the previous frame was dropped we don't need to hold onto it, so
// just keep using the previous frame's structure instead
- if (!wasSkipped(mCurrentFrameInfo)) {
+ if (wasSkipped(mCurrentFrameInfo)) {
+ // Use the oldest skipped frame in case we skip more than a single frame
+ if (!mSkippedFrameInfo) {
+ mSkippedFrameInfo.emplace();
+ mSkippedFrameInfo->vsyncId =
+ mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
+ mSkippedFrameInfo->startTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+ }
+ } else {
mCurrentFrameInfo = mJankTracker.startFrame();
+ mSkippedFrameInfo.reset();
}
mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
@@ -602,10 +611,18 @@
if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
const auto inputEventId =
static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId));
- native_window_set_frame_timeline_info(
- mNativeSurface->getNativeWindow(), frameCompleteNr, vsyncId, inputEventId,
- mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime),
- solelyTextureViewUpdates);
+ const ANativeWindowFrameTimelineInfo ftl = {
+ .frameNumber = frameCompleteNr,
+ .frameTimelineVsyncId = vsyncId,
+ .inputEventId = inputEventId,
+ .startTimeNanos = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime),
+ .useForRefreshRateSelection = solelyTextureViewUpdates,
+ .skippedFrameVsyncId = mSkippedFrameInfo ? mSkippedFrameInfo->vsyncId
+ : UiFrameInfoBuilder::INVALID_VSYNC_ID,
+ .skippedFrameStartTimeNanos =
+ mSkippedFrameInfo ? mSkippedFrameInfo->startTime : 0,
+ };
+ native_window_set_frame_timeline_info(mNativeSurface->getNativeWindow(), ftl);
}
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 5219b57..32ac5af 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -366,6 +366,12 @@
ColorMode mColorMode = ColorMode::Default;
float mTargetSdrHdrRatio = 1.f;
+
+ struct SkippedFrameInfo {
+ int64_t vsyncId;
+ int64_t startTime;
+ };
+ std::optional<SkippedFrameInfo> mSkippedFrameInfo;
};
} /* namespace renderthread */
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 38bb447..b7c97208 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -4933,8 +4933,12 @@
* Called when an output frame has rendered on the output surface.
* <p>
* <strong>Note:</strong> This callback is for informational purposes only: to get precise
- * render timing samples, and can be significantly delayed and batched. Some frames may have
- * been rendered even if there was no callback generated.
+ * render timing samples, and can be significantly delayed and batched. Starting with
+ * Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, a callback will always
+ * be received for each rendered frame providing the MediaCodec is still in the executing
+ * state when the callback is dispatched. Prior to Android
+ * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, some frames may have been
+ * rendered even if there was no callback generated.
*
* @param codec the MediaCodec instance
* @param presentationTimeUs the presentation time (media time) of the frame rendered.
diff --git a/packages/SettingsLib/res/layout/dialog_with_icon.xml b/packages/SettingsLib/res/layout/dialog_with_icon.xml
index 54f8096..55d12eb 100644
--- a/packages/SettingsLib/res/layout/dialog_with_icon.xml
+++ b/packages/SettingsLib/res/layout/dialog_with_icon.xml
@@ -41,7 +41,6 @@
android:id="@+id/dialog_with_icon_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="10dp"
android:gravity="center"
style="@style/TextAppearanceSmall"/>
diff --git a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
index 4ffaf1b..2ded3c6 100644
--- a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
+++ b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
@@ -14,62 +14,48 @@
limitations under the License.
-->
-<ScrollView
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/user_info_editor"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:id="@+id/user_info_scroll"
- android:padding="16dp">
-
- <LinearLayout
- android:layout_width="match_parent"
+ android:baselineAligned="false"
+ android:orientation="vertical">
+ <FrameLayout
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:baselineAligned="false"
- android:orientation="vertical">
- <TextView
- android:id="@+id/user_info_title"
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/EditUserDialogTitle"
- android:text="@string/user_info_settings_title"
- android:textDirection="locale"/>
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center">
- <ImageView
- android:id="@+id/user_photo"
- android:layout_width="@dimen/user_photo_size_in_user_info_dialog"
- android:layout_height="@dimen/user_photo_size_in_user_info_dialog"
- android:contentDescription="@string/user_image_photo_selector"
- android:scaleType="fitCenter"/>
- <ImageView
- android:id="@+id/add_a_photo_icon"
- android:layout_width="@dimen/add_a_photo_icon_size_in_user_info_dialog"
- android:layout_height="@dimen/add_a_photo_icon_size_in_user_info_dialog"
- android:src="@drawable/add_a_photo_circled"
- android:layout_gravity="bottom|right"/>
- </FrameLayout>
+ android:layout_gravity="center">
+ <ImageView
+ android:id="@+id/user_photo"
+ android:layout_width="@dimen/user_photo_size_in_user_info_dialog"
+ android:layout_height="@dimen/user_photo_size_in_user_info_dialog"
+ android:contentDescription="@string/user_image_photo_selector"
+ android:scaleType="fitCenter"/>
+ <ImageView
+ android:id="@+id/add_a_photo_icon"
+ android:layout_width="@dimen/add_a_photo_icon_size_in_user_info_dialog"
+ android:layout_height="@dimen/add_a_photo_icon_size_in_user_info_dialog"
+ android:src="@drawable/add_a_photo_circled"
+ android:layout_gravity="bottom|right"/>
+ </FrameLayout>
- <EditText
- android:id="@+id/user_name"
- android:layout_width="match_parent"
- android:layout_height="@dimen/user_name_height_in_user_info_dialog"
- android:layout_gravity="center"
- android:minWidth="200dp"
- android:layout_marginStart="6dp"
- android:minHeight="@dimen/min_tap_target_size"
- android:ellipsize="end"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textAlignment="viewStart"
- android:inputType="text|textCapWords"
- android:selectAllOnFocus="true"
- android:hint="@string/user_nickname"
- android:maxLength="100"/>
+ <EditText
+ android:id="@+id/user_name"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/user_name_height_in_user_info_dialog"
+ android:layout_gravity="center"
+ android:minWidth="200dp"
+ android:layout_marginStart="6dp"
+ android:minHeight="@dimen/min_tap_target_size"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textAlignment="viewStart"
+ android:inputType="text|textCapWords"
+ android:selectAllOnFocus="true"
+ android:hint="@string/user_nickname"
+ android:maxLength="100"/>
- </LinearLayout>
+</LinearLayout>
-</ScrollView>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index cc2cf48..60bc226 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -827,9 +827,9 @@
<!-- UI debug setting: show touches location summary [CHAR LIMIT=50] -->
<string name="show_touches_summary">Show visual feedback for taps</string>
- <!-- UI debug setting: show key presses? [CHAR LIMIT=25] -->
+ <!-- UI debug setting: show key presses? [CHAR LIMIT=50] -->
<string name="show_key_presses">Show key presses</string>
- <!-- UI debug setting: show physical key presses summary [CHAR LIMIT=50] -->
+ <!-- UI debug setting: show physical key presses summary [CHAR LIMIT=150] -->
<string name="show_key_presses_summary">Show visual feedback for physical key presses</string>
<!-- UI debug setting: show where surface updates happen? [CHAR LIMIT=25] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
index e61c8f5..997d1f4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
@@ -59,6 +59,7 @@
private static final String KEY_IS_ADMIN = "admin_status";
private static final String KEY_ADD_USER_LONG_MESSAGE_DISPLAYED =
"key_add_user_long_message_displayed";
+ public static final int MESSAGE_PADDING = 10;
@Retention(RetentionPolicy.SOURCE)
@IntDef({EXIT_DIALOG, INITIAL_DIALOG, GRANT_ADMIN_DIALOG,
@@ -191,6 +192,7 @@
cancelCallback.run();
clear();
});
+ mCustomDialogHelper.setMessagePadding(MESSAGE_PADDING);
mUserCreationDialog.setCanceledOnTouchOutside(true);
return mUserCreationDialog;
}
@@ -212,7 +214,6 @@
}
updateLayout();
});
- return;
}
private void updateLayout() {
@@ -234,7 +235,6 @@
}
Drawable icon = mActivity.getDrawable(R.drawable.ic_person_add);
mCustomDialogHelper.setVisibility(mCustomDialogHelper.ICON, true)
- .setVisibility(mCustomDialogHelper.TITLE, true)
.setVisibility(mCustomDialogHelper.MESSAGE, true)
.setIcon(icon)
.setButtonEnabled(true)
@@ -248,7 +248,6 @@
mGrantAdminView.setVisibility(View.VISIBLE);
mCustomDialogHelper
.setVisibility(mCustomDialogHelper.ICON, true)
- .setVisibility(mCustomDialogHelper.TITLE, true)
.setVisibility(mCustomDialogHelper.MESSAGE, true)
.setIcon(mActivity.getDrawable(R.drawable.ic_admin_panel_settings))
.setTitle(R.string.user_grant_admin_title)
@@ -262,8 +261,8 @@
case EDIT_NAME_DIALOG:
mCustomDialogHelper
.setVisibility(mCustomDialogHelper.ICON, false)
- .setVisibility(mCustomDialogHelper.TITLE, false)
.setVisibility(mCustomDialogHelper.MESSAGE, false)
+ .setTitle(R.string.user_info_settings_title)
.setNegativeButtonText(R.string.back)
.setPositiveButtonText(R.string.done);
mEditUserInfoView.setVisibility(View.VISIBLE);
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
index e55d7ea..cd5f597 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
@@ -17,7 +17,6 @@
package com.android.settingslib.users;
import android.app.Activity;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
@@ -31,7 +30,6 @@
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.ImageView;
-import android.widget.ScrollView;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -41,6 +39,7 @@
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.settingslib.utils.CustomDialogHelper;
import java.io.File;
import java.util.function.BiConsumer;
@@ -128,7 +127,7 @@
* codes to take photo/choose photo/crop photo.
*/
public Dialog createDialog(Activity activity, ActivityStarter activityStarter,
- @Nullable Drawable oldUserIcon, String defaultUserName, String title,
+ @Nullable Drawable oldUserIcon, String defaultUserName,
BiConsumer<String, Drawable> successCallback, Runnable cancelCallback) {
LayoutInflater inflater = LayoutInflater.from(activity);
View content = inflater.inflate(R.layout.edit_user_info_dialog_content, null);
@@ -160,10 +159,8 @@
userPhotoView);
}
}
- ScrollView scrollView = content.findViewById(R.id.user_info_scroll);
- scrollView.setClipToOutline(true);
mEditUserInfoDialog = buildDialog(activity, content, userNameView, oldUserIcon,
- defaultUserName, title, successCallback, cancelCallback);
+ defaultUserName, successCallback, cancelCallback);
// Make sure the IME is up.
mEditUserInfoDialog.getWindow()
@@ -181,12 +178,13 @@
}
private Dialog buildDialog(Activity activity, View content, EditText userNameView,
- @Nullable Drawable oldUserIcon, String defaultUserName, String title,
+ @Nullable Drawable oldUserIcon, String defaultUserName,
BiConsumer<String, Drawable> successCallback, Runnable cancelCallback) {
- return new AlertDialog.Builder(activity)
- .setView(content)
- .setCancelable(true)
- .setPositiveButton(android.R.string.ok, (dialog, which) -> {
+ CustomDialogHelper dialogHelper = new CustomDialogHelper(activity);
+ dialogHelper
+ .setTitle(R.string.user_info_settings_title)
+ .addCustomView(content)
+ .setPositiveButton(android.R.string.ok, view -> {
Drawable newUserIcon = mEditUserPhotoController != null
? mEditUserPhotoController.getNewUserPhotoDrawable()
: null;
@@ -201,20 +199,23 @@
if (successCallback != null) {
successCallback.accept(userName, userIcon);
}
+ dialogHelper.getDialog().dismiss();
})
- .setNegativeButton(android.R.string.cancel, (dialog, which) -> {
+ .setBackButton(android.R.string.cancel, view -> {
clear();
if (cancelCallback != null) {
cancelCallback.run();
}
- })
- .setOnCancelListener(dialog -> {
- clear();
- if (cancelCallback != null) {
- cancelCallback.run();
- }
- })
- .create();
+ dialogHelper.getDialog().dismiss();
+ });
+ dialogHelper.getDialog().setOnCancelListener(dialog -> {
+ clear();
+ if (cancelCallback != null) {
+ cancelCallback.run();
+ }
+ dialogHelper.getDialog().dismiss();
+ });
+ return dialogHelper.getDialog();
}
@VisibleForTesting
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java b/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java
index de48814..5201b3d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java
@@ -193,6 +193,14 @@
}
/**
+ * Sets message padding of the dialog.
+ */
+ public CustomDialogHelper setMessagePadding(int dp) {
+ mDialogMessage.setPadding(dp, dp, dp, dp);
+ return this;
+ }
+
+ /**
* Sets icon of the dialog.
*/
public CustomDialogHelper setIcon(Drawable icon) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java
index e989ed2..b538077 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java
@@ -111,13 +111,13 @@
mActivityStarter, true, null,
cancelCallback);
dialog.show();
- assertThat(dialog.findViewById(R.id.user_info_scroll).getVisibility()).isEqualTo(View.GONE);
+ assertThat(dialog.findViewById(R.id.user_info_editor).getVisibility()).isEqualTo(View.GONE);
Button next = dialog.findViewById(R.id.button_ok);
next.performClick();
((RadioButton) dialog.findViewById(R.id.grant_admin_yes)).setChecked(true);
- assertThat(dialog.findViewById(R.id.user_info_scroll).getVisibility()).isEqualTo(View.GONE);
+ assertThat(dialog.findViewById(R.id.user_info_editor).getVisibility()).isEqualTo(View.GONE);
next.performClick();
- assertThat(dialog.findViewById(R.id.user_info_scroll).getVisibility())
+ assertThat(dialog.findViewById(R.id.user_info_editor).getVisibility())
.isEqualTo(View.VISIBLE);
dialog.dismiss();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
index f760032..f595cd3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java
@@ -27,7 +27,6 @@
import android.app.Activity;
import android.app.AlertDialog;
-import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
@@ -108,7 +107,7 @@
@Test
public void photoControllerOnActivityResult_whenWaiting_isCalled() {
mController.createDialog(mActivity, mActivityStarter, mCurrentIcon, "test user",
- "title", null, null);
+ null, null);
mController.startingActivityForResult();
Intent resultData = new Intent();
mController.onActivityResult(0, 0, resultData);
@@ -126,9 +125,7 @@
() -> String.valueOf('A')).limit(200).collect(Collectors.joining());
final AlertDialog dialog = (AlertDialog) mController.createDialog(mActivity,
- mActivityStarter, mCurrentIcon,
- "test user", "title", null,
- null);
+ mActivityStarter, mCurrentIcon, "test user", null, null);
dialog.show();
final EditText userNameEditText = dialog.findViewById(R.id.user_name);
userNameEditText.setText(longName);
@@ -143,7 +140,7 @@
AlertDialog dialog = (AlertDialog) mController.createDialog(
mActivity, mActivityStarter, mCurrentIcon, "test",
- "title", successCallback, cancelCallback);
+ successCallback, cancelCallback);
dialog.show();
dialog.cancel();
@@ -159,9 +156,9 @@
AlertDialog dialog = (AlertDialog) mController.createDialog(
mActivity, mActivityStarter, mCurrentIcon, "test",
- "title", successCallback, cancelCallback);
+ successCallback, cancelCallback);
dialog.show();
- dialog.getButton(Dialog.BUTTON_NEGATIVE).performClick();
+ dialog.findViewById(R.id.button_back).performClick();
verifyNoInteractions(successCallback);
verify(cancelCallback, times(1))
@@ -176,11 +173,11 @@
Drawable oldUserIcon = mCurrentIcon;
AlertDialog dialog = (AlertDialog) mController.createDialog(
mActivity, mActivityStarter, oldUserIcon, "test",
- "title", successCallback, cancelCallback);
+ successCallback, cancelCallback);
// No change to the photo.
when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(null);
dialog.show();
- dialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
+ dialog.findViewById(R.id.button_ok).performClick();
verify(successCallback, times(1))
.accept("test", oldUserIcon);
@@ -194,11 +191,11 @@
AlertDialog dialog = (AlertDialog) mController.createDialog(
mActivity, mActivityStarter, null, "test",
- "title", successCallback, cancelCallback);
+ successCallback, cancelCallback);
// No change to the photo.
when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(null);
dialog.show();
- dialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
+ dialog.findViewById(R.id.button_ok).performClick();
verify(successCallback, times(1))
.accept("test", null);
@@ -212,14 +209,14 @@
AlertDialog dialog = (AlertDialog) mController.createDialog(
mActivity, mActivityStarter, mCurrentIcon, "test",
- "title", successCallback, cancelCallback);
+ successCallback, cancelCallback);
// No change to the photo.
when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(null);
dialog.show();
String expectedNewName = "new test user";
EditText editText = (EditText) dialog.findViewById(R.id.user_name);
editText.setText(expectedNewName);
- dialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
+ dialog.findViewById(R.id.button_ok).performClick();
verify(successCallback, times(1))
.accept(expectedNewName, mCurrentIcon);
@@ -233,12 +230,12 @@
AlertDialog dialog = (AlertDialog) mController.createDialog(
mActivity, mActivityStarter, mCurrentIcon, "test",
- "title", successCallback, cancelCallback);
+ successCallback, cancelCallback);
// A different drawable.
Drawable newPhoto = mock(Drawable.class);
when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(newPhoto);
dialog.show();
- dialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
+ dialog.findViewById(R.id.button_ok).performClick();
verify(successCallback, times(1))
.accept("test", newPhoto);
@@ -252,12 +249,12 @@
AlertDialog dialog = (AlertDialog) mController.createDialog(
mActivity, mActivityStarter, null, "test",
- "title", successCallback, cancelCallback);
+ successCallback, cancelCallback);
// A different drawable.
Drawable newPhoto = mock(Drawable.class);
when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(newPhoto);
dialog.show();
- dialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
+ dialog.findViewById(R.id.button_ok).performClick();
verify(successCallback, times(1))
.accept("test", newPhoto);
@@ -269,7 +266,7 @@
mPhotoRestrictedByBase = true;
mController.createDialog(mActivity, mActivityStarter, mCurrentIcon,
- "test", "title", null, null);
+ "test", null, null);
assertThat(mController.mPhotoController).isNull();
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 43f98c3..56e0643 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -839,6 +839,8 @@
<uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR" />
<!-- Permission required for CTS test IntentRedirectionTest -->
<uses-permission android:name="android.permission.QUERY_CLONED_APPS" />
+ <!-- Permission required for accessing all content provider mime types -->
+ <uses-permission android:name="android.permission.GET_ANY_PROVIDER_TYPE" />
<application
android:label="@string/app_label"
diff --git a/packages/Shell/src/com/android/shell/Screenshooter.java b/packages/Shell/src/com/android/shell/Screenshooter.java
index baaddf5..8a5b7da 100644
--- a/packages/Shell/src/com/android/shell/Screenshooter.java
+++ b/packages/Shell/src/com/android/shell/Screenshooter.java
@@ -21,12 +21,10 @@
import android.graphics.Bitmap;
import android.os.RemoteException;
import android.util.Log;
-import android.util.Pair;
import android.view.WindowManagerGlobal;
import android.window.ScreenCapture;
-import android.window.ScreenCapture.ScreenCaptureListener;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
-import android.window.ScreenCapture.ScreenshotSync;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
/**
* Helper class used to take screenshots.
@@ -46,15 +44,15 @@
static Bitmap takeScreenshot() {
Log.d(TAG, "Taking fullscreen screenshot");
// Take the screenshot
- final Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture =
+ final SynchronousScreenCaptureListener syncScreenCapture =
ScreenCapture.createSyncCaptureListener();
try {
WindowManagerGlobal.getWindowManagerService().captureDisplay(DEFAULT_DISPLAY, null,
- syncScreenCapture.first);
+ syncScreenCapture);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
- final ScreenshotHardwareBuffer screenshotBuffer = syncScreenCapture.second.get();
+ final ScreenshotHardwareBuffer screenshotBuffer = syncScreenCapture.getBuffer();
final Bitmap screenShot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
if (screenShot == null) {
Log.e(TAG, "Failed to take fullscreen screenshot");
diff --git a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
index a595566..7105721 100644
--- a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
@@ -110,7 +110,9 @@
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:ellipsize="end"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:ellipsize="marquee"
+ android:singleLine="true"
android:maxLines="1"
android:textColor="@color/media_dialog_item_main_content"
android:textSize="14sp"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 0332c9f..363dd01 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -208,7 +208,7 @@
@Override
public void run() {
final View host = mHost.get();
- if (host != null) {
+ if (host != null && host.isVisibleToUser()) {
host.announceForAccessibility(mTextToAnnounce);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index c1344e0..7c511a3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -275,13 +275,6 @@
*/
@Override
public void finish(boolean fromPrimaryAuth, int targetUserId) {
- if (!mKeyguardStateController.canDismissLockScreen() && !fromPrimaryAuth) {
- Log.e(TAG,
- "Tried to dismiss keyguard when lockscreen is not dismissible and user "
- + "was not authenticated with a primary security method "
- + "(pin/password/pattern).");
- return;
- }
// If there's a pending runnable because the user interacted with a widget
// and we're leaving keyguard, then run it.
boolean deferKeyguardDone = false;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 7cedecc..239a0cc 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -326,7 +326,7 @@
}
if (!Objects.equals(prevContentDescription, mView.getContentDescription())
- && mView.getContentDescription() != null) {
+ && mView.getContentDescription() != null && mView.isVisibleToUser()) {
mView.announceForAccessibility(mView.getContentDescription());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 8af92ce..3ca74ac 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -34,6 +34,7 @@
import android.content.res.Resources;
import android.graphics.RectF;
import android.os.Handler;
+import android.os.Trace;
import android.util.ArrayMap;
import android.util.Log;
import android.view.MotionEvent;
@@ -80,9 +81,6 @@
protected final Handler mHandler;
- private float mMinSwipeProgress = 0f;
- private float mMaxSwipeProgress = 1f;
-
private final SpringConfig mSnapBackSpringConfig =
new SpringConfig(SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
@@ -226,18 +224,11 @@
return v.getMeasuredWidth();
}
- public void setMinSwipeProgress(float minSwipeProgress) {
- mMinSwipeProgress = minSwipeProgress;
- }
-
- public void setMaxSwipeProgress(float maxSwipeProgress) {
- mMaxSwipeProgress = maxSwipeProgress;
- }
-
private float getSwipeProgressForOffset(View view, float translation) {
+ if (translation == 0) return 0;
float viewSize = getSize(view);
float result = Math.abs(translation / viewSize);
- return Math.min(Math.max(mMinSwipeProgress, result), mMaxSwipeProgress);
+ return Math.min(Math.max(0, result), 1);
}
/**
@@ -277,9 +268,11 @@
// invalidate the view's own bounds all the way up the view hierarchy
public static void invalidateGlobalRegion(View view) {
+ Trace.beginSection("SwipeHelper.invalidateGlobalRegion");
invalidateGlobalRegion(
view,
new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
+ Trace.endSection();
}
// invalidate a rectangle relative to the view's coordinate system all the way up the view
@@ -492,7 +485,7 @@
}
if (!mCancelled || wasRemoved) {
mCallback.onChildDismissed(animView);
- resetSwipeOfView(animView);
+ resetViewIfSwiping(animView);
}
if (endAction != null) {
endAction.accept(mCancelled);
@@ -547,7 +540,7 @@
if (!cancelled) {
updateSwipeProgressFromOffset(animView, canBeDismissed);
- resetSwipeOfView(animView);
+ resetViewIfSwiping(animView);
// Clear the snapped view after success, assuming it's not being swiped now
if (animView == mTouchedView && !mIsSwiping) {
mTouchedView = null;
@@ -811,7 +804,7 @@
return mIsSwiping ? mTouchedView : null;
}
- protected void resetSwipeOfView(View view) {
+ protected void resetViewIfSwiping(View view) {
if (getSwipedView() == view) {
resetSwipeState();
}
@@ -825,6 +818,12 @@
resetSwipeStates(/* resetAll= */ true);
}
+ public void forceResetSwipeState(@NonNull View view) {
+ if (view.getTranslationX() == 0) return;
+ setTranslation(view, 0);
+ updateSwipeProgressFromOffset(view, /* dismissable= */ true, 0);
+ }
+
/** This method resets the swipe state, and if `resetAll` is true, also resets the snap state */
private void resetSwipeStates(boolean resetAll) {
final View touchedView = mTouchedView;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index b086912..31b0f056 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -18,7 +18,6 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
-import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -405,16 +404,23 @@
}
private int getMagnificationMode() {
+ // If current capability is window mode, we would like the default value of the mode to
+ // be WINDOW, otherwise, the default value would be FULLSCREEN.
+ int defaultValue =
+ (getMagnificationCapability() == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)
+ ? ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
+ : ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+
return mSecureSettings.getIntForUser(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
- ACCESSIBILITY_MAGNIFICATION_MODE_NONE,
+ defaultValue,
UserHandle.USER_CURRENT);
}
private int getMagnificationCapability() {
return mSecureSettings.getIntForUser(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
- ACCESSIBILITY_MAGNIFICATION_MODE_NONE,
+ ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
UserHandle.USER_CURRENT);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 7c90735..c486603 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -48,6 +48,7 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
@@ -86,7 +87,7 @@
}
val keyguardAuthenticated: Flow<Boolean> = repository.keyguardAuthenticated.filterNotNull()
- val isShowing: Flow<Boolean> = repository.primaryBouncerShow
+ val isShowing: StateFlow<Boolean> = repository.primaryBouncerShow
val startingToHide: Flow<Unit> = repository.primaryBouncerStartingToHide.filter { it }.map {}
val isBackButtonEnabled: Flow<Boolean> = repository.isBackButtonEnabled.filterNotNull()
val showMessage: Flow<BouncerShowMessageModel> = repository.showMessage.filterNotNull()
@@ -383,8 +384,9 @@
mainHandler.removeCallbacks(showRunnable)
}
- private fun isBouncerShowing(): Boolean {
- return repository.primaryBouncerShow.value
+ /** Returns whether the primary bouncer is currently showing. */
+ fun isBouncerShowing(): Boolean {
+ return isShowing.value
}
/** Whether we want to wait to show the bouncer in case passive auth succeeds. */
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index c5e7e0d..ee046c2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -34,13 +34,11 @@
import com.android.systemui.complication.ComplicationLayoutParams.Position
import com.android.systemui.dreams.dagger.DreamOverlayModule
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
-import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel.Companion.DREAM_ANIMATION_DURATION
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.BlurUtils
import com.android.systemui.statusbar.CrossFadeHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
-import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
import javax.inject.Named
import kotlinx.coroutines.flow.MutableStateFlow
@@ -129,6 +127,12 @@
)
}
}
+
+ launch {
+ transitionViewModel.transitionEnded.collect { _ ->
+ mOverlayStateController.setExitAnimationsRunning(false)
+ }
+ }
}
configController.removeCallback(configCallback)
@@ -251,9 +255,9 @@
}
/** Starts the dream content and dream overlay exit animations. */
- fun wakeUp(doneCallback: Runnable, executor: DelayableExecutor) {
+ fun wakeUp() {
cancelAnimations()
- executor.executeDelayed(doneCallback, DREAM_ANIMATION_DURATION.inWholeMilliseconds)
+ mOverlayStateController.setExitAnimationsRunning(true)
}
/** Cancels the dream content and dream overlay animations, if they're currently running. */
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 94523df..78ac453 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -31,8 +31,6 @@
import android.view.View;
import android.view.ViewGroup;
-import androidx.annotation.NonNull;
-
import com.android.app.animation.Interpolators;
import com.android.dream.lowlight.LowLightTransitionCoordinator;
import com.android.systemui.R;
@@ -46,7 +44,6 @@
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.util.ViewController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.Arrays;
@@ -302,20 +299,15 @@
/**
* Handle the dream waking up and run any necessary animations.
- *
- * @param onAnimationEnd Callback to trigger once animations are finished.
- * @param callbackExecutor Executor to execute the callback on.
*/
- public void wakeUp(@NonNull Runnable onAnimationEnd,
- @NonNull DelayableExecutor callbackExecutor) {
+ public void wakeUp() {
// When swiping causes wakeup, do not run any animations as the dream should exit as soon
// as possible.
if (mWakingUpFromSwipe) {
- onAnimationEnd.run();
return;
}
- mDreamOverlayAnimationsController.wakeUp(onAnimationEnd, callbackExecutor);
+ mDreamOverlayAnimationsController.wakeUp();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index df2a749..553405f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -116,6 +116,17 @@
}
};
+ private final DreamOverlayStateController.Callback mExitAnimationFinishedCallback =
+ new DreamOverlayStateController.Callback() {
+ @Override
+ public void onStateChanged() {
+ if (!mStateController.areExitAnimationsRunning()) {
+ mStateController.removeCallback(mExitAnimationFinishedCallback);
+ resetCurrentDreamOverlayLocked();
+ }
+ }
+ };
+
private final DreamOverlayStateController mStateController;
@VisibleForTesting
@@ -257,10 +268,10 @@
}
@Override
- public void onWakeUp(@NonNull Runnable onCompletedCallback) {
+ public void onWakeUp() {
if (mDreamOverlayContainerViewController != null) {
mDreamOverlayCallbackController.onWakeUp();
- mDreamOverlayContainerViewController.wakeUp(onCompletedCallback, mExecutor);
+ mDreamOverlayContainerViewController.wakeUp();
}
}
@@ -330,6 +341,11 @@
}
private void resetCurrentDreamOverlayLocked() {
+ if (mStateController.areExitAnimationsRunning()) {
+ mStateController.addCallback(mExitAnimationFinishedCallback);
+ return;
+ }
+
if (mStarted && mWindow != null) {
try {
mWindowManager.removeView(mWindow.getDecorView());
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 9d0caad..d173fe5 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -89,7 +89,7 @@
// TODO(b/277338665): Tracking Bug
@JvmField
val NOTIFICATION_SHELF_REFACTOR =
- unreleasedFlag(271161129, "notification_shelf_refactor", teamfood = true)
+ unreleasedFlag(271161129, "notification_shelf_refactor")
@JvmField
val ANIMATED_NOTIFICATION_SHADE_INSETS =
@@ -259,6 +259,11 @@
@JvmField
val FP_LISTEN_OCCLUDING_APPS = unreleasedFlag(237, "fp_listen_occluding_apps")
+ /** Flag meant to guard the talkback fix for the KeyguardIndicationTextView */
+ // TODO(b/286563884): Tracking bug
+ @JvmField
+ val KEYGUARD_TALKBACK_FIX = unreleasedFlag(238, "keyguard_talkback_fix")
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -355,8 +360,7 @@
// TODO(b/280426085): Tracking Bug
@JvmField
- val NEW_BLUETOOTH_REPOSITORY =
- unreleasedFlag(612, "new_bluetooth_repository", teamfood = true)
+ val NEW_BLUETOOTH_REPOSITORY = releasedFlag(612, "new_bluetooth_repository")
// 700 - dialer/calls
// TODO(b/254512734): Tracking Bug
@@ -734,4 +738,10 @@
@JvmField
val BIGPICTURE_NOTIFICATION_LAZY_LOADING =
unreleasedFlag(283447257, "bigpicture_notification_lazy_loading")
+
+ // 2900 - CentralSurfaces-related flags
+
+ // TODO(b/285174336): Tracking Bug
+ @JvmField
+ val USE_REPOS_FOR_BOUNCER_SHOWING = unreleasedFlag(2900, "use_repos_for_bouncer_showing")
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 15164c5..ec14b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -20,6 +20,7 @@
import static android.provider.Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT;
import static android.provider.Settings.System.LOCKSCREEN_SOUNDS_ENABLED;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
@@ -36,8 +37,8 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
-import static com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel.LOCKSCREEN_ANIMATION_DURATION_MS;
import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -88,9 +89,11 @@
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl.Transaction;
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import android.view.animation.Animation;
@@ -128,12 +131,14 @@
import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.dagger.KeyguardModule;
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -166,6 +171,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+import kotlinx.coroutines.CoroutineDispatcher;
/**
* Mediates requests related to the keyguard. This includes queries about the
@@ -442,11 +450,6 @@
private final int mDreamOpenAnimationDuration;
/**
- * The duration in milliseconds of the dream close animation.
- */
- private final int mDreamCloseAnimationDuration;
-
- /**
* The animation used for hiding keyguard. This is used to fetch the animation timings if
* WindowManager is not providing us with them.
*/
@@ -1135,49 +1138,57 @@
return;
}
- final RemoteAnimationTarget primary = apps[0];
+ mRemoteAnimationTarget = apps[0];
final boolean isDream = (apps[0].taskInfo != null
&& apps[0].taskInfo.topActivityType
== WindowConfiguration.ACTIVITY_TYPE_DREAM);
- final SyncRtSurfaceTransactionApplier applier =
- new SyncRtSurfaceTransactionApplier(
- mKeyguardViewControllerLazy.get().getViewRootImpl().getView());
+ final View localView = mKeyguardViewControllerLazy.get()
+ .getViewRootImpl().getView();
+ final SyncRtSurfaceTransactionApplier applier =
+ new SyncRtSurfaceTransactionApplier(localView);
mContext.getMainExecutor().execute(() -> {
if (mUnoccludeAnimator != null) {
mUnoccludeAnimator.cancel();
}
+ if (isDream) {
+ initAlphaForAnimationTargets(wallpapers);
+ getRemoteSurfaceAlphaApplier().accept(0.0f);
+ mDreamingToLockscreenTransitionViewModel.get()
+ .startTransition();
+ return;
+ }
+
mUnoccludeAnimator = ValueAnimator.ofFloat(1f, 0f);
- mUnoccludeAnimator.setDuration(isDream ? mDreamCloseAnimationDuration
- : UNOCCLUDE_ANIMATION_DURATION);
+ mUnoccludeAnimator.setDuration(UNOCCLUDE_ANIMATION_DURATION);
mUnoccludeAnimator.setInterpolator(Interpolators.TOUCH_RESPONSE);
mUnoccludeAnimator.addUpdateListener(
animation -> {
final float animatedValue =
(float) animation.getAnimatedValue();
- final float surfaceHeight = primary.screenSpaceBounds.height();
+ final float surfaceHeight =
+ mRemoteAnimationTarget.screenSpaceBounds.height();
// Fade for all types of activities.
SyncRtSurfaceTransactionApplier.SurfaceParams.Builder
paramsBuilder =
new SyncRtSurfaceTransactionApplier.SurfaceParams
- .Builder(primary.leash)
+ .Builder(mRemoteAnimationTarget.leash)
.withAlpha(animatedValue);
- // Set translate if the occluding activity isn't Dream.
- if (!isDream) {
- mUnoccludeMatrix.setTranslate(
- 0f,
- (1f - animatedValue)
- * surfaceHeight
- * UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT);
- paramsBuilder.withMatrix(mUnoccludeMatrix).withCornerRadius(
- mWindowCornerRadius);
- }
+ mUnoccludeMatrix.setTranslate(
+ 0f,
+ (1f - animatedValue)
+ * surfaceHeight
+ * UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT);
+
+ paramsBuilder.withMatrix(mUnoccludeMatrix).withCornerRadius(
+ mWindowCornerRadius);
+
applier.scheduleApply(paramsBuilder.build());
});
mUnoccludeAnimator.addListener(new AnimatorListenerAdapter() {
@@ -1199,6 +1210,34 @@
}
};
+ private static void initAlphaForAnimationTargets(
+ @android.annotation.NonNull RemoteAnimationTarget[] targets
+ ) {
+ for (RemoteAnimationTarget target : targets) {
+ if (target.mode != MODE_OPENING) continue;
+
+ try (Transaction t = new Transaction()) {
+ t.setAlpha(target.leash, 1.f);
+ t.apply();
+ }
+ }
+ }
+
+ private Consumer<Float> getRemoteSurfaceAlphaApplier() {
+ return (Float alpha) -> {
+ if (mRemoteAnimationTarget == null) return;
+ final View localView = mKeyguardViewControllerLazy.get().getViewRootImpl().getView();
+ final SyncRtSurfaceTransactionApplier applier =
+ new SyncRtSurfaceTransactionApplier(localView);
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ params =
+ new SyncRtSurfaceTransactionApplier.SurfaceParams
+ .Builder(mRemoteAnimationTarget.leash)
+ .withAlpha(alpha).build();
+ applier.scheduleApply(params);
+ };
+ }
+
private DeviceConfigProxy mDeviceConfig;
private DozeParameters mDozeParameters;
@@ -1228,6 +1267,10 @@
private FeatureFlags mFeatureFlags;
private final UiEventLogger mUiEventLogger;
private final SessionTracker mSessionTracker;
+ private final CoroutineDispatcher mMainDispatcher;
+ private final Lazy<DreamingToLockscreenTransitionViewModel>
+ mDreamingToLockscreenTransitionViewModel;
+ private RemoteAnimationTarget mRemoteAnimationTarget;
/**
* Injected constructor. See {@link KeyguardModule}.
@@ -1266,7 +1309,9 @@
FeatureFlags featureFlags,
SecureSettings secureSettings,
SystemSettings systemSettings,
- SystemClock systemClock) {
+ SystemClock systemClock,
+ @Main CoroutineDispatcher mainDispatcher,
+ Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel) {
mContext = context;
mUserTracker = userTracker;
mFalsingCollector = falsingCollector;
@@ -1324,11 +1369,13 @@
mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
mDreamOpenAnimationDuration = (int) DREAMING_ANIMATION_DURATION_MS;
- mDreamCloseAnimationDuration = (int) LOCKSCREEN_ANIMATION_DURATION_MS;
mFeatureFlags = featureFlags;
mUiEventLogger = uiEventLogger;
mSessionTracker = sessionTracker;
+
+ mMainDispatcher = mainDispatcher;
+ mDreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel;
}
public void userActivity() {
@@ -1447,6 +1494,13 @@
mUpdateMonitor.registerCallback(mUpdateCallback);
adjustStatusBarLocked();
mDreamOverlayStateController.addCallback(mDreamOverlayStateCallback);
+
+ ViewRootImpl viewRootImpl = mKeyguardViewControllerLazy.get().getViewRootImpl();
+ if (viewRootImpl != null) {
+ collectFlow(viewRootImpl.getView(),
+ mDreamingToLockscreenTransitionViewModel.get().getDreamOverlayAlpha(),
+ getRemoteSurfaceAlphaApplier(), mMainDispatcher);
+ }
}
// Most services aren't available until the system reaches the ready state, so we
// send it here when the device first boots.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 1c5bb5f..6d6205c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -37,6 +37,7 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
@@ -51,6 +52,7 @@
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule;
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger;
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl;
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.settings.UserTracker;
@@ -75,6 +77,8 @@
import java.util.concurrent.Executor;
+import kotlinx.coroutines.CoroutineDispatcher;
+
/**
* Dagger Module providing keyguard.
*/
@@ -134,7 +138,9 @@
FeatureFlags featureFlags,
SecureSettings secureSettings,
SystemSettings systemSettings,
- SystemClock systemClock) {
+ SystemClock systemClock,
+ @Main CoroutineDispatcher mainDispatcher,
+ Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel) {
return new KeyguardViewMediator(
context,
uiEventLogger,
@@ -171,7 +177,9 @@
featureFlags,
secureSettings,
systemSettings,
- systemClock);
+ systemClock,
+ mainDispatcher,
+ dreamingToLockscreenTransitionViewModel);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index c94aa11..84cd3ef 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -165,35 +165,28 @@
// An animator was provided, so use it to run the transition
animator.setFloatValues(startingValue, 1f)
animator.duration = ((1f - startingValue) * animator.duration).toLong()
- val updateListener =
- object : AnimatorUpdateListener {
- override fun onAnimationUpdate(animation: ValueAnimator) {
- emitTransition(
- TransitionStep(
- info,
- (animation.getAnimatedValue() as Float),
- TransitionState.RUNNING
- )
- )
- }
- }
+ val updateListener = AnimatorUpdateListener { animation ->
+ emitTransition(
+ TransitionStep(
+ info,
+ (animation.animatedValue as Float),
+ TransitionState.RUNNING
+ )
+ )
+ }
val adapter =
object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator) {
emitTransition(TransitionStep(info, startingValue, TransitionState.STARTED))
}
override fun onAnimationCancel(animation: Animator) {
- endAnimation(animation, lastStep.value, TransitionState.CANCELED)
+ endAnimation(lastStep.value, TransitionState.CANCELED)
}
override fun onAnimationEnd(animation: Animator) {
- endAnimation(animation, 1f, TransitionState.FINISHED)
+ endAnimation(1f, TransitionState.FINISHED)
}
- private fun endAnimation(
- animation: Animator,
- value: Float,
- state: TransitionState
- ) {
+ private fun endAnimation(value: Float, state: TransitionState) {
emitTransition(TransitionStep(info, value, state))
animator.removeListener(this)
animator.removeUpdateListener(updateListener)
@@ -206,7 +199,7 @@
return@startTransition null
}
?: run {
- emitTransition(TransitionStep(info, 0f, TransitionState.STARTED))
+ emitTransition(TransitionStep(info, startingValue, TransitionState.STARTED))
// No animator, so it's manual. Provide a mechanism to callback
updateTransitionId = UUID.randomUUID()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 323fc31..ee2c2df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -52,7 +52,7 @@
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { (wakefulnessModel, lastStartedTransition) ->
if (
- wakefulnessModel.isStartingToWake() &&
+ wakefulnessModel.isStartingToWakeOrAwake() &&
lastStartedTransition.to == KeyguardState.DOZING
) {
keyguardTransitionRepository.startTransition(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 36c8eb1..ccf4bc1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -23,7 +23,6 @@
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.DozeStateModel
-import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
@@ -48,39 +47,23 @@
) : TransitionInteractor(FromDreamingTransitionInteractor::class.simpleName!!) {
override fun start() {
- listenForDreamingToLockscreen()
listenForDreamingToOccluded()
listenForDreamingToGone()
listenForDreamingToDozing()
}
- private fun listenForDreamingToLockscreen() {
+ fun startToLockscreenTransition() {
scope.launch {
- keyguardInteractor.isAbleToDream
- .sample(
- combine(
- keyguardInteractor.dozeTransitionModel,
- keyguardTransitionInteractor.startedKeyguardTransitionStep,
- ::Pair
- ),
- ::toTriple
+ if (keyguardTransitionInteractor.startedKeyguardState.value == KeyguardState.DREAMING) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.DREAMING,
+ KeyguardState.LOCKSCREEN,
+ getAnimator(TO_LOCKSCREEN_DURATION),
+ )
)
- .collect { (isDreaming, dozeTransitionModel, lastStartedTransition) ->
- if (
- !isDreaming &&
- isDozeOff(dozeTransitionModel.to) &&
- lastStartedTransition.to == KeyguardState.DREAMING
- ) {
- keyguardTransitionRepository.startTransition(
- TransitionInfo(
- name,
- KeyguardState.DREAMING,
- KeyguardState.LOCKSCREEN,
- getAnimator(TO_LOCKSCREEN_DURATION),
- )
- )
- }
- }
+ }
}
}
@@ -173,6 +156,6 @@
companion object {
private val DEFAULT_DURATION = 500.milliseconds
- val TO_LOCKSCREEN_DURATION = 1183.milliseconds
+ val TO_LOCKSCREEN_DURATION = 1167.milliseconds
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index a499e3d..228290a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -114,7 +114,7 @@
isDreaming && isDozeOff(dozeTransitionModel.to)
}
.sample(wakefulnessModel) { isAbleToDream, wakefulnessModel ->
- isAbleToDream && wakefulnessModel.isStartingToWake()
+ isAbleToDream && wakefulnessModel.isStartingToWakeOrAwake()
}
.flatMapLatest { isAbleToDream ->
flow {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index da0ada1..42f12f8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
@@ -29,10 +30,14 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.stateIn
/** Encapsulates business-logic related to the keyguard transitions. */
@SysUISingleton
@@ -40,6 +45,7 @@
@Inject
constructor(
private val repository: KeyguardTransitionRepository,
+ @Application val scope: CoroutineScope,
) {
/** (any)->GONE transition information */
val anyStateToGoneTransition: Flow<TransitionStep> =
@@ -108,10 +114,17 @@
val finishedKeyguardTransitionStep: Flow<TransitionStep> =
repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
- /** The last completed [KeyguardState] transition */
- val finishedKeyguardState: Flow<KeyguardState> =
- finishedKeyguardTransitionStep.map { step -> step.to }
+ /** The destination state of the last started transition */
+ val startedKeyguardState: StateFlow<KeyguardState> =
+ startedKeyguardTransitionStep
+ .map { step -> step.to }
+ .stateIn(scope, SharingStarted.Eagerly, KeyguardState.OFF)
+ /** The last completed [KeyguardState] transition */
+ val finishedKeyguardState: StateFlow<KeyguardState> =
+ finishedKeyguardTransitionStep
+ .map { step -> step.to }
+ .stateIn(scope, SharingStarted.Eagerly, LOCKSCREEN)
/**
* The amount of transition into or out of the given [KeyguardState].
*
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
index dd57713..cfd9e08 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
@@ -33,6 +33,8 @@
fun isDeviceInteractive() = !isAsleep()
+ fun isStartingToWakeOrAwake() = isStartingToWake() || state == WakefulnessState.AWAKE
+
fun isStartingToSleepFromPowerButton() =
isStartingToSleep() && lastWakeReason == WakeSleepReason.POWER_BUTTON
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index db23109..1c2e85b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -404,8 +404,7 @@
val darkClockColor = wallpaperColorScheme?.accent2?.s600
/** Note that when [wallpaperColors] is null, isWallpaperDark is true. */
val isWallpaperDark: Boolean =
- (wallpaperColors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) !=
- WallpaperColors.HINT_SUPPORTS_DARK_TEXT
+ (wallpaperColors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) == 0
clock.events.onSeedColorChanged(
if (isWallpaperDark) lightClockColor else darkClockColor
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index 2c9a9b3..9ca4bd6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -16,15 +16,17 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
-import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
+import com.android.app.animation.Interpolators.EMPHASIZED
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
/**
* Breaks down DREAMING->LOCKSCREEN transition into discrete steps for corresponding views to
@@ -34,22 +36,32 @@
class DreamingToLockscreenTransitionViewModel
@Inject
constructor(
- private val interactor: KeyguardTransitionInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
) {
+ fun startTransition() = fromDreamingTransitionInteractor.startToLockscreenTransition()
+
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
transitionDuration = TO_LOCKSCREEN_DURATION,
- transitionFlow = interactor.dreamingToLockscreenTransition,
+ transitionFlow = keyguardTransitionInteractor.dreamingToLockscreenTransition,
)
+ val transitionEnded =
+ keyguardTransitionInteractor.dreamingToLockscreenTransition.filter { step ->
+ step.transitionState == TransitionState.FINISHED ||
+ step.transitionState == TransitionState.CANCELED
+ }
+
/** Dream overlay y-translation on exit */
fun dreamOverlayTranslationY(translatePx: Int): Flow<Float> {
return transitionAnimation.createFlow(
- duration = 600.milliseconds,
+ duration = TO_LOCKSCREEN_DURATION,
onStep = { it * translatePx },
- interpolator = EMPHASIZED_ACCELERATE,
+ interpolator = EMPHASIZED,
)
}
+
/** Dream overlay views alpha - fade out */
val dreamOverlayAlpha: Flow<Float> =
transitionAnimation.createFlow(
@@ -65,7 +77,7 @@
// Reset on cancel or finish
onFinish = { 0f },
onCancel = { 0f },
- interpolator = EMPHASIZED_DECELERATE,
+ interpolator = EMPHASIZED,
)
}
@@ -76,12 +88,4 @@
duration = 250.milliseconds,
onStep = { it },
)
-
- companion object {
- /* Length of time before ending the dream activity, in order to start unoccluding */
- val DREAM_ANIMATION_DURATION = 250.milliseconds
- @JvmField
- val LOCKSCREEN_ANIMATION_DURATION_MS =
- (TO_LOCKSCREEN_DURATION - DREAM_ANIMATION_DURATION).inWholeMilliseconds
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
index c6187dd..a3ae67d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
@@ -33,7 +33,7 @@
class LockscreenToDreamingTransitionViewModel
@Inject
constructor(
- private val interactor: KeyguardTransitionInteractor,
+ interactor: KeyguardTransitionInteractor,
) {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 88ffa8d..318cd99 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -24,7 +24,6 @@
import android.content.res.ColorStateList;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -175,9 +174,8 @@
mCurrentActivePosition = position;
updateFullItemClickListener(v -> onItemClick(v, device));
setSingleLineLayout(getItemTitle(device));
- initMutingExpectedDevice();
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
- && device.hasSubtext()) {
+ initFakeActiveDevice();
+ } else if (device.hasSubtext()) {
boolean isActiveWithOngoingSession =
(device.hasOngoingSession() && (currentlyConnected || isDeviceIncluded(
mController.getSelectedMediaDevice(), device)));
@@ -267,6 +265,27 @@
setUpDeviceIcon(device);
updateFullItemClickListener(v -> cancelMuteAwaitConnection());
setSingleLineLayout(getItemTitle(device));
+ } else if (device.hasOngoingSession()) {
+ mCurrentActivePosition = position;
+ if (device.isHostForOngoingSession()) {
+ updateTitleIcon(R.drawable.media_output_icon_volume,
+ mController.getColorItemContent());
+ updateEndClickAreaAsSessionEditing(device);
+ mEndClickIcon.setVisibility(View.VISIBLE);
+ setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
+ false /* showProgressBar */, false /* showCheckBox */,
+ true /* showEndTouchArea */);
+ initSeekbar(device, isCurrentSeekbarInvisible);
+ } else {
+ updateDeviceStatusIcon(mContext.getDrawable(
+ R.drawable.ic_sound_bars_anim));
+ mStatusIcon.setVisibility(View.VISIBLE);
+ updateSingleLineLayoutContentAlpha(
+ updateClickActionBasedOnSelectionBehavior(device)
+ ? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
+ setSingleLineLayout(getItemTitle(device));
+ initFakeActiveDevice();
+ }
} else if (mController.isCurrentConnectedDeviceRemote()
&& !mController.getSelectableMediaDevice().isEmpty()) {
//If device is connected and there's other selectable devices, layout as
@@ -351,7 +370,7 @@
ColorStateList.valueOf(mController.getColorItemContent()));
mEndClickIcon.setOnClickListener(
v -> mController.tryToLaunchInAppRoutingIntent(device.getId(), v));
- mEndTouchArea.setOnClickListener(v -> mCheckBox.performClick());
+ mEndTouchArea.setOnClickListener(v -> mEndClickIcon.performClick());
}
public void updateEndClickAreaColor(int color) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 01f7904..b88eba9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -188,6 +188,7 @@
mContainerLayout.setContentDescription(null);
mTitleText.setTextColor(mController.getColorItemContent());
mSubTitleText.setTextColor(mController.getColorItemContent());
+ mSubTitleText.setSelected(true);
mTwoLineTitleText.setTextColor(mController.getColorItemContent());
mVolumeValueText.setTextColor(mController.getColorItemContent());
mSeekBar.setProgressTintList(
@@ -417,7 +418,7 @@
mIconAreaLayout.setOnClickListener(listener);
}
- void initMutingExpectedDevice() {
+ void initFakeActiveDevice() {
disableSeekBar();
updateTitleIcon(R.drawable.media_output_icon_volume,
mController.getColorItemContent());
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 5e9406c..cf192f9 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -254,8 +254,9 @@
private boolean mDeferSetIsOnLeftEdge;
private boolean mIsAttached;
- private boolean mIsGesturalModeEnabled;
+ private boolean mIsGestureHandlingEnabled;
private boolean mIsTrackpadConnected;
+ private boolean mInGestureNavMode;
private boolean mUsingThreeButtonNav;
private boolean mIsEnabled;
private boolean mIsNavBarShownTransiently;
@@ -562,9 +563,7 @@
mInputManager.registerInputDeviceListener(mInputDeviceListener, mMainHandler);
int [] inputDevices = mInputManager.getInputDeviceIds();
for (int inputDeviceId : inputDevices) {
- if (isTrackpadDevice(inputDeviceId)) {
- mIsTrackpadConnected = true;
- }
+ mInputDeviceListener.onInputDeviceAdded(inputDeviceId);
}
}
updateIsEnabled();
@@ -589,8 +588,7 @@
*/
public void onNavigationModeChanged(int mode) {
mUsingThreeButtonNav = QuickStepContract.isLegacyMode(mode);
- mIsGesturalModeEnabled = QuickStepContract.isGesturalMode(mode) || (
- mIsTrackpadGestureFeaturesEnabled && mUsingThreeButtonNav && mIsTrackpadConnected);
+ mInGestureNavMode = QuickStepContract.isGesturalMode(mode);
updateIsEnabled();
updateCurrentUserResources();
}
@@ -613,81 +611,83 @@
private void updateIsEnabled() {
try {
Trace.beginSection("EdgeBackGestureHandler#updateIsEnabled");
- updateIsEnabledTraced();
+
+ mIsGestureHandlingEnabled =
+ mInGestureNavMode || (mIsTrackpadGestureFeaturesEnabled && mUsingThreeButtonNav
+ && mIsTrackpadConnected);
+ boolean isEnabled = mIsAttached && mIsGestureHandlingEnabled;
+ if (isEnabled == mIsEnabled) {
+ return;
+ }
+ mIsEnabled = isEnabled;
+ disposeInputChannel();
+
+ if (mEdgeBackPlugin != null) {
+ mEdgeBackPlugin.onDestroy();
+ mEdgeBackPlugin = null;
+ }
+
+ if (!mIsEnabled) {
+ mGestureNavigationSettingsObserver.unregister();
+ if (DEBUG_MISSING_GESTURE) {
+ Log.d(DEBUG_MISSING_GESTURE_TAG, "Unregister display listener");
+ }
+ mPluginManager.removePluginListener(this);
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
+ mTaskStackListener);
+ DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
+ mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(null));
+
+ try {
+ mWindowManagerService.unregisterSystemGestureExclusionListener(
+ mGestureExclusionListener, mDisplayId);
+ } catch (RemoteException | IllegalArgumentException e) {
+ Log.e(TAG, "Failed to unregister window manager callbacks", e);
+ }
+
+ } else {
+ mGestureNavigationSettingsObserver.register();
+ updateDisplaySize();
+ if (DEBUG_MISSING_GESTURE) {
+ Log.d(DEBUG_MISSING_GESTURE_TAG, "Register display listener");
+ }
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(
+ mTaskStackListener);
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ mMainExecutor::execute, mOnPropertiesChangedListener);
+ mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(
+ mOnIsInPipStateChangedListener));
+ mDesktopModeOptional.ifPresent(
+ dm -> dm.addDesktopGestureExclusionRegionListener(
+ mDesktopCornersChangedListener, mMainExecutor));
+
+ try {
+ mWindowManagerService.registerSystemGestureExclusionListener(
+ mGestureExclusionListener, mDisplayId);
+ } catch (RemoteException | IllegalArgumentException e) {
+ Log.e(TAG, "Failed to register window manager callbacks", e);
+ }
+
+ // Register input event receiver
+ mInputMonitor = mContext.getSystemService(InputManager.class).monitorGestureInput(
+ "edge-swipe", mDisplayId);
+ mInputEventReceiver = new InputChannelCompat.InputEventReceiver(
+ mInputMonitor.getInputChannel(), Looper.getMainLooper(),
+ Choreographer.getInstance(), this::onInputEvent);
+
+ // Add a nav bar panel window
+ mIsNewBackAffordanceEnabled = mFeatureFlags.isEnabled(Flags.NEW_BACK_AFFORDANCE);
+ resetEdgeBackPlugin();
+ mPluginManager.addPluginListener(
+ this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
+ }
+ // Update the ML model resources.
+ updateMLModelState();
} finally {
Trace.endSection();
}
}
- private void updateIsEnabledTraced() {
- boolean isEnabled = mIsAttached && mIsGesturalModeEnabled;
- if (isEnabled == mIsEnabled) {
- return;
- }
- mIsEnabled = isEnabled;
- disposeInputChannel();
-
- if (mEdgeBackPlugin != null) {
- mEdgeBackPlugin.onDestroy();
- mEdgeBackPlugin = null;
- }
-
- if (!mIsEnabled) {
- mGestureNavigationSettingsObserver.unregister();
- if (DEBUG_MISSING_GESTURE) {
- Log.d(DEBUG_MISSING_GESTURE_TAG, "Unregister display listener");
- }
- mPluginManager.removePluginListener(this);
- TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
- DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
- mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(null));
-
- try {
- mWindowManagerService.unregisterSystemGestureExclusionListener(
- mGestureExclusionListener, mDisplayId);
- } catch (RemoteException | IllegalArgumentException e) {
- Log.e(TAG, "Failed to unregister window manager callbacks", e);
- }
-
- } else {
- mGestureNavigationSettingsObserver.register();
- updateDisplaySize();
- if (DEBUG_MISSING_GESTURE) {
- Log.d(DEBUG_MISSING_GESTURE_TAG, "Register display listener");
- }
- TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
- mMainExecutor::execute, mOnPropertiesChangedListener);
- mPipOptional.ifPresent(
- pip -> pip.setOnIsInPipStateChangedListener(mOnIsInPipStateChangedListener));
- mDesktopModeOptional.ifPresent(
- dm -> dm.addDesktopGestureExclusionRegionListener(
- mDesktopCornersChangedListener, mMainExecutor));
-
- try {
- mWindowManagerService.registerSystemGestureExclusionListener(
- mGestureExclusionListener, mDisplayId);
- } catch (RemoteException | IllegalArgumentException e) {
- Log.e(TAG, "Failed to register window manager callbacks", e);
- }
-
- // Register input event receiver
- mInputMonitor = mContext.getSystemService(InputManager.class).monitorGestureInput(
- "edge-swipe", mDisplayId);
- mInputEventReceiver = new InputChannelCompat.InputEventReceiver(
- mInputMonitor.getInputChannel(), Looper.getMainLooper(),
- Choreographer.getInstance(), this::onInputEvent);
-
- // Add a nav bar panel window
- mIsNewBackAffordanceEnabled = mFeatureFlags.isEnabled(Flags.NEW_BACK_AFFORDANCE);
- resetEdgeBackPlugin();
- mPluginManager.addPluginListener(
- this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
- }
- // Update the ML model resources.
- updateMLModelState();
- }
-
@Override
public void onPluginConnected(NavigationEdgeBackPlugin plugin, Context context) {
setEdgeBackPlugin(plugin);
@@ -762,9 +762,9 @@
}
private void updateMLModelState() {
- boolean newState =
- mIsGesturalModeEnabled && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL, false);
+ boolean newState = mIsGestureHandlingEnabled && DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL, false);
if (newState == mUseMLModel) {
return;
@@ -1234,7 +1234,7 @@
pw.println(" mIsEnabled=" + mIsEnabled);
pw.println(" mIsAttached=" + mIsAttached);
pw.println(" mIsBackGestureAllowed=" + mIsBackGestureAllowed);
- pw.println(" mIsGesturalModeEnabled=" + mIsGesturalModeEnabled);
+ pw.println(" mIsGestureHandlingEnabled=" + mIsGestureHandlingEnabled);
pw.println(" mIsNavBarShownTransiently=" + mIsNavBarShownTransiently);
pw.println(" mGestureBlockingActivityRunning=" + mGestureBlockingActivityRunning);
pw.println(" mAllowGesture=" + mAllowGesture);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java
index 3e78489..9962c91 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java
@@ -16,10 +16,6 @@
package com.android.systemui.screenrecord;
-import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.INTERNAL;
-import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC;
-import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC_AND_INTERNAL;
-
import android.content.Context;
import android.content.res.Resources;
import android.view.LayoutInflater;
@@ -40,9 +36,6 @@
private LinearLayout mSelectedMic;
private LinearLayout mSelectedInternal;
private LinearLayout mSelectedMicAndInternal;
- private LinearLayout mMicOption;
- private LinearLayout mMicAndInternalOption;
- private LinearLayout mInternalOption;
public ScreenRecordingAdapter(Context context, int resource,
List<ScreenRecordingAudioSource> objects) {
@@ -54,28 +47,21 @@
mSelectedInternal = getSelected(R.string.screenrecord_device_audio_label);
mSelectedMic = getSelected(R.string.screenrecord_mic_label);
mSelectedMicAndInternal = getSelected(R.string.screenrecord_device_audio_and_mic_label);
-
- mMicOption = getOption(R.string.screenrecord_mic_label, Resources.ID_NULL);
- mMicOption.removeViewAt(1);
-
- mMicAndInternalOption = getOption(
- R.string.screenrecord_device_audio_and_mic_label, Resources.ID_NULL);
- mMicAndInternalOption.removeViewAt(1);
-
- mInternalOption = getOption(R.string.screenrecord_device_audio_label,
- R.string.screenrecord_device_audio_description);
}
private LinearLayout getOption(int label, int description) {
- LayoutInflater inflater = (LayoutInflater) getContext()
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ LayoutInflater inflater = LayoutInflater.from(getContext());
LinearLayout layout = (LinearLayout) inflater
.inflate(R.layout.screen_record_dialog_audio_source, null, false);
((TextView) layout.findViewById(R.id.screen_recording_dialog_source_text))
.setText(label);
- if (description != Resources.ID_NULL)
- ((TextView) layout.findViewById(R.id.screen_recording_dialog_source_description))
- .setText(description);
+ TextView descriptionView = layout.findViewById(
+ R.id.screen_recording_dialog_source_description);
+ if (description != Resources.ID_NULL) {
+ descriptionView.setText(description);
+ } else {
+ descriptionView.setVisibility(View.GONE);
+ }
return layout;
}
@@ -92,11 +78,13 @@
public View getDropDownView(int position, View convertView, ViewGroup parent) {
switch (getItem(position)) {
case INTERNAL:
- return mInternalOption;
+ return getOption(R.string.screenrecord_device_audio_label,
+ R.string.screenrecord_device_audio_description);
case MIC_AND_INTERNAL:
- return mMicAndInternalOption;
+ return getOption(
+ R.string.screenrecord_device_audio_and_mic_label, Resources.ID_NULL);
case MIC:
- return mMicOption;
+ return getOption(R.string.screenrecord_mic_label, Resources.ID_NULL);
default:
return super.getDropDownView(position, convertView, parent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
index 2e47ab6..24fe7d5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
@@ -42,8 +42,8 @@
.setSourceCrop(crop)
.build()
val syncScreenCapture = ScreenCapture.createSyncCaptureListener()
- windowManager.captureDisplay(displayId, captureArgs, syncScreenCapture.first)
- val buffer = syncScreenCapture.second.get()
+ windowManager.captureDisplay(displayId, captureArgs, syncScreenCapture)
+ val buffer = syncScreenCapture.getBuffer()
return buffer?.asBitmap()
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java
index e0b9f9b..4cc2f62 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java
@@ -20,7 +20,7 @@
import android.content.Intent;
import android.os.IBinder;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
-import android.window.ScreenCapture.ScreenshotSync;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import androidx.annotation.Nullable;
@@ -59,9 +59,9 @@
return null;
}
- ScreenshotSync screenshotSync =
+ SynchronousScreenCaptureListener screenshotSync =
mOptionalBubbles.get().getScreenshotExcludingBubble(displayId);
- ScreenshotHardwareBuffer screenshotHardwareBuffer = screenshotSync.get();
+ ScreenshotHardwareBuffer screenshotHardwareBuffer = screenshotSync.getBuffer();
if (screenshotHardwareBuffer == null) {
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 7294137..6d00a2a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1659,10 +1659,9 @@
Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding));
mKeyguardNotificationBottomPadding = bottomPadding;
- float staticTopPadding = mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()
- // getMinStackScrollerPadding is from the top of the screen,
- // but we need it from the top of the NSSL.
- - mNotificationStackScrollLayoutController.getTop();
+ float staticTopPadding = mClockPositionAlgorithm.getLockscreenNotifPadding(
+ mNotificationStackScrollLayoutController.getTop());
+
mKeyguardNotificationTopPadding = staticTopPadding;
// To debug the available space, enable debug lines in this class. If you change how the
@@ -1676,8 +1675,8 @@
Log.i(TAG, "\n");
Log.i(TAG, "staticTopPadding[" + staticTopPadding
+ "] = Clock.padding["
- + mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()
- + "] - NSSLC.top[" + mNotificationStackScrollLayoutController.getTop()
+ + mClockPositionAlgorithm.getLockscreenNotifPadding(
+ mNotificationStackScrollLayoutController.getTop())
+ "]"
);
Log.i(TAG, "bottomPadding[" + bottomPadding
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 81fe3ca..fba0120 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -27,6 +27,7 @@
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.fragments.FragmentService
import com.android.systemui.navigationbar.NavigationModeController
@@ -45,6 +46,7 @@
@VisibleForTesting
internal const val INSET_DEBOUNCE_MILLIS = 500L
+@SysUISingleton
class NotificationsQSContainerController @Inject constructor(
view: NotificationsQuickSettingsContainer,
private val navigationModeController: NavigationModeController,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index 7dff6ea..e5b84bd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -55,6 +55,7 @@
private Consumer<QS> mQSFragmentAttachedListener = qs -> {};
private QS mQs;
private View mQSContainer;
+ private int mLastQSPaddingBottom;
/**
* These are used to compute the bounding box containing the shade and the notification scrim,
@@ -83,6 +84,10 @@
mQs = (QS) fragment;
mQSFragmentAttachedListener.accept(mQs);
mQSContainer = mQs.getView().findViewById(R.id.quick_settings_container);
+ // We need to restore the bottom padding as the fragment may have been recreated due to
+ // some special Configuration change, so we apply the last known padding (this will be
+ // correct even if it has changed while the fragment was destroyed and re-created).
+ setQSContainerPaddingBottom(mLastQSPaddingBottom);
}
@Override
@@ -109,6 +114,7 @@
}
public void setQSContainerPaddingBottom(int paddingBottom) {
+ mLastQSPaddingBottom = paddingBottom;
if (mQSContainer != null) {
mQSContainer.setPadding(
mQSContainer.getPaddingLeft(),
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
index 9b797a7..ee4e98e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
@@ -22,6 +22,7 @@
import android.view.GestureDetector
import android.view.MotionEvent
import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
@@ -29,7 +30,6 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.tuner.TunerService
import com.android.systemui.tuner.TunerService.Tunable
import java.io.PrintWriter
@@ -44,9 +44,8 @@
* screen is still ON and not in the true AoD display state. When the device is in the true AoD
* display state, wake-ups are handled by [com.android.systemui.doze.DozeSensors].
*/
-@CentralSurfacesComponent.CentralSurfacesScope
+@SysUISingleton
class PulsingGestureListener @Inject constructor(
- private val notificationShadeWindowView: NotificationShadeWindowView,
private val falsingManager: FalsingManager,
private val dockManager: DockManager,
private val powerInteractor: PowerInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index 1752ff6..4b2a65f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -37,6 +37,10 @@
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.statusbar.NotificationShelf
+import com.android.systemui.statusbar.NotificationShelfController
+import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent
+import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.TapAgainView
@@ -49,6 +53,7 @@
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import javax.inject.Named
+import javax.inject.Provider
/** Module for classes related to the notification shade. */
@Module
@@ -102,6 +107,32 @@
return notificationShadeWindowView.findViewById(R.id.notification_stack_scroller)
}
+ @Provides
+ @SysUISingleton
+ fun providesNotificationShelfController(
+ featureFlags: FeatureFlags,
+ newImpl: Provider<NotificationShelfViewBinderWrapperControllerImpl>,
+ notificationShelfComponentBuilder: NotificationShelfComponent.Builder,
+ layoutInflater: LayoutInflater,
+ notificationStackScrollLayout: NotificationStackScrollLayout,
+ ): NotificationShelfController {
+ return if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ newImpl.get()
+ } else {
+ val shelfView =
+ layoutInflater.inflate(
+ R.layout.status_bar_notification_shelf,
+ notificationStackScrollLayout,
+ false
+ ) as NotificationShelf
+ val component =
+ notificationShelfComponentBuilder.notificationShelf(shelfView).build()
+ val notificationShelfController = component.notificationShelfController
+ notificationShelfController.init()
+ notificationShelfController
+ }
+ }
+
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@Provides
@SysUISingleton
@@ -157,6 +188,15 @@
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@Provides
@SysUISingleton
+ fun providesNotificationsQuickSettingsContainer(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): NotificationsQuickSettingsContainer {
+ return notificationShadeWindowView.findViewById(R.id.notification_container_parent)
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
@Named(SHADE_HEADER)
fun providesShadeHeaderView(
notificationShadeWindowView: NotificationShadeWindowView,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 1c9bc60..5dcf6d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -43,6 +43,11 @@
void onUserSwitched(int newUserId);
/**
+ * Called when a new row is created and bound to a notification.
+ */
+ void onBindRow(ExpandableNotificationRow row);
+
+ /**
* @return true iff the device is in vr mode
*/
boolean isDeviceInVrMode();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index b116246..fb16d6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -61,6 +61,7 @@
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Pair;
import androidx.annotation.NonNull;
@@ -513,10 +514,16 @@
* @return True if the notification was removed, false otherwise.
*/
private boolean tryRemoveNotification(NotificationEntry entry) {
- if (mNotificationSet.get(entry.getKey()) != entry) {
+ final NotificationEntry storedEntry = mNotificationSet.get(entry.getKey());
+ if (storedEntry == null) {
+ Log.wtf(TAG, "TRY REMOVE non-existent notification " + logKey(entry));
+ return false;
+ } else if (storedEntry != entry) {
throw mEulogizer.record(
- new IllegalStateException("No notification to remove with key "
- + logKey(entry)));
+ new IllegalStateException("Mismatched stored and tryRemoved entries"
+ + " for key " + logKey(entry) + ":"
+ + " stored=@" + Integer.toHexString(storedEntry.hashCode())
+ + " tryRemoved=@" + Integer.toHexString(entry.hashCode())));
}
if (!entry.isCanceled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 706594c..2fa070c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -28,9 +28,12 @@
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.expansionChanges
+import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -47,29 +50,30 @@
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import java.io.PrintWriter
import javax.inject.Inject
+import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emitAll
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
/**
* Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section
- * headers on the lockscreen. If enabled, it will also track and hide seen notifications on the
- * lockscreen.
+ * headers on the lockscreen.
*/
@CoordinatorScope
class KeyguardCoordinator
@@ -82,6 +86,7 @@
private val keyguardRepository: KeyguardRepository,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val logger: KeyguardCoordinatorLogger,
+ private val notifPipelineFlags: NotifPipelineFlags,
@Application private val scope: CoroutineScope,
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
private val secureSettings: SecureSettings,
@@ -90,8 +95,6 @@
) : Coordinator, Dumpable {
private val unseenNotifications = mutableSetOf<NotificationEntry>()
- private val unseenEntryAdded = MutableSharedFlow<NotificationEntry>(extraBufferCapacity = 1)
- private val unseenEntryRemoved = MutableSharedFlow<NotificationEntry>(extraBufferCapacity = 1)
private var unseenFilterEnabled = false
override fun attach(pipeline: NotifPipeline) {
@@ -106,131 +109,79 @@
private fun attachUnseenFilter(pipeline: NotifPipeline) {
pipeline.addFinalizeFilter(unseenNotifFilter)
pipeline.addCollectionListener(collectionListener)
- scope.launch { trackSeenNotifications() }
+ scope.launch { trackUnseenNotificationsWhileUnlocked() }
scope.launch { invalidateWhenUnseenSettingChanges() }
dumpManager.registerDumpable(this)
}
- private suspend fun trackSeenNotifications() {
- // Whether or not keyguard is visible (or occluded).
- val isKeyguardPresent: Flow<Boolean> =
- keyguardTransitionRepository.transitions
- .map { step -> step.to != KeyguardState.GONE }
+ private suspend fun trackUnseenNotificationsWhileUnlocked() {
+ // Whether or not we're actively tracking unseen notifications to mark them as seen when
+ // appropriate.
+ val isTrackingUnseen: Flow<Boolean> =
+ keyguardRepository.isKeyguardShowing
+ // transformLatest so that we can cancel listening to keyguard transitions once
+ // isKeyguardShowing changes (after a successful transition to the keyguard).
+ .transformLatest { isShowing ->
+ if (isShowing) {
+ // If the keyguard is showing, we're not tracking unseen.
+ emit(false)
+ } else {
+ // If the keyguard stops showing, then start tracking unseen notifications.
+ emit(true)
+ // If the screen is turning off, stop tracking, but if that transition is
+ // cancelled, then start again.
+ emitAll(
+ keyguardTransitionRepository.transitions.map { step ->
+ !step.isScreenTurningOff
+ }
+ )
+ }
+ }
+ // Prevent double emit of `false` caused by transition to AOD, followed by keyguard
+ // showing
.distinctUntilChanged()
.onEach { trackingUnseen -> logger.logTrackingUnseen(trackingUnseen) }
- // Separately track seen notifications while the device is locked, applying once the device
- // is unlocked.
- val notificationsSeenWhileLocked = mutableSetOf<NotificationEntry>()
-
- // Use [collectLatest] to cancel any running jobs when [trackingUnseen] changes.
- isKeyguardPresent.collectLatest { isKeyguardPresent: Boolean ->
- if (isKeyguardPresent) {
- // Keyguard is not gone, notifications need to be visible for a certain threshold
- // before being marked as seen
- trackSeenNotificationsWhileLocked(notificationsSeenWhileLocked)
+ // Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is
+ // showing again
+ var clearUnseenOnBeginTracking = false
+ isTrackingUnseen.collectLatest { trackingUnseen ->
+ if (!trackingUnseen) {
+ // Wait for the user to spend enough time on the lock screen before clearing unseen
+ // set when unlocked
+ awaitTimeSpentNotDozing(SEEN_TIMEOUT)
+ clearUnseenOnBeginTracking = true
+ logger.logSeenOnLockscreen()
} else {
- // Mark all seen-while-locked notifications as seen for real.
- if (notificationsSeenWhileLocked.isNotEmpty()) {
- unseenNotifications.removeAll(notificationsSeenWhileLocked)
- logger.logAllMarkedSeenOnUnlock(
- seenCount = notificationsSeenWhileLocked.size,
- remainingUnseenCount = unseenNotifications.size
- )
- notificationsSeenWhileLocked.clear()
+ if (clearUnseenOnBeginTracking) {
+ clearUnseenOnBeginTracking = false
+ logger.logAllMarkedSeenOnUnlock()
+ unseenNotifications.clear()
}
unseenNotifFilter.invalidateList("keyguard no longer showing")
- // Keyguard is gone, notifications can be immediately marked as seen when they
- // become visible.
- trackSeenNotificationsWhileUnlocked()
+ trackUnseenNotifications()
}
}
}
- /**
- * Keep [notificationsSeenWhileLocked] updated to represent which notifications have actually
- * been "seen" while the device is on the keyguard.
- */
- private suspend fun trackSeenNotificationsWhileLocked(
- notificationsSeenWhileLocked: MutableSet<NotificationEntry>,
- ) = coroutineScope {
- // Remove removed notifications from the set
- launch {
- unseenEntryRemoved.collect { entry ->
- if (notificationsSeenWhileLocked.remove(entry)) {
- logger.logRemoveSeenOnLockscreen(entry)
+ private suspend fun awaitTimeSpentNotDozing(duration: Duration) {
+ keyguardRepository.isDozing
+ // Use transformLatest so that the timeout delay is cancelled if the device enters doze,
+ // and is restarted when doze ends.
+ .transformLatest { isDozing ->
+ if (!isDozing) {
+ delay(duration)
+ // Signal timeout has completed
+ emit(Unit)
}
}
- }
- // Use collectLatest so that the timeout delay is cancelled if the device enters doze, and
- // is restarted when doze ends.
- keyguardRepository.isDozing.collectLatest { isDozing ->
- if (!isDozing) {
- trackSeenNotificationsWhileLockedAndNotDozing(notificationsSeenWhileLocked)
- }
- }
+ // Suspend until the first emission
+ .first()
}
- /**
- * Keep [notificationsSeenWhileLocked] updated to represent which notifications have actually
- * been "seen" while the device is on the keyguard and not dozing. Any new and existing unseen
- * notifications are not marked as seen until they are visible for the [SEEN_TIMEOUT] duration.
- */
- private suspend fun trackSeenNotificationsWhileLockedAndNotDozing(
- notificationsSeenWhileLocked: MutableSet<NotificationEntry>
- ) = coroutineScope {
- // All child tracking jobs will be cancelled automatically when this is cancelled.
- val trackingJobsByEntry = mutableMapOf<NotificationEntry, Job>()
-
- /**
- * Wait for the user to spend enough time on the lock screen before removing notification
- * from unseen set upon unlock.
- */
- suspend fun trackSeenDurationThreshold(entry: NotificationEntry) {
- if (notificationsSeenWhileLocked.remove(entry)) {
- logger.logResetSeenOnLockscreen(entry)
- }
- delay(SEEN_TIMEOUT)
- notificationsSeenWhileLocked.add(entry)
- trackingJobsByEntry.remove(entry)
- logger.logSeenOnLockscreen(entry)
- }
-
- /** Stop any unseen tracking when a notification is removed. */
- suspend fun stopTrackingRemovedNotifs(): Nothing =
- unseenEntryRemoved.collect { entry ->
- trackingJobsByEntry.remove(entry)?.let {
- it.cancel()
- logger.logStopTrackingLockscreenSeenDuration(entry)
- }
- }
-
- /** Start tracking new notifications when they are posted. */
- suspend fun trackNewUnseenNotifs(): Nothing = coroutineScope {
- unseenEntryAdded.collect { entry ->
- logger.logTrackingLockscreenSeenDuration(entry)
- // If this is an update, reset the tracking.
- trackingJobsByEntry[entry]?.let {
- it.cancel()
- logger.logResetSeenOnLockscreen(entry)
- }
- trackingJobsByEntry[entry] = launch { trackSeenDurationThreshold(entry) }
- }
- }
-
- // Start tracking for all notifications that are currently unseen.
- logger.logTrackingLockscreenSeenDuration(unseenNotifications)
- unseenNotifications.forEach { entry ->
- trackingJobsByEntry[entry] = launch { trackSeenDurationThreshold(entry) }
- }
-
- launch { trackNewUnseenNotifs() }
- launch { stopTrackingRemovedNotifs() }
- }
-
- // Track "seen" notifications, marking them as such when either shade is expanded or the
+ // Track "unseen" notifications, marking them as seen when either shade is expanded or the
// notification becomes heads up.
- private suspend fun trackSeenNotificationsWhileUnlocked() {
+ private suspend fun trackUnseenNotifications() {
coroutineScope {
launch { clearUnseenNotificationsWhenShadeIsExpanded() }
launch { markHeadsUpNotificationsAsSeen() }
@@ -299,7 +250,6 @@
) {
logger.logUnseenAdded(entry.key)
unseenNotifications.add(entry)
- unseenEntryAdded.tryEmit(entry)
}
}
@@ -309,14 +259,12 @@
) {
logger.logUnseenUpdated(entry.key)
unseenNotifications.add(entry)
- unseenEntryAdded.tryEmit(entry)
}
}
override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
if (unseenNotifications.remove(entry)) {
logger.logUnseenRemoved(entry.key)
- unseenEntryRemoved.tryEmit(entry)
}
}
}
@@ -399,3 +347,6 @@
private val SEEN_TIMEOUT = 5.seconds
}
}
+
+private val TransitionStep.isScreenTurningOff: Boolean
+ get() = transitionState == TransitionState.STARTED && to != KeyguardState.GONE
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt
index c612816..1f8ec34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt
@@ -19,7 +19,6 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.UnseenNotificationLog
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
import javax.inject.Inject
private const val TAG = "KeyguardCoordinator"
@@ -29,14 +28,11 @@
constructor(
@UnseenNotificationLog private val buffer: LogBuffer,
) {
- fun logSeenOnLockscreen(entry: NotificationEntry) =
+ fun logSeenOnLockscreen() =
buffer.log(
TAG,
LogLevel.DEBUG,
- messageInitializer = { str1 = entry.key },
- messagePrinter = {
- "Notification [$str1] on lockscreen will be marked as seen when unlocked."
- },
+ "Notifications on lockscreen will be marked as seen when unlocked."
)
fun logTrackingUnseen(trackingUnseen: Boolean) =
@@ -47,21 +43,11 @@
messagePrinter = { "${if (bool1) "Start" else "Stop"} tracking unseen notifications." },
)
- fun logAllMarkedSeenOnUnlock(
- seenCount: Int,
- remainingUnseenCount: Int,
- ) =
+ fun logAllMarkedSeenOnUnlock() =
buffer.log(
TAG,
LogLevel.DEBUG,
- messageInitializer = {
- int1 = seenCount
- int2 = remainingUnseenCount
- },
- messagePrinter = {
- "$int1 Notifications have been marked as seen now that device is unlocked. " +
- "$int2 notifications remain unseen."
- },
+ "Notifications have been marked as seen now that device is unlocked."
)
fun logShadeExpanded() =
@@ -110,60 +96,4 @@
messageInitializer = { str1 = key },
messagePrinter = { "Unseen notif has become heads up: $str1" },
)
-
- fun logTrackingLockscreenSeenDuration(unseenNotifications: Set<NotificationEntry>) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- messageInitializer = {
- str1 = unseenNotifications.joinToString { it.key }
- int1 = unseenNotifications.size
- },
- messagePrinter = {
- "Tracking $int1 unseen notifications for lockscreen seen duration threshold: $str1"
- },
- )
- }
-
- fun logTrackingLockscreenSeenDuration(entry: NotificationEntry) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- messageInitializer = { str1 = entry.key },
- messagePrinter = {
- "Tracking new notification for lockscreen seen duration threshold: $str1"
- },
- )
- }
-
- fun logStopTrackingLockscreenSeenDuration(entry: NotificationEntry) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- messageInitializer = { str1 = entry.key },
- messagePrinter = {
- "Stop tracking removed notification for lockscreen seen duration threshold: $str1"
- },
- )
- }
-
- fun logResetSeenOnLockscreen(entry: NotificationEntry) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- messageInitializer = { str1 = entry.key },
- messagePrinter = {
- "Reset tracking updated notification for lockscreen seen duration threshold: $str1"
- },
- )
- }
-
- fun logRemoveSeenOnLockscreen(entry: NotificationEntry) {
- buffer.log(
- TAG,
- LogLevel.DEBUG,
- messageInitializer = { str1 = entry.key },
- messagePrinter = { "Notification marked as seen on lockscreen removed: $str1" },
- )
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index bdb206b..38c3723 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -71,7 +71,6 @@
private NotificationPresenter mPresenter;
private NotificationListContainer mListContainer;
- private BindRowCallback mBindRowCallback;
private NotificationClicker mNotificationClicker;
private FeatureFlags mFeatureFlags;
@@ -103,11 +102,9 @@
* Sets up late-bound dependencies for this component.
*/
public void setUpWithPresenter(NotificationPresenter presenter,
- NotificationListContainer listContainer,
- BindRowCallback bindRowCallback) {
+ NotificationListContainer listContainer) {
mPresenter = presenter;
mListContainer = listContainer;
- mBindRowCallback = bindRowCallback;
mIconManager.attach();
}
@@ -179,7 +176,7 @@
mNotificationRemoteInputManager.bindRow(row);
entry.setRow(row);
mNotifBindPipeline.manageRow(entry, row);
- mBindRowCallback.onBindRow(row);
+ mPresenter.onBindRow(row);
row.setInlineReplyAnimationFlagEnabled(
mFeatureFlags.isEnabled(NOTIFICATION_INLINE_REPLY_ANIMATION));
}
@@ -235,12 +232,4 @@
}
});
}
-
- /** Callback for when a row is bound to an entry. */
- public interface BindRowCallback {
- /**
- * Called when a new row is created and bound to a notification.
- */
- void onBindRow(ExpandableNotificationRow row);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
index a5278c3d..c9ebed1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
@@ -20,7 +20,6 @@
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.NotificationActivityStarter
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -38,7 +37,6 @@
listContainer: NotificationListContainer,
stackController: NotifStackController,
notificationActivityStarter: NotificationActivityStarter,
- bindRowCallback: NotificationRowBinderImpl.BindRowCallback
)
fun resetUserExpandedStates()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 0ed4175..6409635 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -83,7 +83,6 @@
listContainer: NotificationListContainer,
stackController: NotifStackController,
notificationActivityStarter: NotificationActivityStarter,
- bindRowCallback: NotificationRowBinderImpl.BindRowCallback
) {
notificationListener.registerAsSystemService()
@@ -97,10 +96,7 @@
clickerBuilder.build(
Optional.ofNullable(centralSurfaces), bubblesOptional,
notificationActivityStarter))
- notificationRowBinder.setUpWithPresenter(
- presenter,
- listContainer,
- bindRowCallback)
+ notificationRowBinder.setUpWithPresenter(presenter, listContainer)
headsUpViewBinder.setPresenter(presenter)
notifBindPipelineInitializer.initialize()
animatedImageNotificationManager.bind()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
index 14856da..302a1f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
@@ -21,7 +21,6 @@
import com.android.systemui.statusbar.NotificationListener
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.NotificationActivityStarter
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -40,7 +39,6 @@
listContainer: NotificationListContainer,
stackController: NotifStackController,
notificationActivityStarter: NotificationActivityStarter,
- bindRowCallback: NotificationRowBinderImpl.BindRowCallback
) {
// Always connect the listener even if notification-handling is disabled. Being a listener
// grants special permissions and it's not clear if other things will break if we lose those
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
index 014406f..69484b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
@@ -17,19 +17,19 @@
package com.android.systemui.statusbar.notification.shelf.domain.interactor
import android.os.PowerManager
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.phone.CentralSurfaces
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
/** Interactor for the [NotificationShelf] */
-@CentralSurfacesComponent.CentralSurfacesScope
+@SysUISingleton
class NotificationShelfInteractor
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 12956ab..2520738 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -19,6 +19,7 @@
import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -32,7 +33,6 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.phone.NotificationIconContainer
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import javax.inject.Inject
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.launch
@@ -43,7 +43,7 @@
* [NotificationShelfController] interface. Once the [LegacyNotificationShelfControllerImpl] is
* removed, this class can go away and the ViewBinder can be used directly.
*/
-@CentralSurfacesScope
+@SysUISingleton
class NotificationShelfViewBinderWrapperControllerImpl @Inject constructor() :
NotificationShelfController {
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 36025e8..0b5e436 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
@@ -3992,7 +3992,7 @@
mNotificationsController.resetUserExpandedStates();
clearTemporaryViews();
clearUserLockedViews();
- cancelActiveSwipe();
+ resetAllSwipeState();
}
}
@@ -4058,7 +4058,7 @@
mGroupExpansionManager.collapseGroups();
mExpandHelper.cancelImmediately();
if (!mIsExpansionChanging) {
- cancelActiveSwipe();
+ resetAllSwipeState();
}
finalizeClearAllAnimation();
}
@@ -4387,7 +4387,7 @@
boolean nowHiddenAtAll = mAmbientState.isHiddenAtAll();
if (nowFullyHidden != wasFullyHidden) {
updateVisibility();
- mSwipeHelper.resetTouchState();
+ resetAllSwipeState();
}
if (!wasHiddenAtAll && nowHiddenAtAll) {
resetExposedMenuView(true /* animate */, true /* animate */);
@@ -5847,9 +5847,14 @@
}
}
- private void cancelActiveSwipe() {
+ private void resetAllSwipeState() {
+ Trace.beginSection("NSSL.resetAllSwipeState()");
mSwipeHelper.resetTouchState();
+ for (int i = 0; i < getChildCount(); i++) {
+ mSwipeHelper.forceResetSwipeState(getChildAt(i));
+ }
updateContinuousShadowDrawing();
+ Trace.endSection();
}
void updateContinuousShadowDrawing() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index a70862ae..aef623a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -56,11 +56,13 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.ExpandHelper;
import com.android.systemui.Gefingerpoken;
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.media.controls.ui.KeyguardMediaController;
import com.android.systemui.plugins.ActivityStarter;
@@ -175,6 +177,7 @@
private final SysuiStatusBarStateController mStatusBarStateController;
private final KeyguardBypassController mKeyguardBypassController;
private final KeyguardInteractor mKeyguardInteractor;
+ private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
private final NotificationLockscreenUserManager mLockscreenUserManager;
// TODO: CentralSurfaces should be encapsulated behind a Controller
private final CentralSurfaces mCentralSurfaces;
@@ -195,6 +198,7 @@
@Nullable
private Boolean mHistoryEnabled;
private int mBarState;
+ private boolean mIsBouncerShowingFromCentralSurfaces;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final FeatureFlags mFeatureFlags;
private final NotificationTargetsHelper mNotificationTargetsHelper;
@@ -631,6 +635,7 @@
KeyguardMediaController keyguardMediaController,
KeyguardBypassController keyguardBypassController,
KeyguardInteractor keyguardInteractor,
+ PrimaryBouncerInteractor primaryBouncerInteractor,
ZenModeController zenModeController,
NotificationLockscreenUserManager lockscreenUserManager,
Optional<NotificationListViewModel> nsslViewModel,
@@ -680,6 +685,7 @@
mKeyguardMediaController = keyguardMediaController;
mKeyguardBypassController = keyguardBypassController;
mKeyguardInteractor = keyguardInteractor;
+ mPrimaryBouncerInteractor = primaryBouncerInteractor;
mZenModeController = zenModeController;
mLockscreenUserManager = lockscreenUserManager;
mViewModel = nsslViewModel;
@@ -1206,6 +1212,14 @@
}
/**
+ * Sets whether the bouncer is currently showing. Should only be called from
+ * {@link CentralSurfaces}.
+ */
+ public void setBouncerShowingFromCentralSurfaces(boolean bouncerShowing) {
+ mIsBouncerShowingFromCentralSurfaces = bouncerShowing;
+ }
+
+ /**
* Set the visibility of the view, and propagate it to specific children.
*
* @param visible either the view is visible or not.
@@ -1236,7 +1250,8 @@
// That avoids "No Notifications" to blink when transitioning to AOD.
// For more details, see: b/228790482
&& !isInTransitionToKeyguard()
- && !mCentralSurfaces.isBouncerShowing();
+ // Don't show any notification content if the bouncer is showing. See b/267060171.
+ && !isBouncerShowing();
mView.updateEmptyShadeView(shouldShow, mZenModeController.areNotificationsHiddenInShade());
@@ -1244,6 +1259,24 @@
}
/**
+ * Returns whether the bouncer is currently showing.
+ *
+ * There's a possible timing difference between when CentralSurfaces marks the bouncer as not
+ * showing and when PrimaryBouncerInteractor marks the bouncer as not showing. (CentralSurfaces
+ * appears to mark the bouncer as showing for 10-200ms longer than PrimaryBouncerInteractor.)
+ *
+ * This timing difference could be load bearing, which is why we have a feature flag protecting
+ * where we fetch the value from. This flag is intended to be short-lived.
+ */
+ private boolean isBouncerShowing() {
+ if (mFeatureFlags.isEnabled(Flags.USE_REPOS_FOR_BOUNCER_SHOWING)) {
+ return mPrimaryBouncerInteractor.isBouncerShowing();
+ } else {
+ return mIsBouncerShowingFromCentralSurfaces;
+ }
+ }
+
+ /**
* Update the importantForAccessibility of NotificationStackScrollLayout.
* <p>
* We want the NSSL to be unimportant for accessibility when there's no
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 ce2658d..fc213b6 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
@@ -309,7 +309,8 @@
float newNotificationEnd = newYTranslation + newHeight;
boolean isHeadsUp = (child instanceof ExpandableNotificationRow) && child.isPinned();
if (mClipNotificationScrollToTop
- && ((isHeadsUp && !firstHeadsUp) || child.isHeadsUpAnimatingAway())
+ && !firstHeadsUp
+ && (isHeadsUp || child.isHeadsUpAnimatingAway())
&& newNotificationEnd > firstHeadsUpEnd
&& !ambientState.isShadeExpanded()) {
// The bottom of this view is peeking out from under the previous view.
@@ -613,13 +614,12 @@
updateViewWithShelf(view, viewState, shelfStart);
}
}
- // Avoid pulsing notification flicker during AOD to LS
- // A pulsing notification is already expanded, no need to expand it again with animation
- if (ambientState.isPulsingRow(view)) {
- expansionFraction = 1.0f;
+ viewState.height = getMaxAllowedChildHeight(view);
+ if (!view.isPinned() && !view.isHeadsUpAnimatingAway()
+ && !ambientState.isPulsingRow(view)) {
+ // The expansion fraction should not affect HUNs or pulsing notifications.
+ viewState.height *= expansionFraction;
}
- // Clip height of view right before shelf.
- viewState.height = (int) (getMaxAllowedChildHeight(view) * expansionFraction);
}
algorithmState.mCurrentYPosition +=
@@ -779,6 +779,8 @@
}
}
if (row.isPinned()) {
+ // Make sure row yTranslation is at maximum the HUN yTranslation,
+ // which accounts for AmbientState.stackTopMargin in split-shade.
childState.setYTranslation(
Math.max(childState.getYTranslation(), headsUpTranslation));
childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
@@ -803,7 +805,11 @@
}
}
if (row.isHeadsUpAnimatingAway()) {
- childState.setYTranslation(Math.max(childState.getYTranslation(), mHeadsUpInset));
+ // Make sure row yTranslation is at maximum the HUN yTranslation,
+ // which accounts for AmbientState.stackTopMargin in split-shade.
+ childState.setYTranslation(
+ Math.max(childState.getYTranslation(), headsUpTranslation));
+ // keep it visible for the animation
childState.hidden = false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 81048d6..1396d8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -173,6 +173,7 @@
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.recents.ScreenPinningRequest;
@@ -634,6 +635,7 @@
private final SysuiColorExtractor mColorExtractor;
private final ScreenLifecycle mScreenLifecycle;
private final WakefulnessLifecycle mWakefulnessLifecycle;
+ protected final PowerInteractor mPowerInteractor;
private boolean mNoAnimationOnNextBarModeChange;
private final SysuiStatusBarStateController mStatusBarStateController;
@@ -648,7 +650,9 @@
private final Optional<StartingSurface> mStartingSurfaceOptional;
private final ActivityIntentHelper mActivityIntentHelper;
- private NotificationStackScrollLayoutController mStackScrollerController;
+
+ @VisibleForTesting
+ protected NotificationStackScrollLayoutController mStackScrollerController;
private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
(extractor, which) -> updateTheme();
@@ -735,6 +739,7 @@
SysuiColorExtractor colorExtractor,
ScreenLifecycle screenLifecycle,
WakefulnessLifecycle wakefulnessLifecycle,
+ PowerInteractor powerInteractor,
SysuiStatusBarStateController statusBarStateController,
Optional<Bubbles> bubblesOptional,
Lazy<NoteTaskController> noteTaskControllerLazy,
@@ -744,6 +749,7 @@
Lazy<AssistManager> assistManagerLazy,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
+ NotificationShelfController notificationShelfController,
DozeParameters dozeParameters,
ScrimController scrimController,
Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
@@ -831,6 +837,7 @@
mColorExtractor = colorExtractor;
mScreenLifecycle = screenLifecycle;
mWakefulnessLifecycle = wakefulnessLifecycle;
+ mPowerInteractor = powerInteractor;
mStatusBarStateController = statusBarStateController;
mBubblesOptional = bubblesOptional;
mNoteTaskControllerLazy = noteTaskControllerLazy;
@@ -840,6 +847,7 @@
mAssistManagerLazy = assistManagerLazy;
mConfigurationController = configurationController;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mNotificationShelfController = notificationShelfController;
mDozeServiceHost = dozeServiceHost;
mPowerManager = powerManager;
mDozeParameters = dozeParameters;
@@ -1571,8 +1579,7 @@
mPresenter,
mNotifListContainer,
mStackScrollerController.getNotifStackController(),
- mNotificationActivityStarter,
- mCentralSurfacesComponent.getBindRowCallback());
+ mNotificationActivityStarter);
}
/**
@@ -1651,7 +1658,6 @@
mNotifListContainer = mCentralSurfacesComponent.getNotificationListContainer();
mPresenter = mCentralSurfacesComponent.getNotificationPresenter();
mNotificationActivityStarter = mCentralSurfacesComponent.getNotificationActivityStarter();
- mNotificationShelfController = mCentralSurfacesComponent.getNotificationShelfController();
mHeadsUpManager.addListener(mCentralSurfacesComponent.getStatusBarHeadsUpChangeListener());
@@ -2922,6 +2928,7 @@
mBouncerShowing = bouncerShowing;
mKeyguardBypassController.setBouncerShowing(bouncerShowing);
mPulseExpansionHandler.setBouncerShowing(bouncerShowing);
+ mStackScrollerController.setBouncerShowingFromCentralSurfaces(bouncerShowing);
setBouncerShowingForStatusBarComponents(bouncerShowing);
mStatusBarHideIconsForBouncerManager.setBouncerShowingAndTriggerUpdate(bouncerShowing);
mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
@@ -3408,7 +3415,7 @@
protected Display mDisplay;
private int mDisplayId;
- protected NotificationShelfController mNotificationShelfController;
+ private final NotificationShelfController mNotificationShelfController;
private final Lazy<AssistManager> mAssistManagerLazy;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 561bd91..fc3c85a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -229,12 +229,18 @@
}
}
- public float getLockscreenMinStackScrollerPadding() {
+ /**
+ * @param nsslTop NotificationStackScrollLayout top, which is below top of the srceen.
+ * @return Distance from nsslTop to top of the first view in the lockscreen shade.
+ */
+ public float getLockscreenNotifPadding(float nsslTop) {
if (mBypassEnabled) {
- return mUnlockedStackScrollerPadding;
+ return mUnlockedStackScrollerPadding - nsslTop;
} else if (mIsSplitShade) {
- return mSplitShadeTargetTopMargin + mUserSwitchHeight;
+ return mSplitShadeTargetTopMargin + mUserSwitchHeight - nsslTop;
} else {
+ // Non-bypass portrait shade already uses values from nsslTop
+ // so we don't need to subtract it here.
return mMinTopMargin + mKeyguardStatusHeight;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 5624e28..3b56d27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -52,7 +52,6 @@
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
@@ -68,9 +67,7 @@
import javax.inject.Inject;
@CentralSurfacesComponent.CentralSurfacesScope
-class StatusBarNotificationPresenter implements NotificationPresenter,
- NotificationRowBinderImpl.BindRowCallback,
- CommandQueue.Callbacks {
+class StatusBarNotificationPresenter implements NotificationPresenter, CommandQueue.Callbacks {
private static final String TAG = "StatusBarNotificationPresenter";
private final ActivityStarter mActivityStarter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java
index 26c483c..d94e434 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.phone;
import com.android.systemui.statusbar.NotificationPresenter;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import dagger.Binds;
import dagger.Module;
@@ -26,8 +25,4 @@
public abstract class StatusBarNotificationPresenterModule {
@Binds
abstract NotificationPresenter bindPresenter(StatusBarNotificationPresenter impl);
-
- @Binds
- abstract NotificationRowBinderImpl.BindRowCallback bindBindRowCallback(
- StatusBarNotificationPresenter impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
index 158f961..64c798b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
@@ -27,9 +27,7 @@
import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeHeaderController;
import com.android.systemui.statusbar.NotificationPresenter;
-import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutListContainerModule;
@@ -90,9 +88,6 @@
NotificationShadeWindowView getNotificationShadeWindowView();
/** */
- NotificationShelfController getNotificationShelfController();
-
- /** */
NotificationStackScrollLayoutController getNotificationStackScrollLayoutController();
/**
@@ -134,7 +129,5 @@
NotificationPresenter getNotificationPresenter();
- NotificationRowBinderImpl.BindRowCallback getBindRowCallback();
-
NotificationListContainer getNotificationListContainer();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 4ccbc5a..260d986 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -23,24 +23,15 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelView;
import com.android.systemui.shade.NotificationPanelViewController;
-import com.android.systemui.shade.NotificationShadeWindowView;
-import com.android.systemui.shade.NotificationsQuickSettingsContainer;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl;
-import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
-import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule;
-import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModelModule;
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
@@ -68,7 +59,6 @@
import java.util.concurrent.Executor;
import javax.inject.Named;
-import javax.inject.Provider;
@Module(subcomponents = StatusBarFragmentComponent.class,
includes = {
@@ -80,56 +70,11 @@
public static final String STATUS_BAR_FRAGMENT = "status_bar_fragment";
/** */
- @Provides
- @CentralSurfacesComponent.CentralSurfacesScope
- public static NotificationShelf providesNotificationShelf(LayoutInflater layoutInflater,
- NotificationStackScrollLayout notificationStackScrollLayout) {
- NotificationShelf view = (NotificationShelf) layoutInflater.inflate(
- R.layout.status_bar_notification_shelf, notificationStackScrollLayout, false);
-
- if (view == null) {
- throw new IllegalStateException(
- "R.layout.status_bar_notification_shelf could not be properly inflated");
- }
- return view;
- }
-
- /** */
- @Provides
- @CentralSurfacesComponent.CentralSurfacesScope
- public static NotificationShelfController providesStatusBarWindowView(
- FeatureFlags featureFlags,
- Provider<NotificationShelfViewBinderWrapperControllerImpl> newImpl,
- NotificationShelfComponent.Builder notificationShelfComponentBuilder,
- NotificationShelf notificationShelf) {
- if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
- return newImpl.get();
- } else {
- NotificationShelfComponent component = notificationShelfComponentBuilder
- .notificationShelf(notificationShelf)
- .build();
- LegacyNotificationShelfControllerImpl notificationShelfController =
- component.getNotificationShelfController();
- notificationShelfController.init();
-
- return notificationShelfController;
- }
- }
-
- /** */
@Binds
@CentralSurfacesComponent.CentralSurfacesScope
abstract ShadeViewController bindsShadeViewController(
NotificationPanelViewController notificationPanelViewController);
- /** */
- @Provides
- @CentralSurfacesComponent.CentralSurfacesScope
- public static NotificationsQuickSettingsContainer getNotificationsQuickSettingsContainer(
- NotificationShadeWindowView notificationShadeWindowView) {
- return notificationShadeWindowView.findViewById(R.id.notification_container_parent);
- }
-
@Binds
@IntoSet
abstract StatusBarBoundsProvider.BoundsChangeListener sysBarAttrsListenerAsBoundsListener(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index f37a9b5..7456d34 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -928,7 +928,6 @@
showRingerDrawer();
}
});
- updateSelectedRingerContainerDescription(mIsRingerDrawerOpen);
mRingerDrawerVibrate.setOnClickListener(
new RingerDrawerItemClickListener(RINGER_MODE_VIBRATE));
@@ -991,19 +990,6 @@
: 0;
}
- @VisibleForTesting String getSelectedRingerContainerDescription() {
- return mSelectedRingerContainer == null ? null :
- mSelectedRingerContainer.getContentDescription().toString();
- }
-
- @VisibleForTesting void toggleRingerDrawer(boolean show) {
- if (show) {
- showRingerDrawer();
- } else {
- hideRingerDrawer();
- }
- }
-
/** Animates in the ringer drawer. */
private void showRingerDrawer() {
if (mIsRingerDrawerOpen) {
@@ -1081,7 +1067,12 @@
.start();
}
- updateSelectedRingerContainerDescription(true);
+ // When the ringer drawer is open, tapping the currently selected ringer will set the ringer
+ // to the current ringer mode. Change the content description to that, instead of the 'tap
+ // to change ringer mode' default.
+ mSelectedRingerContainer.setContentDescription(
+ mContext.getString(getStringDescriptionResourceForRingerMode(
+ mState.ringerModeInternal)));
mIsRingerDrawerOpen = true;
}
@@ -1127,38 +1118,14 @@
.translationY(0f)
.start();
- updateSelectedRingerContainerDescription(false);
+ // When the drawer is closed, tapping the selected ringer drawer will open it, allowing the
+ // user to change the ringer.
+ mSelectedRingerContainer.setContentDescription(
+ mContext.getString(R.string.volume_ringer_change));
mIsRingerDrawerOpen = false;
}
-
- /**
- * @param open false to set the description when drawer is closed
- */
- private void updateSelectedRingerContainerDescription(boolean open) {
- if (mState == null || mSelectedRingerContainer == null) return;
-
- String currentMode = mContext.getString(getStringDescriptionResourceForRingerMode(
- mState.ringerModeInternal));
- String tapToSelect;
-
- if (open) {
- // When the ringer drawer is open, tapping the currently selected ringer will set the
- // ringer to the current ringer mode. Change the content description to that, instead of
- // the 'tap to change ringer mode' default.
- tapToSelect = "";
-
- } else {
- // When the drawer is closed, tapping the selected ringer drawer will open it, allowing
- // the user to change the ringer. The user needs to know that, and also the current mode
- currentMode += ", ";
- tapToSelect = mContext.getString(R.string.volume_ringer_change);
- }
-
- mSelectedRingerContainer.setContentDescription(currentMode + tapToSelect);
- }
-
private void initSettingsH(int lockTaskModeState) {
if (mSettingsView != null) {
mSettingsView.setVisibility(
@@ -1734,7 +1701,7 @@
});
}
- @VisibleForTesting int getStringDescriptionResourceForRingerMode(int mode) {
+ private int getStringDescriptionResourceForRingerMode(int mode) {
switch (mode) {
case RINGER_MODE_SILENT:
return R.string.volume_ringer_status_silent;
@@ -1816,7 +1783,6 @@
updateVolumeRowH(row);
}
updateRingerH();
- updateSelectedRingerContainerDescription(mIsRingerDrawerOpen);
mWindow.setTitle(composeWindowTitle());
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 617b893..0dcd404 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -47,6 +47,7 @@
import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.yield
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -121,7 +122,7 @@
bouncerRepository = bouncerRepository,
configurationRepository = FakeConfigurationRepository(),
),
- KeyguardTransitionInteractor(repository = transitionRepository),
+ KeyguardTransitionInteractor(transitionRepository, TestScope().backgroundScope),
broadcastDispatcher,
batteryController,
keyguardUpdateMonitor,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index f59fd99..e561f1f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -587,21 +587,6 @@
}
@Test
- public void testSecurityCallbackFinish() {
- when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
- when(mKeyguardUpdateMonitor.isUserUnlocked(0)).thenReturn(true);
- mKeyguardSecurityContainerController.finish(true, 0);
- verify(mViewMediatorCallback).keyguardDone(anyBoolean(), anyInt());
- }
-
- @Test
- public void testSecurityCallbackFinish_cannotDismissLockScreenAndNotStrongAuth() {
- when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
- mKeyguardSecurityContainerController.finish(false, 0);
- verify(mViewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt());
- }
-
- @Test
public void testOnStartingToHide() {
mKeyguardSecurityContainerController.onStartingToHide();
verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index bdf6bee..c88c4d6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -158,7 +158,8 @@
mVibrator,
mAuthRippleController,
mResources,
- new KeyguardTransitionInteractor(mTransitionRepository),
+ new KeyguardTransitionInteractor(mTransitionRepository,
+ TestScopeProvider.getTestScope().getBackgroundScope()),
KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(),
mFeatureFlags
);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TestScopeProvider.kt b/packages/SystemUI/tests/src/com/android/keyguard/TestScopeProvider.kt
new file mode 100644
index 0000000..073c7fe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/TestScopeProvider.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 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.keyguard
+
+import kotlinx.coroutines.test.TestScope
+
+class TestScopeProvider {
+ companion object {
+ @JvmStatic fun getTestScope() = TestScope()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
index b3f9958..275723b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
@@ -20,7 +20,6 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
-import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
import static com.google.common.truth.Truth.assertThat;
@@ -29,6 +28,7 @@
import static junit.framework.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -525,13 +525,13 @@
private void setupMagnificationCapabilityAndMode(int capability, int mode) {
when(mSecureSettings.getIntForUser(
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
- ACCESSIBILITY_MAGNIFICATION_MODE_NONE,
- UserHandle.USER_CURRENT)).thenReturn(capability);
+ eq(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY),
+ anyInt(),
+ eq(UserHandle.USER_CURRENT))).thenReturn(capability);
when(mSecureSettings.getIntForUser(
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
- ACCESSIBILITY_MAGNIFICATION_MODE_NONE,
- UserHandle.USER_CURRENT)).thenReturn(mode);
+ eq(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE),
+ anyInt(),
+ eq(UserHandle.USER_CURRENT))).thenReturn(mode);
}
private void setupScaleInSecureSettings(float scale) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
index 1f2b64d..263ce1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
@@ -56,6 +56,7 @@
foldProvider,
KeyguardTransitionInteractor(
keyguardTransitionRepository,
+ testScope.backgroundScope
),
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index 039682c..a00e545 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
@@ -11,7 +11,6 @@
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
import com.android.systemui.statusbar.BlurUtils
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -23,9 +22,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
-import org.mockito.Mockito.anyLong
import org.mockito.Mockito.atLeastOnce
-import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -92,16 +89,10 @@
}
@Test
- fun testWakeUpCallsExecutor() {
- val mockExecutor: DelayableExecutor = mock()
- val mockCallback: Runnable = mock()
+ fun testWakeUpSetsExitAnimationsRunning() {
+ controller.wakeUp()
- controller.wakeUp(
- doneCallback = mockCallback,
- executor = mockExecutor,
- )
-
- verify(mockExecutor).executeDelayed(eq(mockCallback), anyLong())
+ verify(stateController).setExitAnimationsRunning(true)
}
@Test
@@ -112,10 +103,7 @@
verify(mockStartAnimator, never()).cancel()
- controller.wakeUp(
- doneCallback = mock(),
- executor = mock(),
- )
+ controller.wakeUp()
// Verify that we cancelled the start animator in favor of the exit
// animator.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index c97eedb..d99f0da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -25,7 +25,6 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -500,19 +499,15 @@
true /*shouldShowComplication*/);
mMainExecutor.runAllReady();
- final Runnable callback = mock(Runnable.class);
- mService.onWakeUp(callback);
- mMainExecutor.runAllReady();
- verify(mDreamOverlayContainerViewController).wakeUp(callback, mMainExecutor);
+ mService.onWakeUp();
+ verify(mDreamOverlayContainerViewController).wakeUp();
verify(mDreamOverlayCallbackController).onWakeUp();
}
@Test
public void testWakeUpBeforeStartDoesNothing() {
- final Runnable callback = mock(Runnable.class);
- mService.onWakeUp(callback);
- mMainExecutor.runAllReady();
- verify(mDreamOverlayContainerViewController, never()).wakeUp(callback, mMainExecutor);
+ mService.onWakeUp();
+ verify(mDreamOverlayContainerViewController, never()).wakeUp();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index c4a0e7c..a77f476 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -79,6 +79,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.settings.UserTracker;
@@ -116,6 +117,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.flow.Flow;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -172,6 +176,9 @@
private @Mock AlarmManager mAlarmManager;
private FakeSystemClock mSystemClock;
+ private @Mock CoroutineDispatcher mDispatcher;
+ private @Mock DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel;
+
private FakeFeatureFlags mFeatureFlags;
private int mInitialUserId;
@@ -188,6 +195,8 @@
final ViewRootImpl testViewRoot = mock(ViewRootImpl.class);
when(testViewRoot.getView()).thenReturn(mock(View.class));
when(mStatusBarKeyguardViewManager.getViewRootImpl()).thenReturn(testViewRoot);
+ when(mDreamingToLockscreenTransitionViewModel.getDreamOverlayAlpha())
+ .thenReturn(mock(Flow.class));
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mViewMediator, mKeyguardBypassController,
@@ -209,6 +218,7 @@
}
@Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
public void onLockdown_showKeyguard_evenIfKeyguardIsNotEnabledExternally() {
// GIVEN keyguard is not enabled and isn't showing
mViewMediator.onSystemReady();
@@ -698,7 +708,9 @@
mFeatureFlags,
mSecureSettings,
mSystemSettings,
- mSystemClock);
+ mSystemClock,
+ mDispatcher,
+ () -> mDreamingToLockscreenTransitionViewModel);
mViewMediator.start();
mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index eb97022..b4bd473 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -67,7 +67,10 @@
resourceTrimmer =
ResourceTrimmer(
keyguardInteractor,
- KeyguardTransitionInteractor(keyguardTransitionRepository),
+ KeyguardTransitionInteractor(
+ keyguardTransitionRepository,
+ testScope.backgroundScope
+ ),
globalWindowManager,
testScope.backgroundScope,
testDispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 90b3a8f..92ec9a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -216,7 +216,7 @@
)
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
val keyguardTransitionInteractor =
- KeyguardTransitionInteractor(keyguardTransitionRepository)
+ KeyguardTransitionInteractor(keyguardTransitionRepository, testScope.backgroundScope)
return DeviceEntryFaceAuthRepositoryImpl(
mContext,
fmOverride,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index 41ccfe2..80700e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -85,7 +85,8 @@
bouncerRepository = FakeKeyguardBouncerRepository()
faceAuthRepository = FakeDeviceEntryFaceAuthRepository()
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
- keyguardTransitionInteractor = KeyguardTransitionInteractor(keyguardTransitionRepository)
+ keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(keyguardTransitionRepository, testScope.backgroundScope)
underTest =
SystemUIKeyguardFaceAuthInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index 5de24e4..f63be61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -292,7 +292,8 @@
scope = testScope.backgroundScope,
transitionInteractor =
KeyguardTransitionInteractor(
- repository = keyguardTransitionRepository,
+ keyguardTransitionRepository,
+ testScope.backgroundScope
),
repository = keyguardRepository,
logger = logger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index d66e420..fa4941c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -27,11 +27,14 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -46,11 +49,12 @@
private lateinit var underTest: KeyguardTransitionInteractor
private lateinit var repository: FakeKeyguardTransitionRepository
+ private val testScope = TestScope()
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- underTest = KeyguardTransitionInteractor(repository)
+ underTest = KeyguardTransitionInteractor(repository, testScope.backgroundScope)
}
@Test
@@ -108,17 +112,17 @@
}
@Test
- fun keyguardStateTests() = runTest {
+ fun finishedKeyguardStateTests() = testScope.runTest {
val finishedSteps by collectValues(underTest.finishedKeyguardState)
-
+ runCurrent()
val steps = mutableListOf<TransitionStep>()
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0f, STARTED))
+ steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 1f, FINISHED))
+ steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0f, STARTED))
+ steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0.9f, RUNNING))
+ steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 1f, FINISHED))
steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
steps.forEach {
@@ -126,7 +130,29 @@
runCurrent()
}
- assertThat(finishedSteps).isEqualTo(listOf(LOCKSCREEN, AOD))
+ assertThat(finishedSteps).isEqualTo(listOf(LOCKSCREEN, PRIMARY_BOUNCER, AOD))
+ }
+
+ @Test
+ fun startedKeyguardStateTests() = testScope.runTest {
+ val finishedSteps by collectValues(underTest.startedKeyguardState)
+ runCurrent()
+ val steps = mutableListOf<TransitionStep>()
+
+ steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0f, STARTED))
+ steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 1f, FINISHED))
+ steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0f, STARTED))
+ steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0.9f, RUNNING))
+ steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
+
+ steps.forEach {
+ repository.sendTransitionStep(it)
+ runCurrent()
+ }
+
+ assertThat(finishedSteps).isEqualTo(listOf(OFF, PRIMARY_BOUNCER, AOD, GONE))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 3042560..50075b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -108,7 +108,8 @@
keyguardInteractor = createKeyguardInteractor(),
shadeRepository = shadeRepository,
keyguardTransitionRepository = mockTransitionRepository,
- keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+ keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
)
fromLockscreenTransitionInteractor.start()
@@ -117,7 +118,8 @@
scope = testScope,
keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
- keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+ keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
)
fromDreamingTransitionInteractor.start()
@@ -126,7 +128,8 @@
scope = testScope,
keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
- keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+ keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
)
fromAodTransitionInteractor.start()
@@ -135,7 +138,8 @@
scope = testScope,
keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
- keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+ keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
)
fromGoneTransitionInteractor.start()
@@ -144,7 +148,8 @@
scope = testScope,
keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
- keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+ keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
)
fromDozingTransitionInteractor.start()
@@ -153,7 +158,8 @@
scope = testScope,
keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
- keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+ keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
)
fromOccludedTransitionInteractor.start()
@@ -162,7 +168,8 @@
scope = testScope,
keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
- keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+ keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
)
fromAlternateBouncerTransitionInteractor.start()
@@ -171,48 +178,14 @@
scope = testScope,
keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
- keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+ keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
keyguardSecurityModel = keyguardSecurityModel,
)
fromPrimaryBouncerTransitionInteractor.start()
}
@Test
- fun dreamingToLockscreen() =
- testScope.runTest {
- // GIVEN a device is dreaming
- keyguardRepository.setDreamingWithOverlay(true)
- keyguardRepository.setWakefulnessModel(startingToWake())
- runCurrent()
-
- // GIVEN a prior transition has run to DREAMING
- runTransition(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING)
-
- // WHEN doze is complete
- keyguardRepository.setDozeTransitionModel(
- DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
- )
- // AND dreaming has stopped
- keyguardRepository.setDreamingWithOverlay(false)
- advanceUntilIdle()
- // AND then occluded has stopped
- keyguardRepository.setKeyguardOccluded(false)
- advanceUntilIdle()
-
- val info =
- withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
- }
- // THEN a transition to BOUNCER should occur
- assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.DREAMING)
- assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.animator).isNotNull()
-
- coroutineContext.cancelChildren()
- }
-
- @Test
fun lockscreenToPrimaryBouncerViaBouncerShowingCall() =
testScope.runTest {
// GIVEN a device that has at least woken up
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index 4440946..08e99dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.statusbar.LightRevealScrim
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
@@ -45,7 +46,7 @@
private val fakeLightRevealScrimRepository = FakeLightRevealScrimRepository()
private val keyguardTransitionInteractor =
- KeyguardTransitionInteractor(fakeKeyguardTransitionRepository)
+ KeyguardTransitionInteractor(fakeKeyguardTransitionRepository, TestScope().backgroundScope)
private lateinit var underTest: LightRevealScrimInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
index cdd06ac..a341346 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
@@ -25,11 +25,12 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.util.mockito.mock
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -42,13 +43,12 @@
class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: DreamingToLockscreenTransitionViewModel
private lateinit var repository: FakeKeyguardTransitionRepository
- private lateinit var transitionAnimation: KeyguardTransitionAnimationFlow
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository)
- underTest = DreamingToLockscreenTransitionViewModel(interactor)
+ val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ underTest = DreamingToLockscreenTransitionViewModel(interactor, mock())
}
@Test
@@ -60,17 +60,15 @@
val job =
underTest.dreamOverlayTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
- // Should start running here...
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.3f))
repository.sendTransitionStep(step(0.5f))
repository.sendTransitionStep(step(0.6f))
- // ...up to here
repository.sendTransitionStep(step(0.8f))
repository.sendTransitionStep(step(1f))
- assertThat(values.size).isEqualTo(5)
+ assertThat(values.size).isEqualTo(7)
values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
job.cancel()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
index 40511a0..694539b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
@@ -29,6 +29,7 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -45,7 +46,7 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository)
+ val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
underTest = GoneToDreamingTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 9c9aadf..29886d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -212,6 +212,7 @@
transitionInteractor =
KeyguardTransitionInteractor(
repository = FakeKeyguardTransitionRepository(),
+ scope = testScope.backgroundScope
),
repository = repository,
logger = UiEventLoggerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index c98058d..ea17751 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -29,6 +29,7 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -45,7 +46,7 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository)
+ val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
underTest = LockscreenToDreamingTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index 031b7fb..bf56a98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -29,6 +29,7 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -45,7 +46,7 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository)
+ val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
underTest = LockscreenToOccludedTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index c7ff882..34da26e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -29,6 +29,7 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -45,7 +46,7 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository)
+ val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
underTest = OccludedToLockscreenTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index 4919a66..f88b71d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -33,6 +33,7 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -54,7 +55,7 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository)
+ val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
underTest =
PrimaryBouncerToGoneTransitionViewModel(
interactor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index 07f7c15..2aff90c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -63,6 +63,7 @@
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -146,7 +147,7 @@
debugLogger,
mediaFlags,
keyguardUpdateMonitor,
- KeyguardTransitionInteractor(repository = transitionRepository),
+ KeyguardTransitionInteractor(transitionRepository, TestScope().backgroundScope),
)
verify(configurationController).addCallback(capture(configListener))
verify(mediaDataManager).addListener(capture(listener))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 6d8c9b1..7df54d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -280,6 +280,50 @@
}
@Test
+ public void onBindViewHolder_bindConnectedRemoteDeviceWithOnGoingSession_verifyView() {
+ when(mMediaDevice1.hasOngoingSession()).thenReturn(true);
+ when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(
+ ImmutableList.of());
+ when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(true);
+ mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
+ .onCreateViewHolder(new LinearLayout(mContext), 0);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
+ assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_bindConnectedRemoteDeviceWithHostOnGoingSession_verifyView() {
+ when(mMediaDevice1.hasOngoingSession()).thenReturn(true);
+ when(mMediaDevice1.isHostForOngoingSession()).thenReturn(true);
+ when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(
+ ImmutableList.of());
+ when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(true);
+ mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
+ .onCreateViewHolder(new LinearLayout(mContext), 0);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
+ assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
public void onBindViewHolder_bindConnectedDeviceWithMutingExpectedDeviceExist_verifyView() {
when(mMediaOutputController.hasMutingExpectedDevice()).thenReturn(true);
when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
index 9dba9b5..f8971fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
@@ -136,7 +136,10 @@
@After
public void tearDown() {
- mMediaOutputBroadcastDialog.dismissDialog();
+ if (mMediaOutputBroadcastDialog.mAlertDialog != null){
+ mMediaOutputBroadcastDialog.mAlertDialog.dismiss();
+ }
+ mMediaOutputBroadcastDialog.dismiss();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
index 19f9960..9e54224 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
@@ -99,6 +99,7 @@
keyguardTransitionInteractor =
KeyguardTransitionInteractor(
repository = keyguardTransitionRepository,
+ scope = testScope.backgroundScope
),
falsingManager = falsingManager,
shadeController = shadeController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java
index ab321f1..ba3d392 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java
@@ -26,7 +26,7 @@
import android.os.RemoteException;
import android.view.Display;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
-import android.window.ScreenCapture.ScreenshotSync;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import androidx.test.runner.AndroidJUnit4;
@@ -58,7 +58,7 @@
@Mock private Optional<Bubbles> mBubblesOptional;
@Mock private Bubbles mBubbles;
@Mock private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;
- @Mock private ScreenshotSync mScreenshotSync;
+ @Mock private SynchronousScreenCaptureListener mScreenshotSync;
private AppClipsScreenshotHelperService mAppClipsScreenshotHelperService;
@@ -80,7 +80,7 @@
when(mBubblesOptional.isEmpty()).thenReturn(false);
when(mBubblesOptional.get()).thenReturn(mBubbles);
when(mBubbles.getScreenshotExcludingBubble(DEFAULT_DISPLAY)).thenReturn(mScreenshotSync);
- when(mScreenshotSync.get()).thenReturn(null);
+ when(mScreenshotSync.getBuffer()).thenReturn(null);
assertThat(getInterface().takeScreenshot(DEFAULT_DISPLAY)).isNull();
}
@@ -90,7 +90,7 @@
when(mBubblesOptional.isEmpty()).thenReturn(false);
when(mBubblesOptional.get()).thenReturn(mBubbles);
when(mBubbles.getScreenshotExcludingBubble(DEFAULT_DISPLAY)).thenReturn(mScreenshotSync);
- when(mScreenshotSync.get()).thenReturn(mScreenshotHardwareBuffer);
+ when(mScreenshotSync.getBuffer()).thenReturn(mScreenshotHardwareBuffer);
when(mScreenshotHardwareBuffer.getHardwareBuffer()).thenReturn(FAKE_HARDWARE_BUFFER);
when(mScreenshotHardwareBuffer.getColorSpace()).thenReturn(FAKE_COLOR_SPACE);
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 e969112..af40e5b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -111,8 +111,7 @@
@Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
@Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
@Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
- @Mock
- private lateinit var unfoldTransitionProgressProvider:
+ @Mock private lateinit var unfoldTransitionProgressProvider:
Optional<UnfoldTransitionProgressProvider>
@Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock
@@ -195,6 +194,7 @@
keyguardTransitionInteractor =
KeyguardTransitionInteractor(
repository = FakeKeyguardTransitionRepository(),
+ scope = testScope.backgroundScope
),
falsingManager = FalsingManagerFake(),
shadeController = shadeController,
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 9fcffee..d3ecc3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -207,6 +207,7 @@
keyguardTransitionInteractor =
KeyguardTransitionInteractor(
repository = FakeKeyguardTransitionRepository(),
+ scope = testScope.backgroundScope
),
falsingManager = FalsingManagerFake(),
shadeController = shadeController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
new file mode 100644
index 0000000..8bb8f62
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
@@ -0,0 +1,97 @@
+/*
+ * 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.shade
+
+import android.testing.AndroidTestingRunner
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.QSFragment
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class NotificationsQuickSettingsContainerTest : SysuiTestCase() {
+
+ @Mock private lateinit var qsFrame: View
+ @Mock private lateinit var stackScroller: View
+ @Mock private lateinit var keyguardStatusBar: View
+ @Mock private lateinit var qsFragment: QSFragment
+
+ private lateinit var qsView: ViewGroup
+ private lateinit var qsContainer: View
+
+ private lateinit var underTest: NotificationsQuickSettingsContainer
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest = NotificationsQuickSettingsContainer(context, null)
+
+ setUpViews()
+ underTest.onFinishInflate()
+ underTest.onFragmentViewCreated("QS", qsFragment)
+ }
+
+ @Test
+ fun qsContainerPaddingSetAgainAfterQsRecreated() {
+ val padding = 100
+ underTest.setQSContainerPaddingBottom(padding)
+
+ assertThat(qsContainer.paddingBottom).isEqualTo(padding)
+
+ // We reset the padding before "creating" a new QSFragment
+ qsContainer.setPadding(0, 0, 0, 0)
+ underTest.onFragmentViewCreated("QS", qsFragment)
+
+ assertThat(qsContainer.paddingBottom).isEqualTo(padding)
+ }
+
+ private fun setUpViews() {
+ qsView = FrameLayout(context)
+ qsContainer = View(context)
+ qsContainer.id = R.id.quick_settings_container
+ qsView.addView(qsContainer)
+
+ whenever(qsFrame.findViewById<View>(R.id.qs_frame)).thenReturn(qsFrame)
+ whenever(stackScroller.findViewById<View>(R.id.notification_stack_scroller))
+ .thenReturn(stackScroller)
+ whenever(keyguardStatusBar.findViewById<View>(R.id.keyguard_header))
+ .thenReturn(keyguardStatusBar)
+ whenever(qsFragment.view).thenReturn(qsView)
+
+ val layoutParams = ConstraintLayout.LayoutParams(0, 0)
+ whenever(qsFrame.layoutParams).thenReturn(layoutParams)
+ whenever(stackScroller.layoutParams).thenReturn(layoutParams)
+ whenever(keyguardStatusBar.layoutParams).thenReturn(layoutParams)
+
+ underTest.addView(qsFrame)
+ underTest.addView(stackScroller)
+ underTest.addView(keyguardStatusBar)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
index f1d56f9..a4fab1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
@@ -54,8 +54,6 @@
@SmallTest
class PulsingGestureListenerTest : SysuiTestCase() {
@Mock
- private lateinit var view: NotificationShadeWindowView
- @Mock
private lateinit var dockManager: DockManager
@Mock
private lateinit var falsingManager: FalsingManager
@@ -87,7 +85,6 @@
powerRepository = FakePowerRepository()
underTest = PulsingGestureListener(
- view,
falsingManager,
dockManager,
PowerInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index ea70e9e..2fbe871 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -32,6 +32,7 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -45,14 +46,11 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
-import java.util.function.Consumer
-import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineScheduler
@@ -64,8 +62,9 @@
import org.mockito.ArgumentMatchers.same
import org.mockito.Mockito.anyString
import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import java.util.function.Consumer
+import kotlin.time.Duration.Companion.seconds
import org.mockito.Mockito.`when` as whenever
@SmallTest
@@ -76,6 +75,7 @@
private val keyguardNotifVisibilityProvider: KeyguardNotificationVisibilityProvider = mock()
private val keyguardRepository = FakeKeyguardRepository()
private val keyguardTransitionRepository = FakeKeyguardTransitionRepository()
+ private val notifPipelineFlags: NotifPipelineFlags = mock()
private val notifPipeline: NotifPipeline = mock()
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider = mock()
private val statusBarStateController: StatusBarStateController = mock()
@@ -136,8 +136,13 @@
)
testScheduler.runCurrent()
- // THEN: We are no longer listening for shade expansions
- verify(statusBarStateController, never()).addCallback(any())
+ // WHEN: The shade is expanded
+ whenever(statusBarStateController.isExpanded).thenReturn(true)
+ statusBarStateListener.onExpandedChanged(true)
+ testScheduler.runCurrent()
+
+ // THEN: The notification is still treated as "unseen" and is not filtered out.
+ assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isFalse()
}
}
@@ -147,10 +152,6 @@
keyguardRepository.setKeyguardShowing(false)
whenever(statusBarStateController.isExpanded).thenReturn(false)
runKeyguardCoordinatorTest {
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
- )
-
// WHEN: A notification is posted
val fakeEntry = NotificationEntryBuilder().build()
collectionListener.onEntryAdded(fakeEntry)
@@ -161,9 +162,6 @@
// WHEN: The keyguard is now showing
keyguardRepository.setKeyguardShowing(true)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.AOD)
- )
testScheduler.runCurrent()
// THEN: The notification is recognized as "seen" and is filtered out.
@@ -171,9 +169,6 @@
// WHEN: The keyguard goes away
keyguardRepository.setKeyguardShowing(false)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.AOD, to = KeyguardState.GONE)
- )
testScheduler.runCurrent()
// THEN: The notification is shown regardless
@@ -187,10 +182,9 @@
keyguardRepository.setKeyguardShowing(false)
whenever(statusBarStateController.isExpanded).thenReturn(true)
runKeyguardCoordinatorTest {
- val fakeEntry =
- NotificationEntryBuilder()
+ val fakeEntry = NotificationEntryBuilder()
.setNotification(Notification.Builder(mContext, "id").setOngoing(true).build())
- .build()
+ .build()
collectionListener.onEntryAdded(fakeEntry)
// WHEN: The keyguard is now showing
@@ -208,13 +202,11 @@
keyguardRepository.setKeyguardShowing(false)
whenever(statusBarStateController.isExpanded).thenReturn(true)
runKeyguardCoordinatorTest {
- val fakeEntry =
- NotificationEntryBuilder().build().apply {
- row =
- mock<ExpandableNotificationRow>().apply {
- whenever(isMediaRow).thenReturn(true)
- }
+ val fakeEntry = NotificationEntryBuilder().build().apply {
+ row = mock<ExpandableNotificationRow>().apply {
+ whenever(isMediaRow).thenReturn(true)
}
+ }
collectionListener.onEntryAdded(fakeEntry)
// WHEN: The keyguard is now showing
@@ -307,12 +299,14 @@
runKeyguardCoordinatorTest {
// WHEN: A new notification is posted
val fakeSummary = NotificationEntryBuilder().build()
- val fakeChild =
- NotificationEntryBuilder()
+ val fakeChild = NotificationEntryBuilder()
.setGroup(context, "group")
.setGroupSummary(context, false)
.build()
- GroupEntryBuilder().setSummary(fakeSummary).addChild(fakeChild).build()
+ GroupEntryBuilder()
+ .setSummary(fakeSummary)
+ .addChild(fakeChild)
+ .build()
collectionListener.onEntryAdded(fakeSummary)
collectionListener.onEntryAdded(fakeChild)
@@ -337,10 +331,6 @@
runKeyguardCoordinatorTest {
val fakeEntry = NotificationEntryBuilder().build()
collectionListener.onEntryAdded(fakeEntry)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.AOD, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
// WHEN: five seconds have passed
testScheduler.advanceTimeBy(5.seconds)
@@ -348,16 +338,10 @@
// WHEN: Keyguard is no longer showing
keyguardRepository.setKeyguardShowing(false)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
- )
testScheduler.runCurrent()
// WHEN: Keyguard is shown again
keyguardRepository.setKeyguardShowing(true)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.AOD)
- )
testScheduler.runCurrent()
// THEN: The notification is now recognized as "seen" and is filtered out.
@@ -370,17 +354,11 @@
// GIVEN: Keyguard is showing, unseen notification is present
keyguardRepository.setKeyguardShowing(true)
runKeyguardCoordinatorTest {
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
val fakeEntry = NotificationEntryBuilder().build()
collectionListener.onEntryAdded(fakeEntry)
// WHEN: Keyguard is no longer showing
keyguardRepository.setKeyguardShowing(false)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
- )
// WHEN: Keyguard is shown again
keyguardRepository.setKeyguardShowing(true)
@@ -391,212 +369,14 @@
}
}
- @Test
- fun unseenNotificationIsNotMarkedAsSeenIfNotOnKeyguardLongEnough() {
- // GIVEN: Keyguard is showing, not dozing, unseen notification is present
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setIsDozing(false)
- runKeyguardCoordinatorTest {
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- val firstEntry = NotificationEntryBuilder().setId(1).build()
- collectionListener.onEntryAdded(firstEntry)
- testScheduler.runCurrent()
-
- // WHEN: one second has passed
- testScheduler.advanceTimeBy(1.seconds)
- testScheduler.runCurrent()
-
- // WHEN: another unseen notification is posted
- val secondEntry = NotificationEntryBuilder().setId(2).build()
- collectionListener.onEntryAdded(secondEntry)
- testScheduler.runCurrent()
-
- // WHEN: four more seconds have passed
- testScheduler.advanceTimeBy(4.seconds)
- testScheduler.runCurrent()
-
- // WHEN: the keyguard is no longer showing
- keyguardRepository.setKeyguardShowing(false)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
- )
- testScheduler.runCurrent()
-
- // WHEN: Keyguard is shown again
- keyguardRepository.setKeyguardShowing(true)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
-
- // THEN: The first notification is considered seen and is filtered out.
- assertThat(unseenFilter.shouldFilterOut(firstEntry, 0L)).isTrue()
-
- // THEN: The second notification is still considered unseen and is not filtered out
- assertThat(unseenFilter.shouldFilterOut(secondEntry, 0L)).isFalse()
- }
- }
-
- @Test
- fun unseenNotificationOnKeyguardNotMarkedAsSeenIfRemovedAfterThreshold() {
- // GIVEN: Keyguard is showing, not dozing
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setIsDozing(false)
- runKeyguardCoordinatorTest {
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
-
- // WHEN: a new notification is posted
- val entry = NotificationEntryBuilder().setId(1).build()
- collectionListener.onEntryAdded(entry)
- testScheduler.runCurrent()
-
- // WHEN: five more seconds have passed
- testScheduler.advanceTimeBy(5.seconds)
- testScheduler.runCurrent()
-
- // WHEN: the notification is removed
- collectionListener.onEntryRemoved(entry, 0)
- testScheduler.runCurrent()
-
- // WHEN: the notification is re-posted
- collectionListener.onEntryAdded(entry)
- testScheduler.runCurrent()
-
- // WHEN: one more second has passed
- testScheduler.advanceTimeBy(1.seconds)
- testScheduler.runCurrent()
-
- // WHEN: the keyguard is no longer showing
- keyguardRepository.setKeyguardShowing(false)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
- )
- testScheduler.runCurrent()
-
- // WHEN: Keyguard is shown again
- keyguardRepository.setKeyguardShowing(true)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
-
- // THEN: The notification is considered unseen and is not filtered out.
- assertThat(unseenFilter.shouldFilterOut(entry, 0L)).isFalse()
- }
- }
-
- @Test
- fun unseenNotificationOnKeyguardNotMarkedAsSeenIfRemovedBeforeThreshold() {
- // GIVEN: Keyguard is showing, not dozing
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setIsDozing(false)
- runKeyguardCoordinatorTest {
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
-
- // WHEN: a new notification is posted
- val entry = NotificationEntryBuilder().setId(1).build()
- collectionListener.onEntryAdded(entry)
- testScheduler.runCurrent()
-
- // WHEN: one second has passed
- testScheduler.advanceTimeBy(1.seconds)
- testScheduler.runCurrent()
-
- // WHEN: the notification is removed
- collectionListener.onEntryRemoved(entry, 0)
- testScheduler.runCurrent()
-
- // WHEN: the notification is re-posted
- collectionListener.onEntryAdded(entry)
- testScheduler.runCurrent()
-
- // WHEN: one more second has passed
- testScheduler.advanceTimeBy(1.seconds)
- testScheduler.runCurrent()
-
- // WHEN: the keyguard is no longer showing
- keyguardRepository.setKeyguardShowing(false)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
- )
- testScheduler.runCurrent()
-
- // WHEN: Keyguard is shown again
- keyguardRepository.setKeyguardShowing(true)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
-
- // THEN: The notification is considered unseen and is not filtered out.
- assertThat(unseenFilter.shouldFilterOut(entry, 0L)).isFalse()
- }
- }
-
- @Test
- fun unseenNotificationOnKeyguardNotMarkedAsSeenIfUpdatedBeforeThreshold() {
- // GIVEN: Keyguard is showing, not dozing
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setIsDozing(false)
- runKeyguardCoordinatorTest {
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
-
- // WHEN: a new notification is posted
- val entry = NotificationEntryBuilder().setId(1).build()
- collectionListener.onEntryAdded(entry)
- testScheduler.runCurrent()
-
- // WHEN: one second has passed
- testScheduler.advanceTimeBy(1.seconds)
- testScheduler.runCurrent()
-
- // WHEN: the notification is updated
- collectionListener.onEntryUpdated(entry)
- testScheduler.runCurrent()
-
- // WHEN: four more seconds have passed
- testScheduler.advanceTimeBy(4.seconds)
- testScheduler.runCurrent()
-
- // WHEN: the keyguard is no longer showing
- keyguardRepository.setKeyguardShowing(false)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
- )
- testScheduler.runCurrent()
-
- // WHEN: Keyguard is shown again
- keyguardRepository.setKeyguardShowing(true)
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(from = KeyguardState.GONE, to = KeyguardState.LOCKSCREEN)
- )
- testScheduler.runCurrent()
-
- // THEN: The notification is considered unseen and is not filtered out.
- assertThat(unseenFilter.shouldFilterOut(entry, 0L)).isFalse()
- }
- }
-
private fun runKeyguardCoordinatorTest(
testBlock: suspend KeyguardCoordinatorTestScope.() -> Unit
) {
val testDispatcher = UnconfinedTestDispatcher()
val testScope = TestScope(testDispatcher)
- val fakeSettings =
- FakeSettings().apply {
- putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1)
- }
+ val fakeSettings = FakeSettings().apply {
+ putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1)
+ }
val seenNotificationsProvider = SeenNotificationsProviderImpl()
val keyguardCoordinator =
KeyguardCoordinator(
@@ -607,6 +387,7 @@
keyguardRepository,
keyguardTransitionRepository,
mock<KeyguardCoordinatorLogger>(),
+ notifPipelineFlags,
testScope.backgroundScope,
sectionHeaderVisibilityProvider,
fakeSettings,
@@ -616,12 +397,11 @@
keyguardCoordinator.attach(notifPipeline)
testScope.runTest(dispatchTimeoutMs = 1.seconds.inWholeMilliseconds) {
KeyguardCoordinatorTestScope(
- keyguardCoordinator,
- testScope,
- seenNotificationsProvider,
- fakeSettings,
- )
- .testBlock()
+ keyguardCoordinator,
+ testScope,
+ seenNotificationsProvider,
+ fakeSettings,
+ ).testBlock()
}
}
@@ -634,9 +414,10 @@
val testScheduler: TestCoroutineScheduler
get() = scope.testScheduler
- val onStateChangeListener: Consumer<String> = withArgCaptor {
- verify(keyguardNotifVisibilityProvider).addOnStateChangedListener(capture())
- }
+ val onStateChangeListener: Consumer<String> =
+ withArgCaptor {
+ verify(keyguardNotifVisibilityProvider).addOnStateChangedListener(capture())
+ }
val unseenFilter: NotifFilter
get() = keyguardCoordinator.unseenNotifFilter
@@ -645,11 +426,11 @@
verify(notifPipeline).addCollectionListener(capture())
}
- val onHeadsUpChangedListener: OnHeadsUpChangedListener
- get() = withArgCaptor { verify(headsUpManager).addListener(capture()) }
+ val onHeadsUpChangedListener: OnHeadsUpChangedListener get() =
+ withArgCaptor { verify(headsUpManager).addListener(capture()) }
- val statusBarStateListener: StatusBarStateController.StateListener
- get() = withArgCaptor { verify(statusBarStateController).addCallback(capture()) }
+ val statusBarStateListener: StatusBarStateController.StateListener get() =
+ withArgCaptor { verify(statusBarStateController).addCallback(capture()) }
var showOnlyUnseenNotifsOnKeyguardSetting: Boolean
get() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 0e966dc..18cf876 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -44,10 +44,12 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.media.controls.ui.KeyguardMediaController;
import com.android.systemui.plugins.ActivityStarter;
@@ -122,6 +124,7 @@
@Mock private SysuiStatusBarStateController mSysuiStatusBarStateController;
@Mock private KeyguardBypassController mKeyguardBypassController;
@Mock private KeyguardInteractor mKeyguardInteractor;
+ @Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@Mock private MetricsLogger mMetricsLogger;
@Mock private DumpManager mDumpManager;
@@ -145,7 +148,7 @@
@Mock private StackStateLogger mStackLogger;
@Mock private NotificationStackScrollLogger mLogger;
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
- @Mock private FeatureFlags mFeatureFlags;
+ private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock private NotificationTargetsHelper mNotificationTargetsHelper;
@Mock private SecureSettings mSecureSettings;
@Mock private NotificationIconAreaController mIconAreaController;
@@ -163,6 +166,8 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, false);
+
when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
}
@@ -262,28 +267,84 @@
}
@Test
- public void testUpdateEmptyShadeView_bouncerShowing_hideEmptyView() {
+ public void testUpdateEmptyShadeView_bouncerShowing_flagOff_hideEmptyView() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
- when(mCentralSurfaces.isBouncerShowing()).thenReturn(true);
+ // WHEN the flag is off and *only* CentralSurfaces has bouncer as showing
+ mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, false);
+ mController.setBouncerShowingFromCentralSurfaces(true);
+ when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(false);
+
setupShowEmptyShadeViewState(true);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
+
+ // THEN the CentralSurfaces value is used. Since the bouncer is showing, we hide the empty
+ // view.
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ false,
/* areNotificationsHiddenInShade= */ false);
}
@Test
- public void testUpdateEmptyShadeView_bouncerNotShowing_showEmptyView() {
+ public void testUpdateEmptyShadeView_bouncerShowing_flagOn_hideEmptyView() {
when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
initController(/* viewIsAttached= */ true);
- when(mCentralSurfaces.isBouncerShowing()).thenReturn(false);
+ // WHEN the flag is on and *only* PrimaryBouncerInteractor has bouncer as showing
+ mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, true);
+ when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(true);
+ mController.setBouncerShowingFromCentralSurfaces(false);
+
setupShowEmptyShadeViewState(true);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
+
+ // THEN the PrimaryBouncerInteractor value is used. Since the bouncer is showing, we
+ // hide the empty view.
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ /* visible= */ false,
+ /* areNotificationsHiddenInShade= */ false);
+ }
+
+ @Test
+ public void testUpdateEmptyShadeView_bouncerNotShowing_flagOff_showEmptyView() {
+ when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
+ initController(/* viewIsAttached= */ true);
+
+ // WHEN the flag is off and *only* CentralSurfaces has bouncer as not showing
+ mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, false);
+ mController.setBouncerShowingFromCentralSurfaces(false);
+ when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(true);
+
+ setupShowEmptyShadeViewState(true);
+ reset(mNotificationStackScrollLayout);
+ mController.updateShowEmptyShadeView();
+
+ // THEN the CentralSurfaces value is used. Since the bouncer isn't showing, we can show the
+ // empty view.
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ /* visible= */ true,
+ /* areNotificationsHiddenInShade= */ false);
+ }
+
+ @Test
+ public void testUpdateEmptyShadeView_bouncerNotShowing_flagOn_showEmptyView() {
+ when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
+ initController(/* viewIsAttached= */ true);
+
+ // WHEN the flag is on and *only* PrimaryBouncerInteractor has bouncer as not showing
+ mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, true);
+ when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(false);
+ mController.setBouncerShowingFromCentralSurfaces(true);
+
+ setupShowEmptyShadeViewState(true);
+ reset(mNotificationStackScrollLayout);
+ mController.updateShowEmptyShadeView();
+
+ // THEN the PrimaryBouncerInteractor value is used. Since the bouncer isn't showing, we
+ // can show the empty view.
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
/* visible= */ true,
/* areNotificationsHiddenInShade= */ false);
@@ -548,6 +609,7 @@
mKeyguardMediaController,
mKeyguardBypassController,
mKeyguardInteractor,
+ mPrimaryBouncerInteractor,
mZenModeController,
mNotificationLockscreenUserManager,
Optional.<NotificationListViewModel>empty(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 7632d01..df65c09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -21,6 +21,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -29,6 +30,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.animation.Animator;
@@ -670,6 +672,28 @@
}
@Test
+ public void testForceResetSwipeStateDoesNothingIfTranslationIsZero() {
+ doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
+ doReturn(0f).when(mNotificationRow).getTranslationX();
+
+ mSwipeHelper.forceResetSwipeState(mNotificationRow);
+
+ verify(mNotificationRow).getTranslationX();
+ verifyNoMoreInteractions(mNotificationRow);
+ }
+
+ @Test
+ public void testForceResetSwipeStateResetsTranslationAndAlpha() {
+ doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
+ doReturn(10f).when(mNotificationRow).getTranslationX();
+
+ mSwipeHelper.forceResetSwipeState(mNotificationRow);
+
+ verify(mNotificationRow).setTranslation(eq(0f));
+ verify(mNotificationRow).setContentAlpha(eq(1f));
+ }
+
+ @Test
public void testContentAlphaRemainsUnchangedWhenFeatureFlagIsDisabled() {
// Returning true prevents alpha fade. In an unmocked scenario the callback is instantiated
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 7b1565e..d107a0e 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
@@ -34,7 +34,6 @@
@SmallTest
class StackScrollAlgorithmTest : SysuiTestCase() {
-
@JvmField @Rule
var expect: Expect = Expect.create()
@@ -81,26 +80,58 @@
fun resetViewStates_defaultHun_yTranslationIsInset() {
whenever(notificationRow.isPinned).thenReturn(true)
whenever(notificationRow.isHeadsUp).thenReturn(true)
-
- stackScrollAlgorithm.resetViewStates(ambientState, 0)
-
- assertThat(notificationRow.viewState.yTranslation)
- .isEqualTo(stackScrollAlgorithm.mHeadsUpInset)
+ resetViewStates_hunYTranslationIsInset()
}
@Test
- fun resetViewStates_stackMargin_changesHunYTranslation() {
+ fun resetViewStates_defaultHunWithStackMargin_changesHunYTranslation() {
whenever(notificationRow.isPinned).thenReturn(true)
whenever(notificationRow.isHeadsUp).thenReturn(true)
- val minHeadsUpTranslation = context.resources
- .getDimensionPixelSize(R.dimen.notification_side_paddings)
+ resetViewStates_stackMargin_changesHunYTranslation()
+ }
- // split shade case with top margin introduced by shade's status bar
- ambientState.stackTopMargin = 100
- stackScrollAlgorithm.resetViewStates(ambientState, 0)
+ @Test
+ fun resetViewStates_hunAnimatingAway_yTranslationIsInset() {
+ whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+ resetViewStates_hunYTranslationIsInset()
+ }
- // top margin presence should decrease heads up translation up to minHeadsUpTranslation
- assertThat(notificationRow.viewState.yTranslation).isEqualTo(minHeadsUpTranslation)
+ @Test
+ fun resetViewStates_hunAnimatingAway_StackMarginChangesHunYTranslation() {
+ whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+ resetViewStates_stackMargin_changesHunYTranslation()
+ }
+
+ @Test
+ fun resetViewStates_hunAnimatingAway_bottomNotClipped() {
+ whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ assertThat(notificationRow.viewState.clipBottomAmount).isEqualTo(0)
+ }
+
+ @Test
+ fun resetViewStates_hunsOverlapping_bottomHunClipped() {
+ val topHun = mockExpandableNotificationRow()
+ val bottomHun = mockExpandableNotificationRow()
+ whenever(topHun.isHeadsUp).thenReturn(true)
+ whenever(topHun.isPinned).thenReturn(true)
+ whenever(bottomHun.isHeadsUp).thenReturn(true)
+ whenever(bottomHun.isPinned).thenReturn(true)
+
+ resetViewStates_hunsOverlapping_bottomHunClipped(topHun, bottomHun)
+ }
+
+ @Test
+ fun resetViewStates_hunsOverlappingAndBottomHunAnimatingAway_bottomHunClipped() {
+ val topHun = mockExpandableNotificationRow()
+ val bottomHun = mockExpandableNotificationRow()
+ whenever(topHun.isHeadsUp).thenReturn(true)
+ whenever(topHun.isPinned).thenReturn(true)
+ whenever(bottomHun.isHeadsUpAnimatingAway).thenReturn(true)
+
+ resetViewStates_hunsOverlapping_bottomHunClipped(topHun, bottomHun)
}
@Test
@@ -835,6 +866,57 @@
ambientState.stackHeight = ambientState.stackEndHeight * fraction
}
+ private fun resetViewStates_hunYTranslationIsInset() {
+ stackScrollAlgorithm.resetViewStates(ambientState, 0)
+
+ assertThat(notificationRow.viewState.yTranslation)
+ .isEqualTo(stackScrollAlgorithm.mHeadsUpInset)
+ }
+
+ private fun resetViewStates_stackMargin_changesHunYTranslation() {
+ val stackTopMargin = 50
+ val headsUpTranslationY = stackScrollAlgorithm.mHeadsUpInset - stackTopMargin
+
+ // we need the shelf to mock the real-life behaviour of StackScrollAlgorithm#updateChild
+ ambientState.shelf = notificationShelf
+
+ // split shade case with top margin introduced by shade's status bar
+ ambientState.stackTopMargin = stackTopMargin
+ stackScrollAlgorithm.resetViewStates(ambientState, 0)
+
+ // heads up translation should be decreased by the top margin
+ assertThat(notificationRow.viewState.yTranslation).isEqualTo(headsUpTranslationY)
+ }
+
+ private fun resetViewStates_hunsOverlapping_bottomHunClipped(
+ topHun: ExpandableNotificationRow,
+ bottomHun: ExpandableNotificationRow
+ ) {
+ val topHunHeight = mContext.resources.getDimensionPixelSize(
+ R.dimen.notification_content_min_height)
+ val bottomHunHeight = mContext.resources.getDimensionPixelSize(
+ R.dimen.notification_max_heads_up_height)
+ whenever(topHun.intrinsicHeight).thenReturn(topHunHeight)
+ whenever(bottomHun.intrinsicHeight).thenReturn(bottomHunHeight)
+
+ // we need the shelf to mock the real-life behaviour of StackScrollAlgorithm#updateChild
+ ambientState.shelf = notificationShelf
+
+ // add two overlapping HUNs
+ hostView.removeAllViews()
+ hostView.addView(topHun)
+ hostView.addView(bottomHun)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ // the height shouldn't change
+ assertThat(topHun.viewState.height).isEqualTo(topHunHeight)
+ assertThat(bottomHun.viewState.height).isEqualTo(bottomHunHeight)
+ // the HUN at the bottom should be clipped
+ assertThat(topHun.viewState.clipBottomAmount).isEqualTo(0)
+ assertThat(bottomHun.viewState.clipBottomAmount).isEqualTo(bottomHunHeight - topHunHeight)
+ }
+
private fun resetViewStates_expansionChanging_notificationAlphaUpdated(
expansionFraction: Float,
expectedAlpha: Float,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 5ed9a86..4762e57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -99,6 +99,7 @@
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.biometrics.AuthRippleController;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.charging.WiredChargingRippleController;
import com.android.systemui.classifier.FalsingCollectorFake;
@@ -113,7 +114,6 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.notetask.NoteTaskController;
@@ -122,6 +122,7 @@
import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
@@ -145,6 +146,7 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
@@ -259,6 +261,7 @@
@Mock private AccessibilityFloatingMenuController mAccessibilityFloatingMenuController;
@Mock private SysuiColorExtractor mColorExtractor;
private WakefulnessLifecycle mWakefulnessLifecycle;
+ @Mock private PowerInteractor mPowerInteractor;
@Mock private ColorExtractor.GradientColors mGradientColors;
@Mock private PulseExpansionHandler mPulseExpansionHandler;
@Mock private NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
@@ -273,6 +276,7 @@
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private NotificationIconAreaController mNotificationIconAreaController;
@Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
+ @Mock private NotificationShelfController mNotificationShelfController;
@Mock private DozeParameters mDozeParameters;
@Mock private Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
@Mock private LockscreenWallpaper mLockscreenWallpaper;
@@ -490,6 +494,7 @@
mColorExtractor,
mScreenLifecycle,
mWakefulnessLifecycle,
+ mPowerInteractor,
mStatusBarStateController,
Optional.of(mBubbles),
() -> mNoteTaskController,
@@ -499,6 +504,7 @@
() -> mAssistManager,
configurationController,
mNotificationShadeWindowController,
+ mNotificationShelfController,
mDozeParameters,
mScrimController,
mLockscreenWallpaperLazy,
@@ -580,6 +586,7 @@
mCentralSurfaces.mPresenter = mNotificationPresenter;
mCentralSurfaces.mKeyguardIndicationController = mKeyguardIndicationController;
mCentralSurfaces.mBarService = mBarService;
+ mCentralSurfaces.mStackScrollerController = mStackScrollerController;
mCentralSurfaces.mStackScroller = mStackScroller;
mCentralSurfaces.mGestureWakeLock = mPowerManager.newWakeLock(
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "sysui:GestureWakeLock");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 7e69efa..5d2b59b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -67,6 +67,8 @@
private float mQsExpansion;
private int mCutoutTopInset = 0;
private boolean mIsSplitShade = false;
+ private boolean mBypassEnabled = false;
+ private int mUnlockedStackScrollerPadding = 0;
private float mUdfpsTop = -1;
private float mClockBottom = SCREEN_HEIGHT / 2;
private boolean mClockTopAligned;
@@ -339,15 +341,52 @@
}
@Test
- public void notifMinPaddingAlignedWithClockInSplitShadeMode() {
+ public void notifPadding_splitShade() {
givenLockScreen();
mIsSplitShade = true;
mKeyguardStatusHeight = 200;
// WHEN the position algorithm is run
positionClock();
// THEN the padding DOESN'T adjust for keyguard status height.
- assertThat(mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding())
- .isEqualTo(mKeyguardStatusBarHeaderHeight);
+ assertThat(mClockPositionAlgorithm.getLockscreenNotifPadding(/* nsslTop= */ 10))
+ .isEqualTo(mKeyguardStatusBarHeaderHeight - 10);
+ }
+
+ @Test
+ public void notifPadding_portraitShade_bypassOff() {
+ givenLockScreen();
+ mIsSplitShade = false;
+ mBypassEnabled = false;
+
+ // mMinTopMargin = 100 = 80 + max(20, 0)
+ mKeyguardStatusBarHeaderHeight = 80;
+ mUserSwitchHeight = 20;
+ when(mResources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin))
+ .thenReturn(0);
+
+ mKeyguardStatusHeight = 200;
+
+ // WHEN the position algorithm is run
+ positionClock();
+
+ // THEN padding = 300 = mMinTopMargin(100) + mKeyguardStatusHeight(200)
+ assertThat(mClockPositionAlgorithm.getLockscreenNotifPadding(/* nsslTop= */ 50))
+ .isEqualTo(300);
+ }
+
+ @Test
+ public void notifPadding_portraitShade_bypassOn() {
+ givenLockScreen();
+ mIsSplitShade = false;
+ mBypassEnabled = true;
+ mUnlockedStackScrollerPadding = 200;
+
+ // WHEN the position algorithm is run
+ positionClock();
+
+ // THEN padding = 150 = mUnlockedStackScrollerPadding(200) - nsslTop(50)
+ assertThat(mClockPositionAlgorithm.getLockscreenNotifPadding(/* nsslTop= */ 50))
+ .isEqualTo(150);
}
@Test
@@ -589,8 +628,8 @@
0 /* userSwitchPreferredY */,
mDark,
ZERO_DRAG,
- false /* bypassEnabled */,
- 0 /* unlockedStackScrollerPadding */,
+ mBypassEnabled,
+ mUnlockedStackScrollerPadding,
mQsExpansion,
mCutoutTopInset,
mIsSplitShade,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
index c8c24a7..6301fa0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -47,7 +47,8 @@
testScope = TestScope(UnconfinedTestDispatcher())
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(keyguardTransitionRepository)
+ val interactor =
+ KeyguardTransitionInteractor(keyguardTransitionRepository, testScope.backgroundScope)
underTest = CollapsedStatusBarViewModelImpl(interactor, testScope.backgroundScope)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index ef3a332..8f725be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -16,19 +16,13 @@
package com.android.systemui.volume;
-import static android.media.AudioManager.RINGER_MODE_NORMAL;
-import static android.media.AudioManager.RINGER_MODE_SILENT;
-import static android.media.AudioManager.RINGER_MODE_VIBRATE;
-
import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN;
import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN;
import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertTrue;
-import static org.junit.Assume.assumeNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -299,7 +293,7 @@
@Test
public void testSelectVibrateFromDrawer() {
final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
+ initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
mDialog.onStateChangedH(initialUnsetState);
mActiveRinger.performClick();
@@ -313,7 +307,7 @@
@Test
public void testSelectMuteFromDrawer() {
final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
+ initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
mDialog.onStateChangedH(initialUnsetState);
mActiveRinger.performClick();
@@ -335,7 +329,7 @@
// Make sure we've actually changed the ringer mode.
verify(mVolumeDialogController, times(1)).setRingerMode(
- RINGER_MODE_NORMAL, false);
+ AudioManager.RINGER_MODE_NORMAL, false);
}
/**
@@ -517,87 +511,6 @@
}
}
- private enum RingerDrawerState {INIT, OPEN, CLOSE}
-
- @Test
- public void ringerModeNormal_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.INIT);
- }
-
- @Test
- public void ringerModeSilent_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.INIT);
- }
-
- @Test
- public void ringerModeVibrate_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.INIT);
- }
-
- @Test
- public void ringerModeNormal_openDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.OPEN);
- }
-
- @Test
- public void ringerModeSilent_openDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.OPEN);
- }
-
- @Test
- public void ringerModeVibrate_openDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.OPEN);
- }
-
- @Test
- public void ringerModeNormal_closeDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.CLOSE);
- }
-
- @Test
- public void ringerModeSilent_closeDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.CLOSE);
- }
-
- @Test
- public void ringerModeVibrate_closeDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.CLOSE);
- }
-
- /**
- * The content description should include ringer state, and the correct one.
- */
- private void assertRingerContainerDescribesItsState(int ringerMode,
- RingerDrawerState drawerState) {
- State state = createShellState();
- state.ringerModeInternal = ringerMode;
- mDialog.onStateChangedH(state);
-
- mDialog.show(SHOW_REASON_UNKNOWN);
-
- if (drawerState != RingerDrawerState.INIT) {
- // in both cases we first open the drawer
- mDialog.toggleRingerDrawer(true);
-
- if (drawerState == RingerDrawerState.CLOSE) {
- mDialog.toggleRingerDrawer(false);
- }
- }
-
- String ringerContainerDescription = mDialog.getSelectedRingerContainerDescription();
- assumeNotNull(ringerContainerDescription);
-
- String ringerDescription = mContext.getString(
- mDialog.getStringDescriptionResourceForRingerMode(ringerMode));
-
- if (drawerState == RingerDrawerState.OPEN) {
- assertEquals(ringerDescription, ringerContainerDescription);
- } else {
- assertNotSame(ringerDescription, ringerContainerDescription);
- assertTrue(ringerContainerDescription.startsWith(ringerDescription));
- }
- }
-
@After
public void teardown() {
if (mDialog != null) {
diff --git a/packages/overlays/NotesRoleEnabledOverlay/Android.bp b/packages/overlays/NotesRoleEnabledOverlay/Android.bp
index 68ebd96..70b783f 100644
--- a/packages/overlays/NotesRoleEnabledOverlay/Android.bp
+++ b/packages/overlays/NotesRoleEnabledOverlay/Android.bp
@@ -25,6 +25,7 @@
runtime_resource_overlay {
name: "NotesRoleEnabledOverlay",
+ certificate: "platform",
theme: "NotesRoleEnabled",
product_specific: true,
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 1a57bc1..ec4203e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -63,6 +63,7 @@
import android.text.TextUtils.SimpleStringSplitter;
import android.util.ArrayMap;
import android.util.LocalLog;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -334,7 +335,8 @@
// of time.
synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
if (service != null) {
service.onSwitchInputMethod();
}
@@ -366,11 +368,12 @@
boolean isTemporary) {
mAugmentedAutofillState.setServiceInfo(userId, serviceName, isTemporary);
synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
if (service == null) {
// If we cannot get the service from the services cache, it will call
// updateRemoteAugmentedAutofillService() finally. Skip call this update again.
- getServiceForUserLocked(userId);
+ getServiceForUserWithLocalBinderIdentityLocked(userId);
} else {
service.updateRemoteAugmentedAutofillService();
}
@@ -380,17 +383,46 @@
private void onFieldClassificationServiceNameChanged(
@UserIdInt int userId, @Nullable String serviceName, boolean isTemporary) {
synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
if (service == null) {
// If we cannot get the service from the services cache, it will call
// updateRemoteFieldClassificationService() finally. Skip call this update again.
- getServiceForUserLocked(userId);
+ getServiceForUserWithLocalBinderIdentityLocked(userId);
} else {
service.updateRemoteFieldClassificationService();
}
}
}
+ @GuardedBy("mLock")
+ @Nullable
+ private AutofillManagerServiceImpl getServiceForUserWithLocalBinderIdentityLocked(int userId) {
+ final long token = Binder.clearCallingIdentity();
+ AutofillManagerServiceImpl managerService = null;
+ try {
+ managerService = getServiceForUserLocked(userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ return managerService;
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private AutofillManagerServiceImpl peekServiceForUserWithLocalBinderIdentityLocked(int userId) {
+ final long token = Binder.clearCallingIdentity();
+ AutofillManagerServiceImpl managerService = null;
+ try {
+ managerService = peekServiceForUserLocked(userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ return managerService;
+ }
+
@Override // from AbstractMasterSystemService
protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
boolean disabled) {
@@ -1038,7 +1070,8 @@
mUi.hideAll(null);
synchronized (mLock) {
final AutofillManagerServiceImpl service =
- getServiceForUserLocked(UserHandle.getCallingUserId());
+ getServiceForUserWithLocalBinderIdentityLocked(
+ UserHandle.getCallingUserId());
service.onBackKeyPressed();
}
}
@@ -1537,20 +1570,27 @@
public void addClient(IAutoFillManagerClient client, ComponentName componentName,
int userId, IResultReceiver receiver) {
int flags = 0;
- synchronized (mLock) {
- final int enabledFlags = getServiceForUserLocked(userId).addClientLocked(client,
- componentName);
- if (enabledFlags != 0) {
- flags |= enabledFlags;
+ try {
+ synchronized (mLock) {
+ final int enabledFlags =
+ getServiceForUserWithLocalBinderIdentityLocked(userId)
+ .addClientLocked(client, componentName);
+ if (enabledFlags != 0) {
+ flags |= enabledFlags;
+ }
+ if (sDebug) {
+ flags |= AutofillManager.FLAG_ADD_CLIENT_DEBUG;
+ }
+ if (sVerbose) {
+ flags |= AutofillManager.FLAG_ADD_CLIENT_VERBOSE;
+ }
}
- if (sDebug) {
- flags |= AutofillManager.FLAG_ADD_CLIENT_DEBUG;
- }
- if (sVerbose) {
- flags |= AutofillManager.FLAG_ADD_CLIENT_VERBOSE;
- }
+ } catch (Exception ex) {
+ // Don't do anything, send back default flags
+ Log.wtf(TAG, "addClient(): failed " + ex.toString());
+ } finally {
+ send(receiver, flags);
}
- send(receiver, flags);
}
@Override
@@ -1569,7 +1609,8 @@
public void setAuthenticationResult(Bundle data, int sessionId, int authenticationId,
int userId) {
synchronized (mLock) {
- final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
+ final AutofillManagerServiceImpl service =
+ getServiceForUserWithLocalBinderIdentityLocked(userId);
service.setAuthenticationResultLocked(data, sessionId, authenticationId,
getCallingUid());
}
@@ -1578,7 +1619,8 @@
@Override
public void setHasCallback(int sessionId, int userId, boolean hasIt) {
synchronized (mLock) {
- final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
+ final AutofillManagerServiceImpl service =
+ getServiceForUserWithLocalBinderIdentityLocked(userId);
service.setHasCallback(sessionId, getCallingUid(), hasIt);
}
}
@@ -1608,7 +1650,8 @@
final int taskId = mAm.getTaskIdForActivity(activityToken, false);
final long result;
synchronized (mLock) {
- final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
+ final AutofillManagerServiceImpl service =
+ getServiceForUserWithLocalBinderIdentityLocked(userId);
result = service.startSessionLocked(activityToken, taskId, getCallingUid(),
clientCallback, autofillId, bounds, value, hasCallback, clientActivity,
compatMode, mAllowInstantService, flags);
@@ -1624,51 +1667,72 @@
@Override
public void getFillEventHistory(@NonNull IResultReceiver receiver) throws RemoteException {
+ FillEventHistory fillEventHistory = null;
final int userId = UserHandle.getCallingUserId();
- FillEventHistory fillEventHistory = null;
- synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
- if (service != null) {
- fillEventHistory = service.getFillEventHistory(getCallingUid());
- } else if (sVerbose) {
- Slog.v(TAG, "getFillEventHistory(): no service for " + userId);
+ try {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ fillEventHistory = service.getFillEventHistory(getCallingUid());
+ } else if (sVerbose) {
+ Slog.v(TAG, "getFillEventHistory(): no service for " + userId);
+ }
}
+ } catch (Exception ex) {
+ // Do not raise the exception, just send back the null response
+ Log.wtf(TAG, "getFillEventHistory(): failed " + ex.toString());
+ } finally {
+ send(receiver, fillEventHistory);
}
- send(receiver, fillEventHistory);
}
@Override
public void getUserData(@NonNull IResultReceiver receiver) throws RemoteException {
+ UserData userData = null;
final int userId = UserHandle.getCallingUserId();
- UserData userData = null;
- synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
- if (service != null) {
- userData = service.getUserData(getCallingUid());
- } else if (sVerbose) {
- Slog.v(TAG, "getUserData(): no service for " + userId);
+ try {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ userData = service.getUserData(getCallingUid());
+ } else if (sVerbose) {
+ Slog.v(TAG, "getUserData(): no service for " + userId);
+ }
}
+ } catch (Exception ex) {
+ // Do not raise the exception, just send back the null response
+ Log.wtf(TAG, "getUserData(): failed " + ex.toString());
+ } finally {
+ send(receiver, userData);
}
- send(receiver, userData);
}
@Override
public void getUserDataId(@NonNull IResultReceiver receiver) throws RemoteException {
- final int userId = UserHandle.getCallingUserId();
UserData userData = null;
+ final int userId = UserHandle.getCallingUserId();
- synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
- if (service != null) {
- userData = service.getUserData(getCallingUid());
- } else if (sVerbose) {
- Slog.v(TAG, "getUserDataId(): no service for " + userId);
+ try {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ userData = service.getUserData(getCallingUid());
+ } else if (sVerbose) {
+ Slog.v(TAG, "getUserDataId(): no service for " + userId);
+ }
}
+ } catch (Exception ex) {
+ // Do not raise the exception, just send back the null response
+ Log.wtf(TAG, "getUserDataId(): failed " + ex.toString());
+ } finally {
+ final String userDataId = userData == null ? null : userData.getId();
+ send(receiver, userDataId);
}
- final String userDataId = userData == null ? null : userData.getId();
- send(receiver, userDataId);
}
@Override
@@ -1676,7 +1740,8 @@
final int userId = UserHandle.getCallingUserId();
synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
if (service != null) {
service.setUserData(getCallingUid(), userData);
} else if (sVerbose) {
@@ -1688,124 +1753,171 @@
@Override
public void isFieldClassificationEnabled(@NonNull IResultReceiver receiver)
throws RemoteException {
- final int userId = UserHandle.getCallingUserId();
boolean enabled = false;
+ final int userId = UserHandle.getCallingUserId();
- synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
- if (service != null) {
- enabled = service.isFieldClassificationEnabled(getCallingUid());
- } else if (sVerbose) {
- Slog.v(TAG, "isFieldClassificationEnabled(): no service for " + userId);
+ try {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ enabled = service.isFieldClassificationEnabled(getCallingUid());
+ } else if (sVerbose) {
+ Slog.v(TAG, "isFieldClassificationEnabled(): no service for " + userId);
+ }
}
+ } catch (Exception ex) {
+ // Do not raise the exception, just send back false
+ Log.wtf(TAG, "isFieldClassificationEnabled(): failed " + ex.toString());
+ } finally {
+ send(receiver, enabled);
}
- send(receiver, enabled);
}
@Override
public void getDefaultFieldClassificationAlgorithm(@NonNull IResultReceiver receiver)
throws RemoteException {
- final int userId = UserHandle.getCallingUserId();
String algorithm = null;
+ final int userId = UserHandle.getCallingUserId();
- synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
- if (service != null) {
- algorithm = service.getDefaultFieldClassificationAlgorithm(getCallingUid());
- } else {
- if (sVerbose) {
- Slog.v(TAG, "getDefaultFcAlgorithm(): no service for " + userId);
+ try {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ algorithm = service.getDefaultFieldClassificationAlgorithm(getCallingUid());
+ } else {
+ if (sVerbose) {
+ Slog.v(TAG, "getDefaultFcAlgorithm(): no service for " + userId);
+ }
}
- }
+ }
+ } catch (Exception ex) {
+ // Do not raise the exception, just send back null
+ Log.wtf(TAG, "getDefaultFieldClassificationAlgorithm(): failed " + ex.toString());
+ } finally {
+ send(receiver, algorithm);
}
- send(receiver, algorithm);
+
}
@Override
public void setAugmentedAutofillWhitelist(@Nullable List<String> packages,
@Nullable List<ComponentName> activities, @NonNull IResultReceiver receiver)
throws RemoteException {
+ boolean ok = false;
final int userId = UserHandle.getCallingUserId();
- boolean ok;
- synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
- if (service != null) {
- ok = service.setAugmentedAutofillWhitelistLocked(packages, activities,
- getCallingUid());
- } else {
- if (sVerbose) {
- Slog.v(TAG, "setAugmentedAutofillWhitelist(): no service for " + userId);
+ try {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ ok = service.setAugmentedAutofillWhitelistLocked(packages, activities,
+ getCallingUid());
+ } else {
+ if (sVerbose) {
+ Slog.v(TAG, "setAugmentedAutofillWhitelist(): no service for "
+ + userId);
+ }
}
- ok = false;
}
+ } catch (Exception ex) {
+ // Do not raise the exception, return the default value
+ Log.wtf(TAG, "setAugmentedAutofillWhitelist(): failed " + ex.toString());
+ } finally {
+ send(receiver,
+ ok ? AutofillManager.RESULT_OK
+ : AutofillManager.RESULT_CODE_NOT_SERVICE);
}
- send(receiver,
- ok ? AutofillManager.RESULT_OK : AutofillManager.RESULT_CODE_NOT_SERVICE);
}
@Override
public void getAvailableFieldClassificationAlgorithms(@NonNull IResultReceiver receiver)
throws RemoteException {
- final int userId = UserHandle.getCallingUserId();
String[] algorithms = null;
+ final int userId = UserHandle.getCallingUserId();
- synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
- if (service != null) {
- algorithms = service.getAvailableFieldClassificationAlgorithms(getCallingUid());
- } else {
- if (sVerbose) {
- Slog.v(TAG, "getAvailableFcAlgorithms(): no service for " + userId);
+ try {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ algorithms = service
+ .getAvailableFieldClassificationAlgorithms(getCallingUid());
+ } else {
+ if (sVerbose) {
+ Slog.v(TAG, "getAvailableFcAlgorithms(): no service for " + userId);
+ }
}
}
+ } catch (Exception ex) {
+ // Do not raise the exception, return null
+ Log.wtf(TAG, "getAvailableFieldClassificationAlgorithms(): failed "
+ + ex.toString());
+ } finally {
+ send(receiver, algorithms);
}
- send(receiver, algorithms);
}
@Override
public void getAutofillServiceComponentName(@NonNull IResultReceiver receiver)
throws RemoteException {
+ ComponentName componentName = null;
final int userId = UserHandle.getCallingUserId();
- ComponentName componentName = null;
- synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
- if (service != null) {
- componentName = service.getServiceComponentName();
- } else if (sVerbose) {
- Slog.v(TAG, "getAutofillServiceComponentName(): no service for " + userId);
+ try {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ componentName = service.getServiceComponentName();
+ } else if (sVerbose) {
+ Slog.v(TAG, "getAutofillServiceComponentName(): no service for " + userId);
+ }
}
+ } catch (Exception ex) {
+ Log.wtf(TAG, "getAutofillServiceComponentName(): failed " + ex.toString());
+ } finally {
+ send(receiver, componentName);
}
- send(receiver, componentName);
}
@Override
public void restoreSession(int sessionId, @NonNull IBinder activityToken,
@NonNull IBinder appCallback, @NonNull IResultReceiver receiver)
throws RemoteException {
- final int userId = UserHandle.getCallingUserId();
- Objects.requireNonNull(activityToken, "activityToken");
- Objects.requireNonNull(appCallback, "appCallback");
-
boolean restored = false;
- synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
- if (service != null) {
- restored = service.restoreSession(sessionId, getCallingUid(), activityToken,
- appCallback);
- } else if (sVerbose) {
- Slog.v(TAG, "restoreSession(): no service for " + userId);
+ final int userId = UserHandle.getCallingUserId();
+
+ try {
+ Objects.requireNonNull(activityToken, "activityToken");
+ Objects.requireNonNull(appCallback, "appCallback");
+
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ restored = service.restoreSession(sessionId, getCallingUid(), activityToken,
+ appCallback);
+ } else if (sVerbose) {
+ Slog.v(TAG, "restoreSession(): no service for " + userId);
+ }
}
+ } catch (Exception ex) {
+ // Do not propagate exception, send back status
+ Log.wtf(TAG, "restoreSession(): failed " + ex.toString());
+ } finally {
+ send(receiver, restored);
}
- send(receiver, restored);
}
@Override
public void updateSession(int sessionId, AutofillId autoFillId, Rect bounds,
AutofillValue value, int action, int flags, int userId) {
synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
if (service != null) {
service.updateSessionLocked(sessionId, getCallingUid(), autoFillId, bounds,
value, action, flags);
@@ -1818,7 +1930,8 @@
@Override
public void setAutofillFailure(int sessionId, @NonNull List<AutofillId> ids, int userId) {
synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
if (service != null) {
service.setAutofillFailureLocked(sessionId, getCallingUid(), ids);
} else if (sVerbose) {
@@ -1831,7 +1944,8 @@
public void finishSession(int sessionId, int userId,
@AutofillCommitReason int commitReason) {
synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
if (service != null) {
service.finishSessionLocked(sessionId, getCallingUid(), commitReason);
} else if (sVerbose) {
@@ -1843,19 +1957,22 @@
@Override
public void cancelSession(int sessionId, int userId) {
synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
if (service != null) {
service.cancelSessionLocked(sessionId, getCallingUid());
} else if (sVerbose) {
Slog.v(TAG, "cancelSession(): no service for " + userId);
}
}
+
}
@Override
public void disableOwnedAutofillServices(int userId) {
synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
if (service != null) {
service.disableOwnedAutofillServicesLocked(Binder.getCallingUid());
} else if (sVerbose) {
@@ -1867,21 +1984,36 @@
@Override
public void isServiceSupported(int userId, @NonNull IResultReceiver receiver) {
boolean supported = false;
- synchronized (mLock) {
- supported = !isDisabledLocked(userId);
+
+ try {
+ synchronized (mLock) {
+ supported = !isDisabledLocked(userId);
+ }
+ } catch (Exception ex) {
+ // Do not propagate exception
+ Log.wtf(TAG, "isServiceSupported(): failed " + ex.toString());
+ } finally {
+ send(receiver, supported);
}
- send(receiver, supported);
}
@Override
public void isServiceEnabled(int userId, @NonNull String packageName,
@NonNull IResultReceiver receiver) {
boolean enabled = false;
- synchronized (mLock) {
- final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
- enabled = Objects.equals(packageName, service.getServicePackageName());
+
+ try {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ enabled = Objects.equals(packageName, service.getServicePackageName());
+ }
+ } catch (Exception ex) {
+ // Do not propagate exception
+ Log.wtf(TAG, "isServiceEnabled(): failed " + ex.toString());
+ } finally {
+ send(receiver, enabled);
}
- send(receiver, enabled);
}
@Override
@@ -1891,8 +2023,9 @@
|| operation == AutofillManager.PENDING_UI_OPERATION_RESTORE,
"invalid operation: %d", operation);
synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(
- UserHandle.getCallingUserId());
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(
+ UserHandle.getCallingUserId());
if (service != null) {
service.onPendingSaveUi(operation, token);
}
@@ -1907,7 +2040,7 @@
boolean uiOnly = false;
if (args != null) {
for (String arg : args) {
- switch(arg) {
+ switch (arg) {
case "--no-history":
showHistory = false;
break;
@@ -1934,27 +2067,38 @@
try {
sDebug = sVerbose = true;
synchronized (mLock) {
- pw.print("sDebug: "); pw.print(realDebug);
- pw.print(" sVerbose: "); pw.println(realVerbose);
+ pw.print("sDebug: ");
+ pw.print(realDebug);
+ pw.print(" sVerbose: ");
+ pw.println(realVerbose);
pw.print("Flags: ");
synchronized (mFlagLock) {
- pw.print("mPccClassificationEnabled="); pw.print(mPccClassificationEnabled);
+ pw.print("mPccClassificationEnabled=");
+ pw.print(mPccClassificationEnabled);
pw.print(";");
- pw.print("mPccPreferProviderOverPcc="); pw.print(mPccPreferProviderOverPcc);
+ pw.print("mPccPreferProviderOverPcc=");
+ pw.print(mPccPreferProviderOverPcc);
pw.print(";");
- pw.print("mPccUseFallbackDetection="); pw.print(mPccUseFallbackDetection);
+ pw.print("mPccUseFallbackDetection=");
+ pw.print(mPccUseFallbackDetection);
pw.print(";");
- pw.print("mPccProviderHints="); pw.println(mPccProviderHints);
+ pw.print("mPccProviderHints=");
+ pw.println(mPccProviderHints);
}
// Dump per-user services
dumpLocked("", pw);
- mAugmentedAutofillResolver.dumpShort(pw); pw.println();
- pw.print("Max partitions per session: "); pw.println(sPartitionMaxCount);
- pw.print("Max visible datasets: "); pw.println(sVisibleDatasetsMaxCount);
+ mAugmentedAutofillResolver.dumpShort(pw);
+ pw.println();
+ pw.print("Max partitions per session: ");
+ pw.println(sPartitionMaxCount);
+ pw.print("Max visible datasets: ");
+ pw.println(sVisibleDatasetsMaxCount);
if (sFullScreenMode != null) {
- pw.print("Overridden full-screen mode: "); pw.println(sFullScreenMode);
+ pw.print("Overridden full-screen mode: ");
+ pw.println(sFullScreenMode);
}
- pw.println("User data constraints: "); UserData.dumpConstraints(prefix, pw);
+ pw.println("User data constraints: ");
+ UserData.dumpConstraints(prefix, pw);
mUi.dump(pw);
pw.print("Autofill Compat State: ");
mAutofillCompatState.dump(prefix, pw);
@@ -1969,11 +2113,17 @@
pw.print("Augmented Service Request Timeout: ");
pw.println(mAugmentedServiceRequestTimeoutMs);
if (showHistory) {
- pw.println(); pw.println("Requests history:"); pw.println();
+ pw.println();
+ pw.println("Requests history:");
+ pw.println();
mRequestsHistory.reverseDump(fd, pw, args);
- pw.println(); pw.println("UI latency history:"); pw.println();
+ pw.println();
+ pw.println("UI latency history:");
+ pw.println();
mUiLatencyHistory.reverseDump(fd, pw, args);
- pw.println(); pw.println("WTF history:"); pw.println();
+ pw.println();
+ pw.println("WTF history:");
+ pw.println();
mWtfHistory.reverseDump(fd, pw, args);
}
pw.println("Augmented Autofill State: ");
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 3efb6c3..c6e9a7d 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -162,6 +162,8 @@
// TODO(b/180451994): ensure all incoming + outgoing calls have a cleared calling identity
public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull private static final String TAG = VcnManagementService.class.getSimpleName();
+ @NonNull private static final String CONTEXT_ATTRIBUTION_TAG = "VCN";
+
private static final long DUMP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5);
private static final int LOCAL_LOG_LINE_COUNT = 512;
@@ -223,7 +225,9 @@
@VisibleForTesting(visibility = Visibility.PRIVATE)
VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) {
- mContext = requireNonNull(context, "Missing context");
+ mContext =
+ requireNonNull(context, "Missing context")
+ .createAttributionContext(CONTEXT_ATTRIBUTION_TAG);
mDeps = requireNonNull(deps, "Missing dependencies");
mLooper = mDeps.getLooper();
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 9b4f968..0d423d8 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -80,6 +80,7 @@
*/
public class VpnManagerService extends IVpnManager.Stub {
private static final String TAG = VpnManagerService.class.getSimpleName();
+ private static final String CONTEXT_ATTRIBUTION_TAG = "VPN_MANAGER";
@VisibleForTesting
protected final HandlerThread mHandlerThread;
@@ -157,7 +158,7 @@
}
public VpnManagerService(Context context, Dependencies deps) {
- mContext = context;
+ mContext = context.createAttributionContext(CONTEXT_ATTRIBUTION_TAG);
mDeps = deps;
mHandlerThread = mDeps.makeHandlerThread();
mHandlerThread.start();
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index fd98072..ba8a1b9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -149,7 +149,6 @@
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
@@ -609,15 +608,7 @@
return 1;
}
- AtomicReference<String> mimeType = new AtomicReference<>(intent.getType());
-
- if (mimeType.get() == null && intent.getData() != null
- && "content".equals(intent.getData().getScheme())) {
- mInterface.getMimeTypeFilterAsync(intent.getData(), mUserId,
- new RemoteCallback(result -> {
- mimeType.set(result.getPairValue());
- }));
- }
+ final String mimeType = intent.resolveType(mInternal.mContext);
do {
if (mStopOption) {
@@ -629,7 +620,7 @@
int userIdForQuery = mInternal.mUserController.handleIncomingUser(
Binder.getCallingPid(), Binder.getCallingUid(), mUserId, false,
ALLOW_NON_FULL, "ActivityManagerShellCommand", null);
- List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType.get(),
+ List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType,
0, userIdForQuery).getList();
if (activities == null || activities.size() <= 0) {
getErrPrintWriter().println("Error: Intent does not match any activities: "
@@ -726,12 +717,12 @@
}
if (mWaitOption) {
result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, null, intent,
- mimeType.get(), null, null, 0, mStartFlags, profilerInfo,
+ mimeType, null, null, 0, mStartFlags, profilerInfo,
options != null ? options.toBundle() : null, mUserId);
res = result.result;
} else {
res = mInternal.startActivityAsUserWithFeature(null, SHELL_PACKAGE_NAME, null,
- intent, mimeType.get(), null, null, 0, mStartFlags, profilerInfo,
+ intent, mimeType, null, null, 0, mStartFlags, profilerInfo,
options != null ? options.toBundle() : null, mUserId);
}
final long endTime = SystemClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 397eef4..42a4a85 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -33,6 +33,7 @@
import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList;
import static com.android.server.am.BroadcastProcessQueue.reasonToString;
import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList;
+import static com.android.server.am.BroadcastRecord.DELIVERY_DEFERRED;
import static com.android.server.am.BroadcastRecord.deliveryStateToString;
import static com.android.server.am.BroadcastRecord.getReceiverClassName;
import static com.android.server.am.BroadcastRecord.getReceiverPackageName;
@@ -68,6 +69,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.format.DateUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.MathUtils;
@@ -213,6 +215,13 @@
new AtomicReference<>();
/**
+ * Container for holding the set of broadcast records that satisfied a certain criteria.
+ */
+ @GuardedBy("mService")
+ private final AtomicReference<ArrayMap<BroadcastRecord, Boolean>> mRecordsLookupCache =
+ new AtomicReference<>();
+
+ /**
* Map from UID to its last known "foreground" state. A UID is considered to be in
* "foreground" state when it's procState is {@link ActivityManager#PROCESS_STATE_TOP}.
* <p>
@@ -682,10 +691,10 @@
// If this receiver is going to be skipped, skip it now itself and don't even enqueue
// it.
- final boolean wouldBeSkipped = (mSkipPolicy.shouldSkipMessage(r, receiver) != null);
- if (wouldBeSkipped) {
+ final String skipReason = mSkipPolicy.shouldSkipMessage(r, receiver);
+ if (skipReason != null) {
setDeliveryState(null, null, r, i, receiver, BroadcastRecord.DELIVERY_SKIPPED,
- "skipped by policy at enqueue");
+ "skipped by policy at enqueue: " + skipReason);
continue;
}
@@ -742,13 +751,16 @@
broadcastConsumer = mBroadcastConsumerSkipAndCanceled;
break;
case BroadcastOptions.DELIVERY_GROUP_POLICY_MERGED:
+ // TODO: Allow applying MERGED policy for broadcasts with more than one receiver.
+ if (r.receivers.size() > 1) {
+ return;
+ }
final BundleMerger extrasMerger = r.options.getDeliveryGroupExtrasMerger();
if (extrasMerger == null) {
// Extras merger is required to be able to merge the extras. So, if it's not
// supplied, then ignore the delivery group policy.
return;
}
- // TODO: Don't merge with the same BroadcastRecord more than once.
broadcastConsumer = (record, recordIndex) -> {
r.intent.mergeExtras(record.intent, extrasMerger);
mBroadcastConsumerSkipAndCanceled.accept(record, recordIndex);
@@ -758,6 +770,7 @@
logw("Unknown delivery group policy: " + policy);
return;
}
+ final ArrayMap<BroadcastRecord, Boolean> recordsLookupCache = getRecordsLookupCache();
forEachMatchingBroadcast(QUEUE_PREDICATE_ANY, (testRecord, testIndex) -> {
// If the receiver is already in a terminal state, then ignore it.
if (isDeliveryStateTerminal(testRecord.getDeliveryState(testIndex))) {
@@ -769,22 +782,44 @@
|| !r.matchesDeliveryGroup(testRecord)) {
return false;
}
- // TODO: If a process is in a deferred state, we can always apply the policy as long
- // as it is one of the receivers for the new broadcast.
// For ordered broadcast, check if the receivers for the new broadcast is a superset
// of those for the previous one as skipping and removing only one of them could result
// in an inconsistent state.
- if (testRecord.ordered || testRecord.resultTo != null) {
- // TODO: Cache this result in some way so that we don't have to perform the
- // same check for all the broadcast receivers.
- return r.containsAllReceivers(testRecord.receivers);
- } else if (testRecord.prioritized) {
- return r.containsAllReceivers(testRecord.receivers);
+ if (testRecord.ordered || testRecord.prioritized) {
+ return containsAllReceivers(r, testRecord, recordsLookupCache);
+ } else if (testRecord.resultTo != null) {
+ return testRecord.getDeliveryState(testIndex) == DELIVERY_DEFERRED
+ ? r.containsReceiver(testRecord.receivers.get(testIndex))
+ : containsAllReceivers(r, testRecord, recordsLookupCache);
} else {
return r.containsReceiver(testRecord.receivers.get(testIndex));
}
}, broadcastConsumer, true);
+ recordsLookupCache.clear();
+ mRecordsLookupCache.compareAndSet(null, recordsLookupCache);
+ }
+
+ @NonNull
+ private ArrayMap<BroadcastRecord, Boolean> getRecordsLookupCache() {
+ ArrayMap<BroadcastRecord, Boolean> recordsLookupCache =
+ mRecordsLookupCache.getAndSet(null);
+ if (recordsLookupCache == null) {
+ recordsLookupCache = new ArrayMap<>();
+ }
+ return recordsLookupCache;
+ }
+
+ private boolean containsAllReceivers(@NonNull BroadcastRecord record,
+ @NonNull BroadcastRecord testRecord,
+ @NonNull ArrayMap<BroadcastRecord, Boolean> recordsLookupCache) {
+ final int idx = recordsLookupCache.indexOfKey(testRecord);
+ if (idx > 0) {
+ return recordsLookupCache.valueAt(idx);
+ }
+ final boolean containsAll = record.containsAllReceivers(testRecord.receivers);
+ recordsLookupCache.put(testRecord, containsAll);
+ return containsAll;
}
/**
diff --git a/services/core/java/com/android/server/am/StackTracesDumpHelper.java b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
index 01fb0d1..af99684 100644
--- a/services/core/java/com/android/server/am/StackTracesDumpHelper.java
+++ b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
@@ -392,11 +392,9 @@
if (TEMP_DUMP_TIME_LIMIT <= timeTaken) {
Slog.e(TAG, "Aborted stack trace dump (current primary pid=" + pid
+ "); deadline exceeded.");
- tmpTracesFile.delete();
if (latencyTracker != null) {
latencyTracker.dumpStackTracesTempFileTimedOut();
}
- return null;
}
if (DEBUG_ANR) {
Slog.d(TAG, "Done with primary pid " + pid + " in " + timeTaken + "ms"
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index fc72a77..29a1941 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1018,13 +1018,15 @@
}
}
- /*package*/ void clearA2dpSuspended() {
+ /*package*/ void clearA2dpSuspended(boolean internalOnly) {
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "clearA2dpSuspended");
+ Log.v(TAG, "clearA2dpSuspended, internalOnly: " + internalOnly);
}
synchronized (mBluetoothAudioStateLock) {
mBluetoothA2dpSuspendedInt = false;
- mBluetoothA2dpSuspendedExt = false;
+ if (!internalOnly) {
+ mBluetoothA2dpSuspendedExt = false;
+ }
updateAudioHalBluetoothState();
}
}
@@ -1046,13 +1048,15 @@
}
}
- /*package*/ void clearLeAudioSuspended() {
+ /*package*/ void clearLeAudioSuspended(boolean internalOnly) {
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "clearLeAudioSuspended");
+ Log.v(TAG, "clearLeAudioSuspended, internalOnly: " + internalOnly);
}
synchronized (mBluetoothAudioStateLock) {
mBluetoothLeSuspendedInt = false;
- mBluetoothLeSuspendedExt = false;
+ if (!internalOnly) {
+ mBluetoothLeSuspendedExt = false;
+ }
updateAudioHalBluetoothState();
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 219dda3..ec85d57 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1547,7 +1547,7 @@
}
// Reset A2DP suspend state each time a new sink is connected
- mDeviceBroker.clearA2dpSuspended();
+ mDeviceBroker.clearA2dpSuspended(true /* internalOnly */);
// The convention for head tracking sensors associated with A2DP devices is to
// use a UUID derived from the MAC address as follows:
@@ -1970,7 +1970,7 @@
"LE Audio device addr=" + address + " now available").printLog(TAG));
}
// Reset LEA suspend state each time a new sink is connected
- mDeviceBroker.clearLeAudioSuspended();
+ mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */);
UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index ac03c82..c177c4e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -353,7 +353,6 @@
private static final int MSG_PLAY_SOUND_EFFECT = 5;
private static final int MSG_LOAD_SOUND_EFFECTS = 7;
private static final int MSG_SET_FORCE_USE = 8;
- private static final int MSG_BT_HEADSET_CNCT_FAILED = 9;
private static final int MSG_SET_ALL_VOLUMES = 10;
private static final int MSG_UNLOAD_SOUND_EFFECTS = 15;
private static final int MSG_SYSTEM_READY = 16;
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index e46c3cc..c8a17e5 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -445,8 +445,8 @@
/*package*/ synchronized void resetBluetoothSco() {
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- mDeviceBroker.clearA2dpSuspended();
- mDeviceBroker.clearLeAudioSuspended();
+ mDeviceBroker.clearA2dpSuspended(false /* internalOnly */);
+ mDeviceBroker.clearLeAudioSuspended(false /* internalOnly */);
mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index d6b6f77..5aa1175 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -149,6 +149,17 @@
@NonNull String halInstanceName,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull BiometricContext biometricContext) {
+ this(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher,
+ biometricContext, null /* daemon */);
+ }
+
+ @VisibleForTesting FaceProvider(@NonNull Context context,
+ @NonNull BiometricStateCallback biometricStateCallback,
+ @NonNull SensorProps[] props,
+ @NonNull String halInstanceName,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull BiometricContext biometricContext,
+ IFace daemon) {
mContext = context;
mBiometricStateCallback = biometricStateCallback;
mHalInstanceName = halInstanceName;
@@ -160,6 +171,7 @@
mTaskStackListener = new BiometricTaskStackListener();
mBiometricContext = biometricContext;
mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
+ mDaemon = daemon;
for (SensorProps prop : props) {
final int sensorId = prop.commonProps.sensorId;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 58ece89..9b2ea15 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -160,6 +160,17 @@
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull BiometricContext biometricContext) {
+ this(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher,
+ gestureAvailabilityDispatcher, biometricContext, null /* daemon */);
+ }
+
+ @VisibleForTesting FingerprintProvider(@NonNull Context context,
+ @NonNull BiometricStateCallback biometricStateCallback,
+ @NonNull SensorProps[] props, @NonNull String halInstanceName,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
+ @NonNull BiometricContext biometricContext,
+ IFingerprint daemon) {
mContext = context;
mBiometricStateCallback = biometricStateCallback;
mHalInstanceName = halInstanceName;
@@ -170,6 +181,7 @@
mTaskStackListener = new BiometricTaskStackListener();
mBiometricContext = biometricContext;
mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
+ mDaemon = daemon;
final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context);
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index d2dcc50..41651fd 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -126,6 +126,7 @@
private final boolean mDreamsActivatedOnChargeByDefault;
private final boolean mDreamsActivatedOnDockByDefault;
private final boolean mKeepDreamingWhenUnpluggingDefault;
+ private final boolean mDreamsDisabledByAmbientModeSuppressionConfig;
private final CopyOnWriteArrayList<DreamManagerInternal.DreamManagerStateListener>
mDreamManagerStateListeners = new CopyOnWriteArrayList<>();
@@ -239,6 +240,9 @@
mSettingsObserver = new SettingsObserver(mHandler);
mKeepDreamingWhenUnpluggingDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_keepDreamingWhenUnplugging);
+ mDreamsDisabledByAmbientModeSuppressionConfig = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig);
+
}
@Override
@@ -406,6 +410,13 @@
return false;
}
+ if (mDreamsDisabledByAmbientModeSuppressionConfig
+ && mPowerManagerInternal.isAmbientDisplaySuppressed()) {
+ // Don't dream if Bedtime (or something else) is suppressing ambient.
+ Slog.i(TAG, "Can't start dreaming because ambient is suppressed.");
+ return false;
+ }
+
if ((mWhenToDream & DREAM_ON_CHARGE) == DREAM_ON_CHARGE) {
return mIsCharging;
}
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index 611a61a2..3ac1594 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -16,6 +16,10 @@
package com.android.server.input;
+import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE;
+import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER;
+import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
+
import android.annotation.AnyThread;
import android.annotation.MainThread;
import android.annotation.NonNull;
@@ -67,6 +71,8 @@
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.XmlUtils;
+import com.android.server.input.KeyboardMetricsCollector.KeyboardConfigurationEvent;
+import com.android.server.input.KeyboardMetricsCollector.LayoutSelectionCriteria;
import com.android.server.inputmethod.InputMethodManagerInternal;
import libcore.io.Streams;
@@ -118,7 +124,7 @@
// This cache stores "best-matched" layouts so that we don't need to run the matching
// algorithm repeatedly.
@GuardedBy("mKeyboardLayoutCache")
- private final Map<String, String> mKeyboardLayoutCache = new ArrayMap<>();
+ private final Map<String, KeyboardLayoutInfo> mKeyboardLayoutCache = new ArrayMap<>();
private final Object mImeInfoLock = new Object();
@Nullable
@GuardedBy("mImeInfoLock")
@@ -194,7 +200,8 @@
setCurrentKeyboardLayoutForInputDevice(inputDevice.getIdentifier(), layout);
}
}
- config.setCurrentLayout(layout);
+ config.setCurrentLayout(
+ new KeyboardLayoutInfo(layout, LAYOUT_SELECTION_CRITERIA_USER));
if (layout == null) {
// In old settings show notification always until user manually selects a
// layout in the settings.
@@ -205,18 +212,19 @@
final InputDeviceIdentifier identifier = inputDevice.getIdentifier();
final String key = getLayoutDescriptor(identifier);
Set<String> selectedLayouts = new HashSet<>();
- for (ImeInfo imeInfo : getImeInfoListForLayoutMapping()) {
+ List<ImeInfo> imeInfoList = getImeInfoListForLayoutMapping();
+ List<KeyboardLayoutInfo> layoutInfoList = new ArrayList<>();
+ boolean hasMissingLayout = false;
+ for (ImeInfo imeInfo : imeInfoList) {
// Check if the layout has been previously configured
- String layout = getKeyboardLayoutForInputDeviceInternal(identifier,
- new ImeInfo(imeInfo.mUserId, imeInfo.mImeSubtypeHandle,
- imeInfo.mImeSubtype));
- if (layout == null) {
- // If even one layout not configured properly, we need to ask user to configure
- // the keyboard properly from the Settings.
- selectedLayouts.clear();
- break;
+ KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier,
+ imeInfo);
+ boolean noLayoutFound = layoutInfo == null || layoutInfo.mDescriptor == null;
+ if (!noLayoutFound) {
+ selectedLayouts.add(layoutInfo.mDescriptor);
}
- selectedLayouts.add(layout);
+ layoutInfoList.add(layoutInfo);
+ hasMissingLayout |= noLayoutFound;
}
if (DEBUG) {
@@ -225,24 +233,38 @@
+ selectedLayouts);
}
+ // If even one layout not configured properly, we need to ask user to configure
+ // the keyboard properly from the Settings.
+ if (hasMissingLayout) {
+ selectedLayouts.clear();
+ }
+
config.setConfiguredLayouts(selectedLayouts);
// Update current layout: If there is a change then need to reload.
synchronized (mImeInfoLock) {
- String layout = getKeyboardLayoutForInputDeviceInternal(
+ KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
inputDevice.getIdentifier(), mCurrentImeInfo);
- if (!Objects.equals(layout, config.getCurrentLayout())) {
- config.setCurrentLayout(layout);
+ if (!Objects.equals(layoutInfo, config.getCurrentLayout())) {
+ config.setCurrentLayout(layoutInfo);
mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
}
}
synchronized (mDataStore) {
try {
+ boolean isFirstConfiguration = !mDataStore.hasInputDeviceEntry(key);
if (mDataStore.setSelectedKeyboardLayouts(key, selectedLayouts)) {
// Need to show the notification only if layout selection changed
// from the previous configuration
needToShowNotification = true;
+
+ // Logging keyboard configuration data to statsd only if the
+ // configuration changed from the previous configuration. Currently
+ // only logging for New Settings UI where we are using IME to decide
+ // the layout information.
+ logKeyboardConfigurationEvent(inputDevice, imeInfoList, layoutInfoList,
+ isFirstConfiguration);
}
} finally {
mDataStore.saveIfNeeded();
@@ -252,8 +274,6 @@
if (needToShowNotification) {
maybeUpdateNotification();
}
- // TODO (b/280421650): Implement logging statements using KeyboardMetricsCollector
- // for KeyboardConfigured atom
}
private String getDefaultKeyboardLayout(final InputDevice inputDevice) {
@@ -403,7 +423,7 @@
@AnyThread
@Nullable
- public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
+ public KeyboardLayout getKeyboardLayout(@NonNull String keyboardLayoutDescriptor) {
Objects.requireNonNull(keyboardLayoutDescriptor,
"keyboardLayoutDescriptor must not be null");
@@ -751,8 +771,9 @@
String keyboardLayoutDescriptor;
if (useNewSettingsUi()) {
synchronized (mImeInfoLock) {
- keyboardLayoutDescriptor = getKeyboardLayoutForInputDeviceInternal(identifier,
+ KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier,
mCurrentImeInfo);
+ keyboardLayoutDescriptor = layoutInfo == null ? null : layoutInfo.mDescriptor;
}
} else {
keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(identifier);
@@ -789,13 +810,13 @@
return null;
}
InputMethodSubtypeHandle subtypeHandle = InputMethodSubtypeHandle.of(imeInfo, imeSubtype);
- String layout = getKeyboardLayoutForInputDeviceInternal(identifier,
+ KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier,
new ImeInfo(userId, subtypeHandle, imeSubtype));
if (DEBUG) {
Slog.d(TAG, "getKeyboardLayoutForInputDevice() " + identifier.toString() + ", userId : "
- + userId + ", subtypeHandle = " + subtypeHandle + " -> " + layout);
+ + userId + ", subtypeHandle = " + subtypeHandle + " -> " + layoutInfo);
}
- return layout;
+ return layoutInfo != null ? layoutInfo.mDescriptor : null;
}
@AnyThread
@@ -926,11 +947,11 @@
for (int i = 0; i < mConfiguredKeyboards.size(); i++) {
InputDevice inputDevice = Objects.requireNonNull(
getInputDevice(mConfiguredKeyboards.keyAt(i)));
- String layout = getKeyboardLayoutForInputDeviceInternal(inputDevice.getIdentifier(),
- mCurrentImeInfo);
+ KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
+ inputDevice.getIdentifier(), mCurrentImeInfo);
KeyboardConfiguration config = mConfiguredKeyboards.valueAt(i);
- if (!Objects.equals(layout, config.getCurrentLayout())) {
- config.setCurrentLayout(layout);
+ if (!Objects.equals(layoutInfo, config.getCurrentLayout())) {
+ config.setCurrentLayout(layoutInfo);
mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
return;
}
@@ -939,39 +960,40 @@
}
@Nullable
- private String getKeyboardLayoutForInputDeviceInternal(InputDeviceIdentifier identifier,
- @Nullable ImeInfo imeInfo) {
+ private KeyboardLayoutInfo getKeyboardLayoutForInputDeviceInternal(
+ InputDeviceIdentifier identifier, @Nullable ImeInfo imeInfo) {
InputDevice inputDevice = getInputDevice(identifier);
if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
return null;
}
String key = createLayoutKey(identifier, imeInfo);
- String layout;
synchronized (mDataStore) {
- layout = mDataStore.getKeyboardLayout(getLayoutDescriptor(identifier), key);
- }
- if (layout == null) {
- synchronized (mKeyboardLayoutCache) {
- // Check Auto-selected layout cache to see if layout had been previously selected
- if (mKeyboardLayoutCache.containsKey(key)) {
- layout = mKeyboardLayoutCache.get(key);
- } else {
- // NOTE: This list is already filtered based on IME Script code
- KeyboardLayout[] layoutList = getKeyboardLayoutListForInputDeviceInternal(
- identifier, imeInfo);
- // Call auto-matching algorithm to find the best matching layout
- layout = getDefaultKeyboardLayoutBasedOnImeInfo(inputDevice, imeInfo,
- layoutList);
- mKeyboardLayoutCache.put(key, layout);
- }
+ String layout = mDataStore.getKeyboardLayout(getLayoutDescriptor(identifier), key);
+ if (layout != null) {
+ return new KeyboardLayoutInfo(layout, LAYOUT_SELECTION_CRITERIA_USER);
}
}
- return layout;
+
+ synchronized (mKeyboardLayoutCache) {
+ // Check Auto-selected layout cache to see if layout had been previously selected
+ if (mKeyboardLayoutCache.containsKey(key)) {
+ return mKeyboardLayoutCache.get(key);
+ } else {
+ // NOTE: This list is already filtered based on IME Script code
+ KeyboardLayout[] layoutList = getKeyboardLayoutListForInputDeviceInternal(
+ identifier, imeInfo);
+ // Call auto-matching algorithm to find the best matching layout
+ KeyboardLayoutInfo layoutInfo =
+ getDefaultKeyboardLayoutBasedOnImeInfo(inputDevice, imeInfo, layoutList);
+ mKeyboardLayoutCache.put(key, layoutInfo);
+ return layoutInfo;
+ }
+ }
}
@Nullable
- private static String getDefaultKeyboardLayoutBasedOnImeInfo(InputDevice inputDevice,
- @Nullable ImeInfo imeInfo, KeyboardLayout[] layoutList) {
+ private static KeyboardLayoutInfo getDefaultKeyboardLayoutBasedOnImeInfo(
+ InputDevice inputDevice, @Nullable ImeInfo imeInfo, KeyboardLayout[] layoutList) {
Arrays.sort(layoutList);
// Check <VendorID, ProductID> matching for explicitly declared custom KCM files.
@@ -984,7 +1006,8 @@
+ "vendor and product Ids. " + inputDevice.getIdentifier()
+ " : " + layout.getDescriptor());
}
- return layout.getDescriptor();
+ return new KeyboardLayoutInfo(layout.getDescriptor(),
+ LAYOUT_SELECTION_CRITERIA_DEVICE);
}
}
@@ -1001,7 +1024,7 @@
+ "HW information (Language tag and Layout type). "
+ inputDevice.getIdentifier() + " : " + layoutDesc);
}
- return layoutDesc;
+ return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_DEVICE);
}
}
@@ -1023,7 +1046,10 @@
+ "IME locale matching. " + inputDevice.getIdentifier() + " : "
+ layoutDesc);
}
- return layoutDesc;
+ if (layoutDesc != null) {
+ return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD);
+ }
+ return null;
}
@Nullable
@@ -1229,6 +1255,26 @@
}
}
+ private void logKeyboardConfigurationEvent(@NonNull InputDevice inputDevice,
+ @NonNull List<ImeInfo> imeInfoList, @NonNull List<KeyboardLayoutInfo> layoutInfoList,
+ boolean isFirstConfiguration) {
+ if (imeInfoList.isEmpty() || layoutInfoList.isEmpty()) {
+ return;
+ }
+ KeyboardConfigurationEvent.Builder configurationEventBuilder =
+ new KeyboardConfigurationEvent.Builder(inputDevice).setIsFirstTimeConfiguration(
+ isFirstConfiguration);
+ for (int i = 0; i < imeInfoList.size(); i++) {
+ KeyboardLayoutInfo layoutInfo = layoutInfoList.get(i);
+ boolean noLayoutFound = layoutInfo == null || layoutInfo.mDescriptor == null;
+ configurationEventBuilder.addLayoutSelection(imeInfoList.get(i).mImeSubtype,
+ noLayoutFound ? null : getKeyboardLayout(layoutInfo.mDescriptor),
+ noLayoutFound ? LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
+ : layoutInfo.mSelectionCriteria);
+ }
+ KeyboardMetricsCollector.logKeyboardConfiguredAtom(configurationEventBuilder.build());
+ }
+
private boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_EXISTING_DEVICES:
@@ -1411,7 +1457,7 @@
// If null, it means no layout is selected for the device.
@Nullable
- private String mCurrentLayout;
+ private KeyboardLayoutInfo mCurrentLayout;
private boolean hasConfiguredLayouts() {
return mConfiguredLayouts != null && !mConfiguredLayouts.isEmpty();
@@ -1427,15 +1473,42 @@
}
@Nullable
- private String getCurrentLayout() {
+ private KeyboardLayoutInfo getCurrentLayout() {
return mCurrentLayout;
}
- private void setCurrentLayout(String currentLayout) {
+ private void setCurrentLayout(KeyboardLayoutInfo currentLayout) {
mCurrentLayout = currentLayout;
}
}
+ private static class KeyboardLayoutInfo {
+ @Nullable
+ private final String mDescriptor;
+ @LayoutSelectionCriteria
+ private final int mSelectionCriteria;
+
+ private KeyboardLayoutInfo(@Nullable String descriptor,
+ @LayoutSelectionCriteria int selectionCriteria) {
+ mDescriptor = descriptor;
+ mSelectionCriteria = selectionCriteria;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof KeyboardLayoutInfo) {
+ return Objects.equals(mDescriptor, ((KeyboardLayoutInfo) obj).mDescriptor)
+ && mSelectionCriteria == ((KeyboardLayoutInfo) obj).mSelectionCriteria;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * mSelectionCriteria + mDescriptor.hashCode();
+ }
+ }
+
private interface KeyboardLayoutVisitor {
void visitKeyboardLayout(Resources resources,
int keyboardLayoutResId, KeyboardLayout layout);
diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
index b8f57f5..19fa7a8 100644
--- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
+++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
@@ -19,16 +19,25 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.input.KeyboardLayout;
+import android.icu.util.ULocale;
+import android.util.Log;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.InputDevice;
+import android.view.inputmethod.InputMethodSubtype;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.KeyboardConfiguredProto.KeyboardLayoutConfig;
import com.android.internal.os.KeyboardConfiguredProto.RepeatedKeyboardLayoutConfig;
import com.android.internal.util.FrameworkStatsLog;
import java.lang.annotation.Retention;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Collect Keyboard metrics
@@ -36,6 +45,30 @@
public final class KeyboardMetricsCollector {
private static final String TAG = "KeyboardMetricCollector";
+ // To enable these logs, run: 'adb shell setprop log.tag.KeyboardMetricCollector DEBUG'
+ // (requires restart)
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ @Retention(SOURCE)
+ @IntDef(prefix = { "LAYOUT_SELECTION_CRITERIA_" }, value = {
+ LAYOUT_SELECTION_CRITERIA_USER,
+ LAYOUT_SELECTION_CRITERIA_DEVICE,
+ LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
+ })
+ public @interface LayoutSelectionCriteria {}
+
+ /** Manual selection by user */
+ public static final int LAYOUT_SELECTION_CRITERIA_USER = 0;
+
+ /** Auto-detection based on device provided language tag and layout type */
+ public static final int LAYOUT_SELECTION_CRITERIA_DEVICE = 1;
+
+ /** Auto-detection based on IME provided language tag and layout type */
+ public static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD = 2;
+
+ @VisibleForTesting
+ static final String DEFAULT_LAYOUT = "Default";
+
/**
* Log keyboard system shortcuts for the proto
* {@link com.android.os.input.KeyboardSystemsEventReported}
@@ -43,120 +76,233 @@
*/
public static void logKeyboardSystemsEventReportedAtom(InputDevice inputDevice,
int keyboardSystemEvent, int[] keyCode, int modifierState) {
- int vendor_id = inputDevice.getVendorId();
- int product_id = inputDevice.getProductId();
+ int vendorId = inputDevice.getVendorId();
+ int productId = inputDevice.getProductId();
FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED,
- vendor_id, product_id, keyboardSystemEvent, keyCode, modifierState);
+ vendorId, productId, keyboardSystemEvent, keyCode, modifierState);
}
/**
* Function to log the KeyboardConfigured
* {@link com.android.os.input.KeyboardConfigured} atom
*
- * @param inputDevice Input device
- * @param keyboardLayoutConfigurations List of keyboard configurations
- * @param isFirstTimeConfiguration Whether keyboard is configured for the first time
+ * @param event {@link KeyboardConfigurationEvent} contains information about keyboard
+ * configuration. Use {@link KeyboardConfigurationEvent.Builder} to create the
+ * configuration event to log.
*/
- public static void logKeyboardConfiguredAtom(InputDevice inputDevice,
- List<KeyboardLayoutConfiguration> keyboardLayoutConfigurations,
- boolean isFirstTimeConfiguration) {
- int vendor_id = inputDevice.getVendorId();
- int product_id = inputDevice.getProductId();
-
+ public static void logKeyboardConfiguredAtom(KeyboardConfigurationEvent event) {
// Creating proto to log nested field KeyboardLayoutConfig in atom
ProtoOutputStream proto = new ProtoOutputStream();
- for (KeyboardLayoutConfiguration keyboardLayoutConfiguration :
- keyboardLayoutConfigurations) {
- addKeyboardLayoutConfigurationToProto(proto, keyboardLayoutConfiguration);
+ for (LayoutConfiguration layoutConfiguration : event.getLayoutConfigurations()) {
+ addKeyboardLayoutConfigurationToProto(proto, layoutConfiguration);
}
// Push the atom to Statsd
FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_CONFIGURED,
- isFirstTimeConfiguration, vendor_id, product_id, proto.getBytes());
+ event.isFirstConfiguration(), event.getVendorId(), event.getProductId(),
+ proto.getBytes());
+
+ if (DEBUG) {
+ Slog.d(TAG, "Logging Keyboard configuration event: " + event);
+ }
}
/**
* Populate the KeyboardLayoutConfig proto which is a repeated proto
* in the RepeatedKeyboardLayoutConfig proto with values from the
- * {@link KeyboardLayoutConfiguration} class
+ * {@link LayoutConfiguration} class
* The proto definitions can be found at:
* "frameworks/proto_logging/stats/atoms/input/input_extension_atoms.proto"
*
* @param proto Representing the nested proto RepeatedKeyboardLayoutConfig
- * @param keyboardLayoutConfiguration Class containing the fields for populating the
+ * @param layoutConfiguration Class containing the fields for populating the
* KeyboardLayoutConfig proto
*/
private static void addKeyboardLayoutConfigurationToProto(ProtoOutputStream proto,
- KeyboardLayoutConfiguration keyboardLayoutConfiguration) {
+ LayoutConfiguration layoutConfiguration) {
// Start a new KeyboardLayoutConfig proto.
long keyboardLayoutConfigToken = proto.start(
RepeatedKeyboardLayoutConfig.KEYBOARD_LAYOUT_CONFIG);
proto.write(KeyboardLayoutConfig.KEYBOARD_LANGUAGE_TAG,
- keyboardLayoutConfiguration.getKeyboardLanguageTag());
+ layoutConfiguration.keyboardLanguageTag);
proto.write(KeyboardLayoutConfig.KEYBOARD_LAYOUT_TYPE,
- keyboardLayoutConfiguration.getKeyboardLayoutType());
+ layoutConfiguration.keyboardLayoutType);
proto.write(KeyboardLayoutConfig.KEYBOARD_LAYOUT_NAME,
- keyboardLayoutConfiguration.getKeyboardLayoutName());
+ layoutConfiguration.keyboardLayoutName);
proto.write(KeyboardLayoutConfig.LAYOUT_SELECTION_CRITERIA,
- keyboardLayoutConfiguration.getLayoutSelectionCriteria());
+ layoutConfiguration.layoutSelectionCriteria);
proto.end(keyboardLayoutConfigToken);
}
/**
- * Java class representing the proto KeyboardLayoutConfig defined in
- * "frameworks/proto_logging/stats/atoms/input/input_extension_atoms.proto"
+ * Class representing the proto KeyboardLayoutConfig defined in
+ * "frameworks/proto_logging/stats/atoms/input/input_extension_atoms.proto
*
* @see com.android.os.input.KeyboardConfigured
*/
- public static class KeyboardLayoutConfiguration {
- // KeyboardLayoutType in "frameworks/base/core/res/res/values/attrs.xml"
- // contains mapping for enums to int
- int mKeyboardLayoutType;
- String mKeyboardLanguageTag;
- KeyboardLayout mKeyboardLayout;
- @LayoutSelectionCriteria int mLayoutSelectionCriteria;
+ public static class KeyboardConfigurationEvent {
- @Retention(SOURCE)
- @IntDef(prefix = { "LAYOUT_SELECTION_CRITERIA_" }, value = {
- LAYOUT_SELECTION_CRITERIA_USER,
- LAYOUT_SELECTION_CRITERIA_DEVICE,
- LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
- })
- public @interface LayoutSelectionCriteria {}
+ private final InputDevice mInputDevice;
+ private final boolean mIsFirstConfiguration;
+ private final List<LayoutConfiguration> mLayoutConfigurations;
- /** Manual selection by user */
- public static final int LAYOUT_SELECTION_CRITERIA_USER = 0;
-
- /** Auto-detection based on device provided language tag and layout type */
- public static final int LAYOUT_SELECTION_CRITERIA_DEVICE = 1;
-
- /** Auto-detection based on IME provided language tag and layout type */
- public static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD = 2;
-
- KeyboardLayoutConfiguration(int keyboardLayoutType,
- String keyboardLanguageTag,
- KeyboardLayout keyboardLayout,
- @LayoutSelectionCriteria int layoutSelectionCriteria) {
- mKeyboardLayoutType = keyboardLayoutType;
- mKeyboardLanguageTag = keyboardLanguageTag;
- mKeyboardLayout = keyboardLayout;
- mLayoutSelectionCriteria = layoutSelectionCriteria;
- }
- int getKeyboardLayoutType() {
- return mKeyboardLayoutType;
+ private KeyboardConfigurationEvent(InputDevice inputDevice, boolean isFirstConfiguration,
+ List<LayoutConfiguration> layoutConfigurations) {
+ mInputDevice = inputDevice;
+ mIsFirstConfiguration = isFirstConfiguration;
+ mLayoutConfigurations = layoutConfigurations;
}
- String getKeyboardLanguageTag() {
- return mKeyboardLanguageTag;
+ public int getVendorId() {
+ return mInputDevice.getVendorId();
}
- String getKeyboardLayoutName() {
- return mKeyboardLayout.getLabel();
+ public int getProductId() {
+ return mInputDevice.getProductId();
}
- @LayoutSelectionCriteria int getLayoutSelectionCriteria() {
- return mLayoutSelectionCriteria;
+ public boolean isFirstConfiguration() {
+ return mIsFirstConfiguration;
}
+
+ public List<LayoutConfiguration> getLayoutConfigurations() {
+ return mLayoutConfigurations;
+ }
+
+ @Override
+ public String toString() {
+ return "InputDevice = {VendorId = " + Integer.toHexString(getVendorId())
+ + ", ProductId = " + Integer.toHexString(getProductId())
+ + "}, isFirstConfiguration = " + mIsFirstConfiguration
+ + ", LayoutConfigurations = " + mLayoutConfigurations;
+ }
+
+ /**
+ * Builder class to help create {@link KeyboardConfigurationEvent}.
+ */
+ public static class Builder {
+ @NonNull
+ private final InputDevice mInputDevice;
+ private boolean mIsFirstConfiguration;
+ private final List<InputMethodSubtype> mImeSubtypeList = new ArrayList<>();
+ private final List<KeyboardLayout> mSelectedLayoutList = new ArrayList<>();
+ private final List<Integer> mLayoutSelectionCriteriaList = new ArrayList<>();
+
+ public Builder(@NonNull InputDevice inputDevice) {
+ Objects.requireNonNull(inputDevice, "InputDevice provided should not be null");
+ mInputDevice = inputDevice;
+ }
+
+ /**
+ * Set whether this is the first time this keyboard is configured.
+ */
+ public Builder setIsFirstTimeConfiguration(boolean isFirstTimeConfiguration) {
+ mIsFirstConfiguration = isFirstTimeConfiguration;
+ return this;
+ }
+
+ /**
+ * Adds keyboard layout configuration info for a particular IME subtype language
+ */
+ public Builder addLayoutSelection(@NonNull InputMethodSubtype imeSubtype,
+ @Nullable KeyboardLayout selectedLayout,
+ @LayoutSelectionCriteria int layoutSelectionCriteria) {
+ Objects.requireNonNull(imeSubtype, "IME subtype provided should not be null");
+ if (!isValidSelectionCriteria(layoutSelectionCriteria)) {
+ throw new IllegalStateException("Invalid layout selection criteria");
+ }
+ mImeSubtypeList.add(imeSubtype);
+ mSelectedLayoutList.add(selectedLayout);
+ mLayoutSelectionCriteriaList.add(layoutSelectionCriteria);
+ return this;
+ }
+
+ /**
+ * Creates {@link KeyboardConfigurationEvent} from the provided information
+ */
+ public KeyboardConfigurationEvent build() {
+ int size = mImeSubtypeList.size();
+ if (size == 0) {
+ throw new IllegalStateException("Should have at least one configuration");
+ }
+ List<LayoutConfiguration> configurationList = new ArrayList<>();
+ for (int i = 0; i < size; i++) {
+ KeyboardLayout selectedLayout = mSelectedLayoutList.get(i);
+ @LayoutSelectionCriteria int layoutSelectionCriteria =
+ mLayoutSelectionCriteriaList.get(i);
+ InputMethodSubtype imeSubtype = mImeSubtypeList.get(i);
+ String keyboardLanguageTag;
+ String keyboardLayoutStringType;
+ if (layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEVICE) {
+ keyboardLanguageTag = mInputDevice.getKeyboardLanguageTag();
+ keyboardLayoutStringType = mInputDevice.getKeyboardLayoutType();
+ } else {
+ ULocale pkLocale = imeSubtype.getPhysicalKeyboardHintLanguageTag();
+ keyboardLanguageTag = pkLocale != null ? pkLocale.toLanguageTag()
+ : imeSubtype.getCanonicalizedLanguageTag();
+ keyboardLayoutStringType = imeSubtype.getPhysicalKeyboardHintLayoutType();
+ }
+ // Sanitize null values
+ String keyboardLayoutName =
+ selectedLayout == null ? DEFAULT_LAYOUT : selectedLayout.getLabel();
+ keyboardLanguageTag = keyboardLanguageTag == null ? "" : keyboardLanguageTag;
+ int keyboardLayoutType = KeyboardLayout.LayoutType.getLayoutTypeEnumValue(
+ keyboardLayoutStringType);
+
+ configurationList.add(
+ new LayoutConfiguration(keyboardLayoutType, keyboardLanguageTag,
+ keyboardLayoutName, layoutSelectionCriteria));
+ }
+ return new KeyboardConfigurationEvent(mInputDevice, mIsFirstConfiguration,
+ configurationList);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ static class LayoutConfiguration {
+ // This should match enum values defined in "frameworks/base/core/res/res/values/attrs.xml"
+ public final int keyboardLayoutType;
+ public final String keyboardLanguageTag;
+ public final String keyboardLayoutName;
+ @LayoutSelectionCriteria
+ public final int layoutSelectionCriteria;
+
+ private LayoutConfiguration(int keyboardLayoutType, String keyboardLanguageTag,
+ String keyboardLayoutName, @LayoutSelectionCriteria int layoutSelectionCriteria) {
+ this.keyboardLayoutType = keyboardLayoutType;
+ this.keyboardLanguageTag = keyboardLanguageTag;
+ this.keyboardLayoutName = keyboardLayoutName;
+ this.layoutSelectionCriteria = layoutSelectionCriteria;
+ }
+
+ @Override
+ public String toString() {
+ return "{keyboardLanguageTag = " + keyboardLanguageTag + " keyboardLayoutType = "
+ + KeyboardLayout.LayoutType.getLayoutNameFromValue(keyboardLayoutType)
+ + " keyboardLayoutName = " + keyboardLayoutName + " layoutSelectionCriteria = "
+ + getStringForSelectionCriteria(layoutSelectionCriteria) + "}";
+ }
+ }
+
+ private static String getStringForSelectionCriteria(
+ @LayoutSelectionCriteria int layoutSelectionCriteria) {
+ switch (layoutSelectionCriteria) {
+ case LAYOUT_SELECTION_CRITERIA_USER:
+ return "LAYOUT_SELECTION_CRITERIA_USER";
+ case LAYOUT_SELECTION_CRITERIA_DEVICE:
+ return "LAYOUT_SELECTION_CRITERIA_DEVICE";
+ case LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD:
+ return "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD";
+ default:
+ return "INVALID_CRITERIA";
+ }
+ }
+
+ private static boolean isValidSelectionCriteria(int layoutSelectionCriteria) {
+ return layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_USER
+ || layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEVICE
+ || layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
}
}
diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java
index bce210d..31083fd 100644
--- a/services/core/java/com/android/server/input/PersistentDataStore.java
+++ b/services/core/java/com/android/server/input/PersistentDataStore.java
@@ -101,6 +101,10 @@
}
}
+ public boolean hasInputDeviceEntry(String inputDeviceDescriptor) {
+ return getInputDeviceState(inputDeviceDescriptor) != null;
+ }
+
public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
InputDeviceState state = getInputDeviceState(inputDeviceDescriptor);
if (state == null) {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 2e62ef4..38f153b 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -41,6 +41,7 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE;
+import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYPE_STRONG;
@@ -278,8 +279,6 @@
protected IGateKeeperService mGateKeeperService;
protected IAuthSecret mAuthSecretService;
- private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
-
/**
* The UIDs that are used for system credential storage in keystore.
*/
@@ -311,6 +310,7 @@
if (phase == PHASE_ACTIVITY_MANAGER_READY) {
mLockSettingsService.migrateOldDataAfterSystemReady();
mLockSettingsService.loadEscrowData();
+ mLockSettingsService.deleteRepairModePersistentDataIfNeeded();
}
}
@@ -541,7 +541,7 @@
}
public boolean isGsiRunning() {
- return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
+ return LockPatternUtils.isGsiRunning();
}
public FingerprintManager getFingerprintManager() {
@@ -949,6 +949,16 @@
return success;
}
+ @VisibleForTesting
+ void deleteRepairModePersistentDataIfNeeded() {
+ if (!LockPatternUtils.isRepairModeSupported(mContext)
+ || LockPatternUtils.isRepairModeActive(mContext)
+ || mInjector.isGsiRunning()) {
+ return;
+ }
+ mStorage.deleteRepairModePersistentData();
+ }
+
// This is called when Weaver is guaranteed to be available (if the device supports Weaver).
// It does any synthetic password related work that was delayed from earlier in the boot.
private void onThirdPartyAppsStarted() {
@@ -2202,6 +2212,12 @@
response = authResult.gkResponse;
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
+ if ((flags & VERIFY_FLAG_WRITE_REPAIR_MODE_PW) != 0) {
+ if (!mSpManager.writeRepairModeCredentialLocked(protectorId, userId)) {
+ Slog.e(TAG, "Failed to write repair mode credential");
+ return VerifyCredentialResponse.ERROR;
+ }
+ }
// credential has matched
mBiometricDeferredQueue.addPendingLockoutResetForUser(userId,
authResult.syntheticPassword.deriveGkPassword());
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 2fa637e..1ace6e5 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -90,6 +90,9 @@
private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/";
+ private static final String REPAIR_MODE_DIRECTORY = "repair-mode/";
+ private static final String REPAIR_MODE_PERSISTENT_FILE = "pst";
+
private static final Object DEFAULT = new Object();
private static final String[] SETTINGS_TO_BACKUP = new String[] {
@@ -390,6 +393,29 @@
}
}
+ @VisibleForTesting
+ File getRepairModePersistentDataFile() {
+ final File directory = new File(Environment.getMetadataDirectory(), REPAIR_MODE_DIRECTORY);
+ return new File(directory, REPAIR_MODE_PERSISTENT_FILE);
+ }
+
+ public PersistentData readRepairModePersistentData() {
+ final byte[] data = readFile(getRepairModePersistentDataFile());
+ if (data == null) {
+ return PersistentData.NONE;
+ }
+ return PersistentData.fromBytes(data);
+ }
+
+ public void writeRepairModePersistentData(int persistentType, int userId, byte[] payload) {
+ writeFile(getRepairModePersistentDataFile(),
+ PersistentData.toBytes(persistentType, userId, /* qualityForUi= */0, payload));
+ }
+
+ public void deleteRepairModePersistentData() {
+ deleteFile(getRepairModePersistentDataFile());
+ }
+
/**
* Writes the synthetic password state file for the given user ID, protector ID, and state name.
* If the file already exists, then it is atomically replaced.
@@ -583,6 +609,17 @@
}
}
+ /**
+ * Provides a concrete data structure to represent the minimal information from
+ * a user's LSKF-based SP protector that is needed to verify the user's LSKF,
+ * in combination with the corresponding Gatekeeper enrollment or Weaver slot.
+ * It can be stored in {@link com.android.server.PersistentDataBlockService} for
+ * FRP to live across factory resets not initiated via the Settings UI.
+ * Written to {@link #REPAIR_MODE_PERSISTENT_FILE} to support verification for
+ * exiting repair mode, since the device runs with an empty data partition in
+ * repair mode and the same credential be provided to exit repair mode is
+ * required.
+ */
public static class PersistentData {
static final byte VERSION_1 = 1;
static final int VERSION_1_HEADER_SIZE = 1 + 1 + 4 + 4;
@@ -685,6 +722,19 @@
}
pw.decreaseIndent();
}
+ // Dump repair mode file states
+ final File repairModeFile = getRepairModePersistentDataFile();
+ if (repairModeFile.exists()) {
+ pw.println(TextUtils.formatSimple("Repair Mode [%s]:", repairModeFile.getParent()));
+ pw.increaseIndent();
+ pw.println(TextUtils.formatSimple("%6d %s %s", repairModeFile.length(),
+ LockSettingsService.timestampToString(repairModeFile.lastModified()),
+ repairModeFile.getName()));
+ final PersistentData data = readRepairModePersistentData();
+ pw.println(TextUtils.formatSimple("type: %d, user id: %d, payload size: %d",
+ data.type, data.userId, data.payload != null ? data.payload.length : 0));
+ pw.decreaseIndent();
+ }
}
static class DatabaseHelper extends SQLiteOpenHelper {
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 66f862a..d74d304 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -1114,6 +1114,57 @@
}
}
+ /**
+ * Writes the user's synthetic password data to the repair mode file.
+ *
+ * @param protectorId current LSKF based protectorId
+ * @param userId user id of the user
+ */
+ public boolean writeRepairModeCredentialLocked(long protectorId, int userId) {
+ if (!shouldWriteRepairModeCredential(userId)) {
+ return false;
+ }
+ final byte[] data = loadState(PASSWORD_DATA_NAME, protectorId, userId);
+ if (data == null) {
+ Slogf.w(TAG, "Password data not found for user %d", userId);
+ return false;
+ }
+ final PasswordData pwd = PasswordData.fromBytes(data);
+ if (isNoneCredential(pwd)) {
+ Slogf.w(TAG, "User %d has NONE credential", userId);
+ return false;
+ }
+ Slogf.d(TAG, "Writing repair mode credential tied to user %d", userId);
+ final int weaverSlot = loadWeaverSlot(protectorId, userId);
+ if (weaverSlot != INVALID_WEAVER_SLOT) {
+ // write weaver password
+ mStorage.writeRepairModePersistentData(
+ PersistentData.TYPE_SP_WEAVER, weaverSlot, pwd.toBytes());
+ } else {
+ // write gatekeeper password
+ mStorage.writeRepairModePersistentData(
+ PersistentData.TYPE_SP_GATEKEEPER, userId, pwd.toBytes());
+ }
+ return true;
+ }
+
+ private boolean shouldWriteRepairModeCredential(int userId) {
+ final UserInfo userInfo = mUserManager.getUserInfo(userId);
+ if (!LockPatternUtils.canUserEnterRepairMode(mContext, userInfo)) {
+ Slogf.w(TAG, "User %d can't enter repair mode", userId);
+ return false;
+ }
+ if (LockPatternUtils.isRepairModeActive(mContext)) {
+ Slog.w(TAG, "Can't write repair mode credential while repair mode is already active");
+ return false;
+ }
+ if (LockPatternUtils.isGsiRunning()) {
+ Slog.w(TAG, "Can't write repair mode credential while GSI is running");
+ return false;
+ }
+ return true;
+ }
+
private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
/**
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 695a0cf..a53b831 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -283,7 +283,7 @@
private static final long ENHANCED_DISCHARGE_PREDICTION_BROADCAST_MIN_DELAY_MS = 60 * 1000L;
/**
- * Apps targeting Android U and above need to define
+ * Apps targeting Android V and above need to define
* {@link android.Manifest.permission#TURN_SCREEN_ON} in their manifest for
* {@link android.os.PowerManager#ACQUIRE_CAUSES_WAKEUP} to have any effect.
* Note that most applications should use {@link android.R.attr#turnScreenOn} or
@@ -291,7 +291,7 @@
* previous foreground app from being resumed first when the screen turns on.
*/
@ChangeId
- @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT)
public static final long REQUIRE_TURN_SCREEN_ON_PERMISSION = 216114297L;
/** Reason ID for holding display suspend blocker. */
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index b4613a7..efb3622 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -171,7 +171,7 @@
true, /* disableAod */
true, /* disableLaunchBoost */
true, /* disableOptionalSensors */
- true, /* disableVibration */
+ false, /* disableVibration */
false, /* enableAdjustBrightness */
false, /* enableDataSaver */
true, /* enableFirewall */
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index c38bfd7..322083c 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -366,6 +366,7 @@
if (lockedWallpaper != null) {
detachWallpaperLocked(lockedWallpaper);
}
+ clearWallpaperBitmaps(mWallpaper.userId, FLAG_LOCK);
mLockWallpaperMap.remove(wallpaper.userId);
notifyColorsWhich |= FLAG_LOCK;
}
@@ -1991,7 +1992,11 @@
WallpaperData data = null;
synchronized (mLock) {
- clearWallpaperLocked(false, which, userId, null);
+ if (mIsLockscreenLiveWallpaperEnabled) {
+ clearWallpaperLocked(callingPackage, false, which, userId);
+ } else {
+ clearWallpaperLocked(false, which, userId, null);
+ }
if (which == FLAG_LOCK) {
data = mLockWallpaperMap.get(userId);
@@ -2008,7 +2013,64 @@
}
}
- void clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply) {
+ private void clearWallpaperLocked(String callingPackage, boolean defaultFailed,
+ int which, int userId) {
+
+ // Might need to bring it in the first time to establish our rewrite
+ if (!mWallpaperMap.contains(userId)) {
+ loadSettingsLocked(userId, false, FLAG_LOCK | FLAG_SYSTEM);
+ }
+ final WallpaperData wallpaper = mWallpaperMap.get(userId);
+ final WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
+ if (which == FLAG_LOCK && lockWallpaper == null) {
+ // It's already gone; we're done.
+ if (DEBUG) {
+ Slog.i(TAG, "Lock wallpaper already cleared");
+ }
+ return;
+ }
+
+ RuntimeException e = null;
+ try {
+ if (userId != mCurrentUserId && !hasCrossUserPermission()) return;
+
+ final ComponentName component;
+ final int finalWhich;
+
+ if ((which & FLAG_LOCK) > 0 && lockWallpaper != null) {
+ clearWallpaperBitmaps(lockWallpaper);
+ }
+ if ((which & FLAG_SYSTEM) > 0) {
+ clearWallpaperBitmaps(wallpaper);
+ }
+
+ // lock only case: set the system wallpaper component to both screens
+ if (which == FLAG_LOCK) {
+ component = wallpaper.wallpaperComponent;
+ finalWhich = FLAG_LOCK | FLAG_SYSTEM;
+ } else {
+ component = defaultFailed ? mImageWallpaper : null;
+ finalWhich = which;
+ }
+
+ boolean success = withCleanCallingIdentity(() -> setWallpaperComponent(
+ component, callingPackage, finalWhich, userId));
+ if (success) return;
+ } catch (IllegalArgumentException e1) {
+ e = e1;
+ }
+
+ // This can happen if the default wallpaper component doesn't
+ // exist. This should be a system configuration problem, but
+ // let's not let it crash the system and just live with no
+ // wallpaper.
+ Slog.e(TAG, "Default wallpaper component not found!", e);
+ withCleanCallingIdentity(() -> clearWallpaperComponentLocked(wallpaper));
+ }
+
+ // TODO(b/266818039) remove this version of the method
+ private void clearWallpaperLocked(boolean defaultFailed, int which, int userId,
+ IRemoteCallback reply) {
if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear");
}
@@ -3099,8 +3161,13 @@
// Migrate the bitmap files outright; no need to copy
try {
- Os.rename(sysWP.wallpaperFile.getAbsolutePath(), lockWP.wallpaperFile.getAbsolutePath());
- Os.rename(sysWP.cropFile.getAbsolutePath(), lockWP.cropFile.getAbsolutePath());
+ if (!mIsLockscreenLiveWallpaperEnabled || sysWP.wallpaperFile.exists()) {
+ Os.rename(sysWP.wallpaperFile.getAbsolutePath(),
+ lockWP.wallpaperFile.getAbsolutePath());
+ }
+ if (!mIsLockscreenLiveWallpaperEnabled || sysWP.cropFile.exists()) {
+ Os.rename(sysWP.cropFile.getAbsolutePath(), lockWP.cropFile.getAbsolutePath());
+ }
mLockWallpaperMap.put(userId, lockWP);
if (mIsLockscreenLiveWallpaperEnabled) {
SELinux.restorecon(lockWP.wallpaperFile);
@@ -3163,16 +3230,17 @@
}
@VisibleForTesting
- void setWallpaperComponent(ComponentName name, String callingPackage,
+ boolean setWallpaperComponent(ComponentName name, String callingPackage,
@SetWallpaperFlags int which, int userId) {
if (mIsLockscreenLiveWallpaperEnabled) {
- setWallpaperComponentInternal(name, callingPackage, which, userId);
+ return setWallpaperComponentInternal(name, callingPackage, which, userId);
} else {
setWallpaperComponentInternalLegacy(name, callingPackage, which, userId);
+ return true;
}
}
- private void setWallpaperComponentInternal(ComponentName name, String callingPackage,
+ private boolean setWallpaperComponentInternal(ComponentName name, String callingPackage,
@SetWallpaperFlags int which, int userIdIn) {
if (DEBUG) {
Slog.v(TAG, "Setting new live wallpaper: which=" + which + ", component: " + name);
@@ -3183,6 +3251,7 @@
checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
boolean shouldNotifyColors = false;
+ boolean bindSuccess;
final WallpaperData newWallpaper;
synchronized (mLock) {
@@ -3231,7 +3300,7 @@
*/
boolean forceRebind = same && systemIsBoth && which == FLAG_SYSTEM;
- boolean bindSuccess = bindWallpaperComponentLocked(name, /* force */
+ bindSuccess = bindWallpaperComponentLocked(name, /* force */
forceRebind, /* fromUser */ true, newWallpaper, callback);
if (bindSuccess) {
if (!same) {
@@ -3250,8 +3319,10 @@
});
}
}
+ boolean lockBitmapCleared = false;
if (!mImageWallpaper.equals(newWallpaper.wallpaperComponent)) {
clearWallpaperBitmaps(newWallpaper);
+ lockBitmapCleared = newWallpaper.mWhich == FLAG_LOCK;
}
newWallpaper.wallpaperId = makeWallpaperIdLocked();
notifyCallbacksLocked(newWallpaper);
@@ -3269,6 +3340,9 @@
updateEngineFlags(newWallpaper);
}
}
+ if (!lockBitmapCleared) {
+ clearWallpaperBitmaps(newWallpaper.userId, FLAG_LOCK);
+ }
mLockWallpaperMap.remove(newWallpaper.userId);
}
}
@@ -3281,6 +3355,7 @@
notifyWallpaperColorsChanged(newWallpaper, which);
notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
}
+ return bindSuccess;
}
// TODO(b/266818039) Remove this method
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9f16a844..788bfbc 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -223,6 +223,7 @@
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
+import static com.android.server.wm.LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
@@ -2425,7 +2426,7 @@
final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
allowTaskSnapshot, activityCreated, activityAllDrawn, snapshot);
- //TODO(191787740) Remove for T+
+ //TODO(191787740) Remove for V+
final boolean useLegacy = type == STARTING_WINDOW_TYPE_SPLASH_SCREEN
&& mWmService.mStartingSurfaceController.isExceptionApp(packageName, mTargetSdk,
() -> {
@@ -4550,6 +4551,13 @@
* immediately finishes after, so we have to transfer T to M.
*/
void transferStartingWindowFromHiddenAboveTokenIfNeeded() {
+ final WindowState mainWin = findMainWindow(false);
+ if (mainWin != null && mainWin.mWinAnimator.getShown()) {
+ // This activity already has a visible window, so doesn't need to transfer the starting
+ // window from above activity to here. The starting window will be removed with above
+ // activity.
+ return;
+ }
task.forAllActivities(fromActivity -> {
if (fromActivity == this) return true;
return !fromActivity.isVisibleRequested() && transferStartingWindow(fromActivity);
@@ -8294,6 +8302,8 @@
}
mIsAspectRatioApplied = false;
+ mIsEligibleForFixedOrientationLetterbox = false;
+ mLetterboxBoundsForFixedOrientationAndAspectRatio = null;
// Can't use resolvedConfig.windowConfiguration.getWindowingMode() because it can be
// different from windowing mode of the task (PiP) during transition from fullscreen to PiP
@@ -8303,8 +8313,11 @@
final boolean isFixedOrientationLetterboxAllowed =
parentWindowingMode == WINDOWING_MODE_MULTI_WINDOW
|| parentWindowingMode == WINDOWING_MODE_FULLSCREEN
- // Switching from PiP to fullscreen.
- || (parentWindowingMode == WINDOWING_MODE_PINNED
+ // When starting to switch between PiP and fullscreen, the task is pinned
+ // and the activity is fullscreen. But only allow to apply letterbox if the
+ // activity is exiting PiP because an entered PiP should fill the task.
+ || (!mWaitForEnteringPinnedMode
+ && parentWindowingMode == WINDOWING_MODE_PINNED
&& resolvedConfig.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN);
// TODO(b/181207944): Consider removing the if condition and always run
@@ -8698,8 +8711,6 @@
* in this method.
*/
private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig) {
- mLetterboxBoundsForFixedOrientationAndAspectRatio = null;
- mIsEligibleForFixedOrientationLetterbox = false;
final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
final Rect stableBounds = new Rect();
// If orientation is respected when insets are applied, then stableBounds will be empty.
@@ -8784,9 +8795,18 @@
final float letterboxAspectRatioOverride =
mLetterboxUiController.getFixedOrientationLetterboxAspectRatio(newParentConfig);
- final float desiredAspectRatio =
- letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
- ? letterboxAspectRatioOverride : computeAspectRatio(parentBounds);
+
+ // Aspect ratio as suggested by the system. Apps requested mix/max aspect ratio will
+ // be respected in #applyAspectRatio.
+ final float desiredAspectRatio;
+ if (isDefaultMultiWindowLetterboxAspectRatioDesired(newParentConfig)) {
+ desiredAspectRatio = DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
+ } else if (letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) {
+ desiredAspectRatio = letterboxAspectRatioOverride;
+ } else {
+ desiredAspectRatio = computeAspectRatio(parentBounds);
+ }
+
// Apply aspect ratio to resolved bounds
mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingBoundsWithInsets,
containingBounds, desiredAspectRatio);
@@ -8812,6 +8832,20 @@
}
/**
+ * Returns {@code true} if the default aspect ratio for a letterboxed app in multi-window mode
+ * should be used.
+ */
+ private boolean isDefaultMultiWindowLetterboxAspectRatioDesired(
+ @NonNull Configuration parentConfig) {
+ if (mDisplayContent == null) {
+ return false;
+ }
+ final int windowingMode = parentConfig.windowConfiguration.getWindowingMode();
+ return WindowConfiguration.inMultiWindowMode(windowingMode)
+ && !mDisplayContent.getIgnoreOrientationRequest();
+ }
+
+ /**
* Resolves aspect ratio restrictions for an activity. If the bounds are restricted by
* aspect ratio, the position will be adjusted later in {@link #updateResolvedBoundsPosition
* within parent's app bounds to balance the visual appearance. The policy of aspect ratio has
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 3c97672..738797b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1891,7 +1891,7 @@
// DestroyActivityItem may be called first.
final ActivityRecord top = task.getTopMostActivity();
if (top != null && top.finishing && !top.mAppStopped && top.lastVisibleTime > 0
- && !task.mKillProcessesOnDestroyed) {
+ && !task.mKillProcessesOnDestroyed && top.hasProcess()) {
task.mKillProcessesOnDestroyed = true;
mHandler.sendMessageDelayed(
mHandler.obtainMessage(KILL_TASK_PROCESSES_TIMEOUT_MSG, task),
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7fc86b0..ef19eef 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -244,6 +244,7 @@
import android.window.DisplayWindowPolicyController;
import android.window.IDisplayAreaOrganizer;
import android.window.ScreenCapture;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import android.window.TransitionRequestInfo;
import com.android.internal.R;
@@ -5088,7 +5089,7 @@
return null;
}
- Pair<ScreenCapture.ScreenCaptureListener, ScreenCapture.ScreenshotSync> syncScreenCapture =
+ SynchronousScreenCaptureListener syncScreenCapture =
ScreenCapture.createSyncCaptureListener();
getBounds(mTmpRect);
@@ -5097,10 +5098,10 @@
new ScreenCapture.LayerCaptureArgs.Builder(getSurfaceControl())
.setSourceCrop(mTmpRect).build();
- ScreenCapture.captureLayers(args, syncScreenCapture.first);
+ ScreenCapture.captureLayers(args, syncScreenCapture);
final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
- syncScreenCapture.second.get();
+ syncScreenCapture.getBuffer();
final Bitmap bitmap = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
if (bitmap == null) {
Slog.w(TAG_WM, "Failed to take screenshot");
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index a93cb8ad..067a18c 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -52,6 +52,9 @@
*/
static final float MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO = 1.0f;
+ /** The default aspect ratio for a letterboxed app in multi-window mode. */
+ static final float DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW = 1.01f;
+
/** Letterboxed app window position multiplier indicating center position. */
static final float LETTERBOX_POSITION_MULTIPLIER_CENTER = 0.5f;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index bc7fa3112..54fec3e 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2013,7 +2013,6 @@
// from doing work and changing the activity visuals while animating
// TODO(task-org): Figure-out more structured way to do this long term.
r.setWindowingMode(r.getWindowingMode());
- r.mWaitForEnteringPinnedMode = true;
final TaskFragment organizedTf = r.getOrganizedTaskFragment();
final boolean singleActivity = task.getNonFinishingActivityCount() == 1;
@@ -2140,6 +2139,10 @@
}
rootTask.setDeferTaskAppear(false);
+ // After setting this, it is not expected to change activity configuration until the
+ // transition animation is finished. So the activity can keep consistent appearance
+ // when animating.
+ r.mWaitForEnteringPinnedMode = true;
// Reset the state that indicates it can enter PiP while pausing after we've moved it
// to the root pinned task
r.supportsEnterPipOnTaskSwitch = false;
@@ -3310,9 +3313,14 @@
if (aOptions != null) {
// Resolve the root task the task should be placed in now based on options
// and reparent if needed.
+ // TODO(b/229927851) For split-screen, setLaunchRootTask is no longer the "root"
+ // task, consider to rename methods like "parentTask" instead of "rootTask".
final Task targetRootTask =
getOrCreateRootTask(null, aOptions, task, onTop);
- if (targetRootTask != null && task.getRootTask() != targetRootTask) {
+ // When launch with ActivityOptions#getLaunchRootTask, the "root task" just mean the
+ // parent of current launch, not the "root task" in hierarchy.
+ if (targetRootTask != null && task.getRootTask() != targetRootTask
+ && task.getParent() != targetRootTask) {
final int reparentMode = onTop
? REPARENT_MOVE_ROOT_TASK_TO_FRONT : REPARENT_LEAVE_ROOT_TASK_IN_PLACE;
task.reparent(targetRootTask, onTop, reparentMode, ANIMATE, DEFER_RESUME,
diff --git a/services/core/java/com/android/server/wm/SplashScreenExceptionList.java b/services/core/java/com/android/server/wm/SplashScreenExceptionList.java
index b3cd3f0..3fb897b 100644
--- a/services/core/java/com/android/server/wm/SplashScreenExceptionList.java
+++ b/services/core/java/com/android/server/wm/SplashScreenExceptionList.java
@@ -70,7 +70,7 @@
}
/**
- * Returns true if the packageName is in the list and the target sdk is before or including T.
+ * Returns true if the packageName is in the list and the target sdk is before or including V.
*
* @param packageName The package name of the application to check
* @param targetSdk The target sdk of the application
@@ -82,7 +82,7 @@
@SuppressWarnings("AndroidFrameworkCompatChange") // Target sdk check
public boolean isException(@NonNull String packageName, int targetSdk,
@Nullable Supplier<ApplicationInfo> infoSupplier) {
- if (targetSdk > Build.VERSION_CODES.TIRAMISU) {
+ if (targetSdk > Build.VERSION_CODES.UPSIDE_DOWN_CAKE + 1) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index bfdf84e..39772dda 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3451,6 +3451,7 @@
: INVALID_TASK_ID;
info.isFocused = isFocused();
info.isVisible = hasVisibleChildren();
+ info.isVisibleRequested = isVisibleRequested();
info.isSleeping = shouldSleepActivities();
info.isLetterboxDoubleTapEnabled = top != null
&& top.mLetterboxUiController.isLetterboxDoubleTapEducationEnabled();
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 3672820..d7d2b4e 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -563,7 +563,7 @@
return;
}
- final long diff = lastLaunchTime - launchTime;
+ final long diff = launchTime - lastLaunchTime;
if (diff < RAPID_ACTIVITY_LAUNCH_MS) {
mRapidActivityLaunchCount++;
} else if (diff >= RESET_RAPID_ACTIVITY_LAUNCH_MS) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b52935e..a1473b1 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3887,15 +3887,13 @@
}
/**
- * @return {@code true} if activity bounds are letterboxed or letterboxed for display cutout.
- * Note that it's always {@code false} if the activity is in pip mode.
+ * Returns {@code true} if activity bounds are letterboxed or letterboxed for display cutout.
*
* <p>Note that letterbox UI may not be shown even when this returns {@code true}. See {@link
* LetterboxUiController#shouldShowLetterboxUi} for more context.
*/
boolean areAppWindowBoundsLetterboxed() {
return mActivityRecord != null
- && !mActivityRecord.inPinnedWindowingMode()
&& (mActivityRecord.areBoundsLetterboxed() || isLetterboxedForDisplayCutout());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index ccaaa87..f4238f6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -1094,6 +1094,17 @@
verifyPendingRecords(greenQueue, List.of(screenOff, screenOn));
verifyPendingRecords(redQueue, List.of(screenOff));
verifyPendingRecords(blueQueue, List.of(screenOff, screenOn));
+
+ final BroadcastRecord screenOffRecord = makeBroadcastRecord(screenOff, screenOnOffOptions,
+ List.of(greenReceiver, redReceiver, blueReceiver), resultTo, false);
+ screenOffRecord.setDeliveryState(2, BroadcastRecord.DELIVERY_DEFERRED,
+ "testDeliveryGroupPolicy_resultTo_diffReceivers");
+ mImpl.enqueueBroadcastLocked(screenOffRecord);
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions,
+ List.of(greenReceiver, blueReceiver), resultTo, false));
+ verifyPendingRecords(greenQueue, List.of(screenOff, screenOn));
+ verifyPendingRecords(redQueue, List.of(screenOff));
+ verifyPendingRecords(blueQueue, List.of(screenOn));
}
@Test
@@ -1279,6 +1290,36 @@
}
@Test
+ public void testDeliveryGroupPolicy_merged_multipleReceivers() {
+ final long now = SystemClock.elapsedRealtime();
+ final Pair<Intent, BroadcastOptions> dropboxEntryBroadcast1 = createDropboxBroadcast(
+ "TAG_A", now, 2);
+ final Pair<Intent, BroadcastOptions> dropboxEntryBroadcast2 = createDropboxBroadcast(
+ "TAG_A", now + 1000, 4);
+
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast1.first,
+ dropboxEntryBroadcast1.second,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_RED, CLASS_RED)),
+ false));
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast2.first,
+ dropboxEntryBroadcast2.second,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_RED, CLASS_RED)),
+ false));
+
+ final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED,
+ getUidForPackage(PACKAGE_RED));
+
+ verifyPendingRecords(greenQueue,
+ List.of(dropboxEntryBroadcast1.first, dropboxEntryBroadcast2.first));
+ verifyPendingRecords(redQueue,
+ List.of(dropboxEntryBroadcast1.first, dropboxEntryBroadcast2.first));
+ }
+
+ @Test
public void testDeliveryGroupPolicy_sameAction_differentMatchingCriteria() {
final Intent closeSystemDialogs1 = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
final BroadcastOptions optionsCloseSystemDialog1 = BroadcastOptions.makeBasic()
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index efd82fa..4f98dca 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -646,14 +646,16 @@
@Test
public void testSetScreenOffBrightnessSensorEnabled_DisplayIsInDoze() {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+
Settings.System.putInt(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
DisplayPowerRequest dpr = new DisplayPowerRequest();
dpr.policy = DisplayPowerRequest.POLICY_DOZE;
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
advanceTime(1); // Run updatePowerState
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
index 31a58cd..d1d6e9d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java
@@ -36,7 +36,6 @@
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
-import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -73,7 +72,7 @@
private SensorProps[] mSensorProps;
private LockoutResetDispatcher mLockoutResetDispatcher;
- private TestableFaceProvider mFaceProvider;
+ private FaceProvider mFaceProvider;
private static void waitForIdle() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -98,8 +97,9 @@
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
- mFaceProvider = new TestableFaceProvider(mDaemon, mContext, mBiometricStateCallback,
- mSensorProps, TAG, mLockoutResetDispatcher, mBiometricContext);
+ mFaceProvider = new FaceProvider(mContext, mBiometricStateCallback,
+ mSensorProps, TAG, mLockoutResetDispatcher, mBiometricContext,
+ mDaemon);
}
@Test
@@ -130,6 +130,7 @@
for (SensorProps prop : mSensorProps) {
final BiometricScheduler scheduler =
mFaceProvider.mFaceSensors.get(prop.commonProps.sensorId).getScheduler();
+ scheduler.reset();
for (int i = 0; i < numFakeOperations; i++) {
final HalClientMonitor testMonitor = mock(HalClientMonitor.class);
when(testMonitor.getFreshDaemon()).thenReturn(new Object());
@@ -142,7 +143,7 @@
for (SensorProps prop : mSensorProps) {
final BiometricScheduler scheduler =
mFaceProvider.mFaceSensors.get(prop.commonProps.sensorId).getScheduler();
- assertEquals(numFakeOperations, scheduler.getCurrentPendingCount());
+ assertEquals(numFakeOperations - 1, scheduler.getCurrentPendingCount());
assertNotNull(scheduler.getCurrentClient());
}
@@ -159,25 +160,4 @@
assertEquals(0, scheduler.getCurrentPendingCount());
}
}
-
- private static class TestableFaceProvider extends FaceProvider {
- private final IFace mDaemon;
-
- TestableFaceProvider(@NonNull IFace daemon,
- @NonNull Context context,
- @NonNull BiometricStateCallback biometricStateCallback,
- @NonNull SensorProps[] props,
- @NonNull String halInstanceName,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull BiometricContext biometricContext) {
- super(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher,
- biometricContext);
- mDaemon = daemon;
- }
-
- @Override
- synchronized IFace getHalInstance() {
- return mDaemon;
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index 9c01de6..8f6efff 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -39,7 +39,6 @@
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
-import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -81,7 +80,7 @@
private SensorProps[] mSensorProps;
private LockoutResetDispatcher mLockoutResetDispatcher;
- private TestableFingerprintProvider mFingerprintProvider;
+ private FingerprintProvider mFingerprintProvider;
private static void waitForIdle() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -110,17 +109,13 @@
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
- mFingerprintProvider = new TestableFingerprintProvider(mDaemon, mContext,
+ mFingerprintProvider = new FingerprintProvider(mContext,
mBiometricStateCallback, mSensorProps, TAG, mLockoutResetDispatcher,
- mGestureAvailabilityDispatcher, mBiometricContext);
+ mGestureAvailabilityDispatcher, mBiometricContext, mDaemon);
}
@Test
public void testAddingSensors() {
- mFingerprintProvider = new TestableFingerprintProvider(mDaemon, mContext,
- mBiometricStateCallback, mSensorProps, TAG, mLockoutResetDispatcher,
- mGestureAvailabilityDispatcher, mBiometricContext);
-
waitForIdle();
for (SensorProps prop : mSensorProps) {
@@ -147,6 +142,7 @@
final BiometricScheduler scheduler =
mFingerprintProvider.mFingerprintSensors.get(prop.commonProps.sensorId)
.getScheduler();
+ scheduler.reset();
for (int i = 0; i < numFakeOperations; i++) {
final HalClientMonitor testMonitor = mock(HalClientMonitor.class);
when(testMonitor.getFreshDaemon()).thenReturn(new Object());
@@ -160,7 +156,7 @@
final BiometricScheduler scheduler =
mFingerprintProvider.mFingerprintSensors.get(prop.commonProps.sensorId)
.getScheduler();
- assertEquals(numFakeOperations, scheduler.getCurrentPendingCount());
+ assertEquals(numFakeOperations - 1, scheduler.getCurrentPendingCount());
assertNotNull(scheduler.getCurrentClient());
}
@@ -178,26 +174,4 @@
assertEquals(0, scheduler.getCurrentPendingCount());
}
}
-
- private static class TestableFingerprintProvider extends FingerprintProvider {
- private final IFingerprint mDaemon;
-
- TestableFingerprintProvider(@NonNull IFingerprint daemon,
- @NonNull Context context,
- @NonNull BiometricStateCallback biometricStateCallback,
- @NonNull SensorProps[] props,
- @NonNull String halInstanceName,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
- @NonNull BiometricContext biometricContext) {
- super(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher,
- gestureAvailabilityDispatcher, biometricContext);
- mDaemon = daemon;
- }
-
- @Override
- synchronized IFingerprint getHalInstance() {
- return mDaemon;
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/dreams/DreamOverlayServiceTest.java b/services/tests/servicestests/src/com/android/server/dreams/DreamOverlayServiceTest.java
index 851d8f9..f05fa65 100644
--- a/services/tests/servicestests/src/com/android/server/dreams/DreamOverlayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/dreams/DreamOverlayServiceTest.java
@@ -101,12 +101,6 @@
mMonitor.onEndDream();
super.onEndDream();
}
-
- @Override
- public void onWakeUp(@NonNull Runnable onCompleteCallback) {
- mMonitor.onWakeUp();
- super.onWakeUp(onCompleteCallback);
- }
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt b/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
new file mode 100644
index 0000000..c9724a3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
@@ -0,0 +1,179 @@
+/*
+ * 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.server.input
+
+import android.hardware.input.KeyboardLayout
+import android.icu.util.ULocale
+import android.platform.test.annotations.Presubmit
+import android.view.InputDevice
+import android.view.inputmethod.InputMethodSubtype
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+private fun createKeyboard(
+ deviceId: Int,
+ vendorId: Int,
+ productId: Int,
+ languageTag: String?,
+ layoutType: String?
+): InputDevice =
+ InputDevice.Builder()
+ .setId(deviceId)
+ .setName("Device $deviceId")
+ .setDescriptor("descriptor $deviceId")
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC)
+ .setExternal(true)
+ .setVendorId(vendorId)
+ .setProductId(productId)
+ .setKeyboardLanguageTag(languageTag)
+ .setKeyboardLayoutType(layoutType)
+ .build()
+
+private fun createImeSubtype(
+ imeSubtypeId: Int,
+ languageTag: String,
+ layoutType: String
+): InputMethodSubtype =
+ InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(imeSubtypeId)
+ .setPhysicalKeyboardHint(ULocale.forLanguageTag(languageTag), layoutType).build()
+
+/**
+ * Tests for {@link KeyboardMetricsCollector}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:KeyboardMetricsCollectorTests
+ */
+@Presubmit
+class KeyboardMetricsCollectorTests {
+
+ companion object {
+ const val DEVICE_ID = 1
+ const val DEFAULT_VENDOR_ID = 123
+ const val DEFAULT_PRODUCT_ID = 456
+ }
+
+ @Test
+ fun testCreateKeyboardConfigurationEvent_throwsExceptionWithoutAnyLayoutConfiguration() {
+ assertThrows(IllegalStateException::class.java) {
+ KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
+ createKeyboard(
+ DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ null,
+ null
+ )
+ ).build()
+ }
+ }
+
+ @Test
+ fun testCreateKeyboardConfigurationEvent_throwsExceptionWithInvalidLayoutSelectionCriteria() {
+ assertThrows(IllegalStateException::class.java) {
+ KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
+ createKeyboard(
+ DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ null,
+ null
+ )
+ ).addLayoutSelection(createImeSubtype(1, "en-US", "qwerty"), null, 123).build()
+ }
+ }
+
+ @Test
+ fun testCreateKeyboardConfigurationEvent_withMultipleConfigurations() {
+ val builder = KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
+ createKeyboard(
+ DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ "de-CH",
+ "qwertz"
+ )
+ )
+ val event = builder.addLayoutSelection(
+ createImeSubtype(1, "en-US", "qwerty"),
+ KeyboardLayout(null, "English(US)(Qwerty)", null, 0, null, 0, 0, 0),
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
+ ).addLayoutSelection(
+ createImeSubtype(2, "en-US", "azerty"),
+ null,
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER
+ ).addLayoutSelection(
+ createImeSubtype(3, "en-US", "qwerty"),
+ KeyboardLayout(null, "German", null, 0, null, 0, 0, 0),
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
+ ).setIsFirstTimeConfiguration(true).build()
+
+ assertEquals(
+ "KeyboardConfigurationEvent should pick vendor ID from provided InputDevice",
+ DEFAULT_VENDOR_ID,
+ event.vendorId
+ )
+ assertEquals(
+ "KeyboardConfigurationEvent should pick product ID from provided InputDevice",
+ DEFAULT_PRODUCT_ID,
+ event.productId
+ )
+ assertTrue(event.isFirstConfiguration)
+
+ assertEquals(
+ "KeyboardConfigurationEvent should contain 3 configurations provided",
+ 3,
+ event.layoutConfigurations.size
+ )
+ assertExpectedLayoutConfiguration(
+ event.layoutConfigurations[0],
+ "en-US",
+ KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"),
+ "English(US)(Qwerty)",
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
+ )
+ assertExpectedLayoutConfiguration(
+ event.layoutConfigurations[1],
+ "en-US",
+ KeyboardLayout.LayoutType.getLayoutTypeEnumValue("azerty"),
+ KeyboardMetricsCollector.DEFAULT_LAYOUT,
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER
+ )
+ assertExpectedLayoutConfiguration(
+ event.layoutConfigurations[2],
+ "de-CH",
+ KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"),
+ "German",
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
+ )
+ }
+
+ private fun assertExpectedLayoutConfiguration(
+ configuration: KeyboardMetricsCollector.LayoutConfiguration,
+ expectedLanguageTag: String,
+ expectedLayoutType: Int,
+ expectedSelectedLayout: String,
+ expectedLayoutSelectionCriteria: Int
+ ) {
+ assertEquals(expectedLanguageTag, configuration.keyboardLanguageTag)
+ assertEquals(expectedLayoutType, configuration.keyboardLayoutType)
+ assertEquals(expectedSelectedLayout, configuration.keyboardLayoutName)
+ assertEquals(expectedLayoutSelectionCriteria, configuration.layoutSelectionCriteria)
+ }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index e960e99..fe2ac17 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -254,6 +254,8 @@
.thenReturn(true);
when(res.getBoolean(eq(com.android.internal.R.bool.config_strongAuthRequiredOnBoot)))
.thenReturn(true);
+ when(res.getBoolean(eq(com.android.internal.R.bool.config_repairModeSupported)))
+ .thenReturn(true);
return res;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
index 36dc6c5..a029db9 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
@@ -84,6 +84,11 @@
}
@Override
+ File getRepairModePersistentDataFile() {
+ return remapToStorageDir(super.getRepairModePersistentDataFile());
+ }
+
+ @Override
PersistentDataBlockManagerInternal getPersistentDataBlockManager() {
return mPersistentDataBlockManager;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 10ed882..23f14f8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -457,6 +457,31 @@
assertEquals(2, PersistentData.TYPE_SP_WEAVER);
}
+ @Test
+ public void testRepairMode_emptyPersistentData() {
+ assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock());
+ }
+
+ @Test
+ public void testRepairMode_writeGatekeeperPersistentData() {
+ mStorage.writeRepairModePersistentData(
+ PersistentData.TYPE_SP_GATEKEEPER, SOME_USER_ID, PAYLOAD);
+
+ final PersistentData data = mStorage.readRepairModePersistentData();
+ assertEquals(PersistentData.TYPE_SP_GATEKEEPER, data.type);
+ assertArrayEquals(PAYLOAD, data.payload);
+ }
+
+ @Test
+ public void testRepairMode_writeWeaverPersistentData() {
+ mStorage.writeRepairModePersistentData(
+ PersistentData.TYPE_SP_WEAVER, SOME_USER_ID, PAYLOAD);
+
+ final PersistentData data = mStorage.readRepairModePersistentData();
+ assertEquals(PersistentData.TYPE_SP_WEAVER, data.type);
+ assertArrayEquals(PAYLOAD, data.payload);
+ }
+
private static void assertArrayEquals(byte[] expected, byte[] actual) {
if (!Arrays.equals(expected, actual)) {
fail("expected:<" + Arrays.toString(expected) +
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java
new file mode 100644
index 0000000..dc47b87
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.locksettings;
+
+import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import android.app.PropertyInvalidatedCache;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.server.locksettings.LockSettingsStorage.PersistentData;
+import com.android.server.locksettings.SyntheticPasswordManager.PasswordData;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class LockscreenRepairModeTest extends BaseLockSettingsServiceTests {
+
+ @Before
+ public void setUp() throws Exception {
+ PropertyInvalidatedCache.disableForTestMode();
+ mService.initializeSyntheticPassword(PRIMARY_USER_ID);
+ }
+
+ @Test
+ public void verifyPin_writeRepairModePW() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN,
+ getCredentialType(mStorage.readRepairModePersistentData()));
+ }
+
+ @Test
+ public void verifyPattern_writeRepairModePW() {
+ mService.setLockCredential(newPattern("4321"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPattern("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+ getCredentialType(mStorage.readRepairModePersistentData()));
+ }
+
+ @Test
+ public void verifyPassword_writeRepairModePW() {
+ mService.setLockCredential(newPassword("4321"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPassword("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ getCredentialType(mStorage.readRepairModePersistentData()));
+ }
+
+ @Test
+ public void verifyCredential_writeRepairModePW_repairModeActive() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+
+ setRepairModeActive(true);
+ assertEquals(VerifyCredentialResponse.RESPONSE_ERROR,
+ mService.verifyCredential(
+ newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+ }
+
+ @Test
+ public void deleteRepairModePersistentData() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN,
+ getCredentialType(mStorage.readRepairModePersistentData()));
+
+ mService.deleteRepairModePersistentDataIfNeeded();
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+ }
+
+ private void setRepairModeActive(boolean active) {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.REPAIR_MODE_ACTIVE, active ? 1 : 0);
+ }
+
+ private static int getCredentialType(PersistentData persistentData) {
+ if (persistentData == null || persistentData.payload == null) {
+ return LockPatternUtils.CREDENTIAL_TYPE_NONE;
+ }
+ return PasswordData.fromBytes(persistentData.payload).credentialType;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
index aa6ee09..0b13f9a 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
@@ -52,7 +52,7 @@
private static final int SOUND_TRIGGER_MODE = 0; // SOUND_TRIGGER_MODE_ALL_ENABLED
private static final int DEFAULT_SOUND_TRIGGER_MODE =
PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY;
- private static final String BATTERY_SAVER_CONSTANTS = "disable_vibration=true,"
+ private static final String BATTERY_SAVER_CONSTANTS = "disable_vibration=false,"
+ "advertise_is_enabled=true,"
+ "disable_animation=false,"
+ "enable_firewall=true,"
@@ -117,7 +117,7 @@
@SmallTest
public void testGetBatterySaverPolicy_PolicyVibration_DefaultValueCorrect() {
- testServiceDefaultValue_On(ServiceType.VIBRATION);
+ testServiceDefaultValue_Off(ServiceType.VIBRATION);
}
@SmallTest
@@ -211,7 +211,7 @@
private void verifyBatterySaverConstantsUpdated() {
final PowerSaveState vibrationState =
mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.VIBRATION);
- assertThat(vibrationState.batterySaverEnabled).isTrue();
+ assertThat(vibrationState.batterySaverEnabled).isFalse();
final PowerSaveState animationState =
mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.ANIMATION);
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index e5371975..5f48f3c 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -82,7 +82,10 @@
],
platform_apis: true,
- test_suites: ["device-tests"],
+ test_suites: [
+ "device-tests",
+ "automotive-tests",
+ ],
certificate: "platform",
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 41fcd69..3db53eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2784,8 +2784,12 @@
testLegacySplashScreen(Build.VERSION_CODES.S, TYPE_PARAMETER_LEGACY_SPLASH_SCREEN);
testLegacySplashScreen(Build.VERSION_CODES.TIRAMISU,
TYPE_PARAMETER_LEGACY_SPLASH_SCREEN);
- // Above T
- testLegacySplashScreen(Build.VERSION_CODES.TIRAMISU + 1, 0);
+ testLegacySplashScreen(Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ TYPE_PARAMETER_LEGACY_SPLASH_SCREEN);
+ testLegacySplashScreen(Build.VERSION_CODES.UPSIDE_DOWN_CAKE + 1,
+ TYPE_PARAMETER_LEGACY_SPLASH_SCREEN);
+ // Above V
+ testLegacySplashScreen(Build.VERSION_CODES.UPSIDE_DOWN_CAKE + 2, 0);
} finally {
try {
DeviceConfig.setProperties(properties);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index d173ce9..3bc6450 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -337,6 +337,17 @@
// Ensure a task has moved over.
ensureTaskPlacement(task, activity);
assertTrue(task.inPinnedWindowingMode());
+
+ // The activity with fixed orientation should not apply letterbox when entering PiP.
+ final int requestedOrientation = task.getConfiguration().orientation
+ == Configuration.ORIENTATION_PORTRAIT
+ ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+ doReturn(requestedOrientation).when(activity).getRequestedConfigurationOrientation();
+ doReturn(false).when(activity).handlesOrientationChangeFromDescendant(anyInt());
+ final Rect bounds = new Rect(task.getBounds());
+ bounds.scale(0.5f);
+ task.setBounds(bounds);
+ assertFalse(activity.isLetterboxedForFixedOrientationAndAspectRatio());
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
index 1ee0959..b181213 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
@@ -43,16 +43,14 @@
import android.os.Looper;
import android.os.ServiceManager;
import android.platform.test.annotations.Presubmit;
-import android.util.Pair;
import android.view.IWindowManager;
import android.view.PointerIcon;
import android.view.SurfaceControl;
import android.view.cts.surfacevalidator.BitmapPixelChecker;
import android.view.cts.surfacevalidator.SaveBitmapHelper;
import android.window.ScreenCapture;
-import android.window.ScreenCapture.ScreenCaptureListener;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
-import android.window.ScreenCapture.ScreenshotSync;
+import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
@@ -169,10 +167,10 @@
.setPosition(sc, point.x, point.y)
.apply(true);
- Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture =
+ SynchronousScreenCaptureListener syncScreenCapture =
ScreenCapture.createSyncCaptureListener();
- windowManager.captureDisplay(DEFAULT_DISPLAY, null, syncScreenCapture.first);
- ScreenshotHardwareBuffer hardwareBuffer = syncScreenCapture.second.get();
+ windowManager.captureDisplay(DEFAULT_DISPLAY, null, syncScreenCapture);
+ ScreenshotHardwareBuffer hardwareBuffer = syncScreenCapture.getBuffer();
assertNotNull(hardwareBuffer);
Bitmap screenshot = hardwareBuffer.asBitmap();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 27e6e31..3908947 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -2047,6 +2047,72 @@
}
@Test
+ public void testDefaultLetterboxAspectRatioForMultiWindowMode_fixedOrientationApp() {
+ // Set-up display in portrait.
+ mAtm.mDevEnableNonResizableMultiWindow = true;
+ final int screenWidth = 1100;
+ final int screenHeight = 2100;
+ setUpDisplaySizeWithApp(screenWidth, screenHeight);
+
+ mActivity.mDisplayContent.getWindowConfiguration()
+ .setAppBounds(/* left */ 0, /* top */ 0, screenWidth, screenHeight);
+
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+ // Move activity to multi-window which takes half of the screen.
+ mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+ organizer.mPrimary.setBounds(0, 0, screenWidth, getExpectedSplitSize(screenHeight));
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+
+ // Unresizable portrait-only activity.
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Activity should be letterboxed with an aspect ratio of 1.01.
+ final Rect afterBounds = mActivity.getBounds();
+ final float actualAspectRatio = 1f * afterBounds.height() / afterBounds.width();
+ assertEquals(LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW,
+ actualAspectRatio, 0.001f);
+ assertTrue(mActivity.areBoundsLetterboxed());
+ }
+
+ @Test
+ public void
+ testDefaultLetterboxAspectRatioForMultiWindowMode_fixedOrientationAppWithMinRatio() {
+ // Set-up display in portrait.
+ mAtm.mDevEnableNonResizableMultiWindow = true;
+ final int screenWidth = 1100;
+ final int screenHeight = 2100;
+ setUpDisplaySizeWithApp(screenWidth, screenHeight);
+
+ mActivity.mDisplayContent.getWindowConfiguration()
+ .setAppBounds(/* left */ 0, /* top */ 0, screenWidth, screenHeight);
+
+ // Set min aspect ratio to value greater than the default letterbox aspect ratio for
+ // multi-window mode.
+ final float minAspectRatio = 1.2f;
+ mActivity.info.setMinAspectRatio(minAspectRatio);
+
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+ // Move activity to multi-window which takes half of the screen.
+ mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+ organizer.mPrimary.setBounds(0, 0, screenWidth, getExpectedSplitSize(screenHeight));
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+
+ // Unresizable portrait-only activity.
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Activity should be letterboxed with the min aspect ratio requested by the app NOT the
+ // default letterbox aspect ratio for multi-window.
+ final Rect afterBounds = mActivity.getBounds();
+ final float actualAspectRatio = 1f * afterBounds.height() / afterBounds.width();
+ assertEquals(minAspectRatio, actualAspectRatio, 0.001f);
+ assertTrue(mActivity.areBoundsLetterboxed());
+ }
+
+ @Test
public void testDisplayIgnoreOrientationRequest_unresizableWithCorrespondingMinAspectRatio() {
// Set up a display in landscape and ignoring orientation request.
setUpDisplaySizeWithApp(2800, 1400);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
index 8425844..8bd5473 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
@@ -90,19 +90,23 @@
public void packageFromDeviceConfigIgnored() {
setExceptionListAndWaitForCallback("com.test.nosplashscreen1,com.test.nosplashscreen2");
- // In list, up to T included
+ // In list, up to V included
assertIsException("com.test.nosplashscreen1", VERSION_CODES.R);
assertIsException("com.test.nosplashscreen1", VERSION_CODES.S);
assertIsException("com.test.nosplashscreen1", VERSION_CODES.TIRAMISU);
+ assertIsException("com.test.nosplashscreen1", VERSION_CODES.UPSIDE_DOWN_CAKE);
+ assertIsException("com.test.nosplashscreen1", VERSION_CODES.UPSIDE_DOWN_CAKE + 1);
- // In list, after T
- assertIsNotException("com.test.nosplashscreen2", VERSION_CODES.TIRAMISU + 1);
+ // In list, after V
+ assertIsNotException("com.test.nosplashscreen2", VERSION_CODES.UPSIDE_DOWN_CAKE + 2);
assertIsNotException("com.test.nosplashscreen2", VERSION_CODES.CUR_DEVELOPMENT);
- // Not in list, up to T included
+ // Not in list, up to V included
assertIsNotException("com.test.splashscreen", VERSION_CODES.S);
assertIsNotException("com.test.splashscreen", VERSION_CODES.R);
assertIsNotException("com.test.splashscreen", VERSION_CODES.TIRAMISU);
+ assertIsNotException("com.test.splashscreen", VERSION_CODES.UPSIDE_DOWN_CAKE);
+ assertIsNotException("com.test.splashscreen", VERSION_CODES.UPSIDE_DOWN_CAKE + 1);
}
@Test
@@ -119,15 +123,19 @@
assertIsNotException(packageName, VERSION_CODES.R, activityInfo);
assertIsNotException(packageName, VERSION_CODES.S, activityInfo);
assertIsNotException(packageName, VERSION_CODES.TIRAMISU, activityInfo);
+ assertIsNotException(packageName, VERSION_CODES.UPSIDE_DOWN_CAKE, activityInfo);
+ assertIsNotException(packageName, VERSION_CODES.UPSIDE_DOWN_CAKE + 1, activityInfo);
- // Exception up to T
+ // Exception up to V
metaData.putBoolean("android.splashscreen.exception_opt_out", false);
assertIsException(packageName, VERSION_CODES.R, activityInfo);
assertIsException(packageName, VERSION_CODES.S, activityInfo);
assertIsException(packageName, VERSION_CODES.TIRAMISU, activityInfo);
+ assertIsException(packageName, VERSION_CODES.UPSIDE_DOWN_CAKE, activityInfo);
+ assertIsException(packageName, VERSION_CODES.UPSIDE_DOWN_CAKE + 1, activityInfo);
- // No Exception after T
- assertIsNotException(packageName, VERSION_CODES.TIRAMISU + 1, activityInfo);
+ // No Exception after V
+ assertIsNotException(packageName, VERSION_CODES.UPSIDE_DOWN_CAKE + 2, activityInfo);
assertIsNotException(packageName, VERSION_CODES.CUR_DEVELOPMENT, activityInfo);
// Edge Cases
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 4123f80..960b57c 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -118,6 +118,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnManagementServiceTest {
+ private static final String CONTEXT_ATTRIBUTION_TAG = "VCN";
private static final String TEST_PACKAGE_NAME =
VcnManagementServiceTest.class.getPackage().getName();
private static final String TEST_PACKAGE_NAME_2 = "TEST_PKG_2";
@@ -177,6 +178,7 @@
0 /* carrierId */,
0 /* profileClass */);
+ private final Context mMockContextWithoutAttributionTag = mock(Context.class);
private final Context mMockContext = mock(Context.class);
private final VcnManagementService.Dependencies mMockDeps =
mock(VcnManagementService.Dependencies.class);
@@ -202,6 +204,10 @@
private final IBinder mMockIBinder = mock(IBinder.class);
public VcnManagementServiceTest() throws Exception {
+ doReturn(mMockContext)
+ .when(mMockContextWithoutAttributionTag)
+ .createAttributionContext(CONTEXT_ATTRIBUTION_TAG);
+
setupSystemService(
mMockContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
setupSystemService(
@@ -249,7 +255,7 @@
doReturn(bundle).when(mConfigReadWriteHelper).readFromDisk();
setupMockedCarrierPrivilege(true);
- mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
+ mVcnMgmtSvc = new VcnManagementService(mMockContextWithoutAttributionTag, mMockDeps);
setupActiveSubscription(TEST_UUID_1);
doReturn(mMockIBinder).when(mMockPolicyListener).asBinder();