Merge "[flexiglass] Addresses a couple of comments in BouncerScene." into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 3f4a34b..9ce5342 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -14699,31 +14699,31 @@
}
@FlaggedApi("android.database.sqlite.sqlite_apis_35") public final class SQLiteRawStatement implements java.io.Closeable {
- method public void bindBlob(int, @NonNull byte[]) throws android.database.sqlite.SQLiteException;
- method public void bindBlob(int, @NonNull byte[], int, int) throws android.database.sqlite.SQLiteException;
- method public void bindDouble(int, double) throws android.database.sqlite.SQLiteException;
- method public void bindInt(int, int) throws android.database.sqlite.SQLiteException;
- method public void bindLong(int, long) throws android.database.sqlite.SQLiteException;
- method public void bindNull(int) throws android.database.sqlite.SQLiteException;
- method public void bindText(int, @NonNull String) throws android.database.sqlite.SQLiteException;
+ method public void bindBlob(int, @NonNull byte[]);
+ method public void bindBlob(int, @NonNull byte[], int, int);
+ method public void bindDouble(int, double);
+ method public void bindInt(int, int);
+ method public void bindLong(int, long);
+ method public void bindNull(int);
+ method public void bindText(int, @NonNull String);
method public void clearBindings();
method public void close();
- method @Nullable public byte[] getColumnBlob(int) throws android.database.sqlite.SQLiteException;
- method public double getColumnDouble(int) throws android.database.sqlite.SQLiteException;
- method public int getColumnInt(int) throws android.database.sqlite.SQLiteException;
- method public int getColumnLength(int) throws android.database.sqlite.SQLiteException;
- method public long getColumnLong(int) throws android.database.sqlite.SQLiteException;
- method @NonNull public String getColumnName(int) throws android.database.sqlite.SQLiteException;
- method @NonNull public String getColumnText(int) throws android.database.sqlite.SQLiteException;
- method public int getColumnType(int) throws android.database.sqlite.SQLiteException;
+ method @Nullable public byte[] getColumnBlob(int);
+ method public double getColumnDouble(int);
+ method public int getColumnInt(int);
+ method public int getColumnLength(int);
+ method public long getColumnLong(int);
+ method @NonNull public String getColumnName(int);
+ method @NonNull public String getColumnText(int);
+ method public int getColumnType(int);
method public int getParameterCount();
method public int getParameterIndex(@NonNull String);
method @Nullable public String getParameterName(int);
method public int getResultColumnCount();
method public boolean isOpen();
- method public int readColumnBlob(int, @NonNull byte[], int, int, int) throws android.database.sqlite.SQLiteException;
+ method public int readColumnBlob(int, @NonNull byte[], int, int, int);
method public void reset();
- method public boolean step() throws android.database.sqlite.SQLiteException;
+ method public boolean step();
field public static final int SQLITE_DATA_TYPE_BLOB = 4; // 0x4
field public static final int SQLITE_DATA_TYPE_FLOAT = 2; // 0x2
field public static final int SQLITE_DATA_TYPE_INTEGER = 1; // 0x1
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index 1308b1f..449249e 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -1,40 +1,4 @@
// Baseline format: 1.0
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindBlob(int, byte[]):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindBlob(int, byte[], int, int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindDouble(int, double):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindInt(int, int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindLong(int, long):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindNull(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#bindText(int, String):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnBlob(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnDouble(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnInt(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnLength(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnLong(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnName(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnText(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnType(int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#readColumnBlob(int, byte[], int, int, int):
- Methods must not throw unchecked exceptions
-BannedThrow: android.database.sqlite.SQLiteRawStatement#step():
- Methods must not throw unchecked exceptions
-
-
BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED:
Field 'ACTION_NEXT_ALARM_CLOCK_CHANGED' is missing @BroadcastBehavior
BroadcastBehavior: android.app.AlarmManager#ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED:
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 8d480e5..de07fdde6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -16834,6 +16834,10 @@
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteCapabilities> CREATOR;
}
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteCapabilitiesCallback {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilities);
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class SatelliteDatagram implements android.os.Parcelable {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public byte[] getSatelliteDatagram();
@@ -16852,6 +16856,7 @@
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteCapabilitiesChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
@@ -16872,6 +16877,7 @@
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void startSatelliteTransmissionUpdates(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void stopSatelliteTransmissionUpdates(@NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 35509237..fd13174 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -575,8 +575,9 @@
extras.putString(Downloads.DIR_TYPE, dirType);
client.call(Downloads.CALL_CREATE_EXTERNAL_PUBLIC_DIR, null, extras);
} catch (RemoteException e) {
- throw new IllegalStateException("Unable to create directory: "
- + file.getAbsolutePath());
+ throw new IllegalStateException(
+ "Unable to create directory: " + file.getAbsolutePath(),
+ e);
}
} else {
if (file.exists()) {
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index b0332c3..ffb79b3 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1642,6 +1642,7 @@
*
* <p>
*/
+ // TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
public Policy getNotificationPolicy() {
INotificationManager service = getService();
try {
@@ -1660,6 +1661,7 @@
*
* @param policy The new desired policy.
*/
+ // TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
public void setNotificationPolicy(@NonNull Policy policy) {
checkRequired("policy", policy);
INotificationManager service = getService();
@@ -2608,6 +2610,7 @@
* Only available if policy access is granted to this package. See
* {@link #isNotificationPolicyAccessGranted}.
*/
+ // TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
public final void setInterruptionFilter(@InterruptionFilter int interruptionFilter) {
final INotificationManager service = getService();
try {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4c70c91..3df11f6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -81,6 +81,9 @@
import android.app.IServiceConnection;
import android.app.KeyguardManager;
import android.app.admin.SecurityLog.SecurityEvent;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -9118,6 +9121,19 @@
}
/**
+ * For apps targeting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above, the
+ * {@link #isDeviceOwnerApp} method will use the user contained within the
+ * context.
+ * For apps targeting an SDK version <em>below</em> this, the user of the calling process will
+ * be used (Process.myUserHandle()).
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public static final long IS_DEVICE_OWNER_USER_AWARE = 307233716L;
+
+ /**
* Used to determine if a particular package has been registered as a Device Owner app.
* A device owner app is a special device admin that cannot be deactivated by the user, once
* activated as a device admin. It also cannot be uninstalled. To check whether a particular
@@ -9130,8 +9146,13 @@
* app, if any.
* @return whether or not the package is registered as the device owner app.
*/
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public boolean isDeviceOwnerApp(String packageName) {
throwIfParentInstance("isDeviceOwnerApp");
+ if (android.permission.flags.Flags.roleControllerInSystemServer()
+ && CompatChanges.isChangeEnabled(IS_DEVICE_OWNER_USER_AWARE)) {
+ return isDeviceOwnerAppOnContextUser(packageName);
+ }
return isDeviceOwnerAppOnCallingUser(packageName);
}
@@ -9192,6 +9213,24 @@
return packageName.equals(deviceOwner.getPackageName());
}
+ private boolean isDeviceOwnerAppOnContextUser(String packageName) {
+ if (packageName == null) {
+ return false;
+ }
+ ComponentName deviceOwner = null;
+ if (mService != null) {
+ try {
+ deviceOwner = mService.getDeviceOwnerComponentOnUser(myUserId());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ if (deviceOwner == null) {
+ return false;
+ }
+ return packageName.equals(deviceOwner.getPackageName());
+ }
+
private ComponentName getDeviceOwnerComponentInner(boolean callingUserOnly) {
if (mService != null) {
try {
@@ -9608,6 +9647,7 @@
* @param packageName The package name of the app to compare with the registered profile owner.
* @return Whether or not the package is registered as the profile owner.
*/
+ @UserHandleAware
public boolean isProfileOwnerApp(String packageName) {
throwIfParentInstance("isProfileOwnerApp");
if (mService != null) {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 6fe40be..575fa4c 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -179,6 +179,7 @@
boolean setDeviceOwner(in ComponentName who, int userId, boolean setProfileOwnerOnCurrentUserIfNecessary);
ComponentName getDeviceOwnerComponent(boolean callingUserOnly);
+ ComponentName getDeviceOwnerComponentOnUser(int userId);
boolean hasDeviceOwner();
String getDeviceOwnerName();
void clearDeviceOwner(String packageName);
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
index 543703e..a407704 100644
--- a/core/java/android/content/pm/SigningInfo.java
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -190,9 +190,11 @@
mSigningDetails.writeToParcel(dest, parcelableFlags);
}
- /* @hide */
+ /**
+ * @hide
+ */
@NonNull
- SigningDetails getSigningDetails() {
+ public SigningDetails getSigningDetails() {
return mSigningDetails;
}
diff --git a/core/java/android/database/sqlite/SQLiteRawStatement.java b/core/java/android/database/sqlite/SQLiteRawStatement.java
index 33f602b..c59d3ce 100644
--- a/core/java/android/database/sqlite/SQLiteRawStatement.java
+++ b/core/java/android/database/sqlite/SQLiteRawStatement.java
@@ -163,7 +163,7 @@
* {@link IllegalStateException} if a transaction is not in progress. Clients should call
* {@link SQLiteDatabase.createRawStatement} to create a new instance.
*/
- SQLiteRawStatement(@NonNull SQLiteDatabase db, @NonNull String sql) throws SQLiteException {
+ SQLiteRawStatement(@NonNull SQLiteDatabase db, @NonNull String sql) {
mThread = Thread.currentThread();
mDatabase = db;
mSession = mDatabase.getThreadSession();
@@ -245,7 +245,7 @@
* @throws SQLiteDatabaseLockedException if the database is locked or busy.
* @throws SQLiteException if a native error occurs.
*/
- public boolean step() throws SQLiteException {
+ public boolean step() {
throwIfInvalid();
try {
int err = nativeStep(mStatement, true);
@@ -392,7 +392,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindBlob(int parameterIndex, @NonNull byte[] value) throws SQLiteException {
+ public void bindBlob(int parameterIndex, @NonNull byte[] value) {
Objects.requireNonNull(value);
throwIfInvalid();
try {
@@ -418,8 +418,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindBlob(int parameterIndex, @NonNull byte[] value, int offset, int length)
- throws SQLiteException {
+ public void bindBlob(int parameterIndex, @NonNull byte[] value, int offset, int length) {
Objects.requireNonNull(value);
throwIfInvalid();
throwIfInvalidBounds(value.length, offset, length);
@@ -442,7 +441,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindDouble(int parameterIndex, double value) throws SQLiteException {
+ public void bindDouble(int parameterIndex, double value) {
throwIfInvalid();
try {
nativeBindDouble(mStatement, parameterIndex, value);
@@ -462,7 +461,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindInt(int parameterIndex, int value) throws SQLiteException {
+ public void bindInt(int parameterIndex, int value) {
throwIfInvalid();
try {
nativeBindInt(mStatement, parameterIndex, value);
@@ -482,7 +481,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindLong(int parameterIndex, long value) throws SQLiteException {
+ public void bindLong(int parameterIndex, long value) {
throwIfInvalid();
try {
nativeBindLong(mStatement, parameterIndex, value);
@@ -502,7 +501,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindNull(int parameterIndex) throws SQLiteException {
+ public void bindNull(int parameterIndex) {
throwIfInvalid();
try {
nativeBindNull(mStatement, parameterIndex);
@@ -523,7 +522,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public void bindText(int parameterIndex, @NonNull String value) throws SQLiteException {
+ public void bindText(int parameterIndex, @NonNull String value) {
Objects.requireNonNull(value);
throwIfInvalid();
try {
@@ -562,7 +561,7 @@
* @throws SQLiteException if a native error occurs.
*/
@SQLiteDataType
- public int getColumnType(int columnIndex) throws SQLiteException {
+ public int getColumnType(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnType(mStatement, columnIndex);
@@ -584,7 +583,7 @@
* @throws SQLiteOutOfMemoryException if the database cannot allocate memory for the name.
*/
@NonNull
- public String getColumnName(int columnIndex) throws SQLiteException {
+ public String getColumnName(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnName(mStatement, columnIndex);
@@ -609,7 +608,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public int getColumnLength(int columnIndex) throws SQLiteException {
+ public int getColumnLength(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnBytes(mStatement, columnIndex);
@@ -635,7 +634,7 @@
* @throws SQLiteException if a native error occurs.
*/
@Nullable
- public byte[] getColumnBlob(int columnIndex) throws SQLiteException {
+ public byte[] getColumnBlob(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnBlob(mStatement, columnIndex);
@@ -668,8 +667,7 @@
* @throws SQLiteException if a native error occurs.
*/
public int readColumnBlob(int columnIndex, @NonNull byte[] buffer, int offset,
- int length, int srcOffset)
- throws SQLiteException {
+ int length, int srcOffset) {
Objects.requireNonNull(buffer);
throwIfInvalid();
throwIfInvalidBounds(buffer.length, offset, length);
@@ -695,7 +693,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public double getColumnDouble(int columnIndex) throws SQLiteException {
+ public double getColumnDouble(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnDouble(mStatement, columnIndex);
@@ -719,7 +717,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public int getColumnInt(int columnIndex) throws SQLiteException {
+ public int getColumnInt(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnInt(mStatement, columnIndex);
@@ -743,7 +741,7 @@
* @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
* @throws SQLiteException if a native error occurs.
*/
- public long getColumnLong(int columnIndex) throws SQLiteException {
+ public long getColumnLong(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnLong(mStatement, columnIndex);
@@ -768,7 +766,7 @@
* @throws SQLiteException if a native error occurs.
*/
@NonNull
- public String getColumnText(int columnIndex) throws SQLiteException {
+ public String getColumnText(int columnIndex) {
throwIfInvalid();
try {
return nativeColumnText(mStatement, columnIndex);
diff --git a/core/java/android/hardware/camera2/impl/FrameNumberTracker.java b/core/java/android/hardware/camera2/impl/FrameNumberTracker.java
index 8304796..cf496d2 100644
--- a/core/java/android/hardware/camera2/impl/FrameNumberTracker.java
+++ b/core/java/android/hardware/camera2/impl/FrameNumberTracker.java
@@ -90,6 +90,22 @@
break;
}
}
+
+ if (!removeError) {
+ // Check for the case where we might have an error after a frame number gap
+ // caused by other types of capture requests
+ int otherType1 = (requestType + 1) % CaptureRequest.REQUEST_TYPE_COUNT;
+ int otherType2 = (requestType + 2) % CaptureRequest.REQUEST_TYPE_COUNT;
+ if (mPendingFrameNumbersWithOtherType[otherType1].isEmpty() &&
+ mPendingFrameNumbersWithOtherType[otherType2].isEmpty()) {
+ long errorGapNumber = Math.max(mCompletedFrameNumber[otherType1],
+ mCompletedFrameNumber[otherType2]) + 1;
+ if ((errorGapNumber > mCompletedFrameNumber[requestType] + 1) &&
+ (errorGapNumber == errorFrameNumber)) {
+ removeError = true;
+ }
+ }
+ }
}
if (removeError) {
mCompletedFrameNumber[requestType] = errorFrameNumber;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 9956220..0ccc485 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -30,9 +30,11 @@
import com.android.internal.os.BinderCallHeavyHitterWatcher.BinderCallHeavyHitterListener;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.BinderInternal.CallSession;
+import com.android.internal.os.SomeArgs;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
+import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
@@ -46,6 +48,7 @@
import java.io.PrintWriter;
import java.lang.reflect.Modifier;
import java.util.concurrent.atomic.AtomicReferenceArray;
+import java.util.function.Supplier;
/**
* Base class for a remotable object, the core part of a lightweight
@@ -289,6 +292,33 @@
sWarnOnBlockingOnCurrentThread.set(sWarnOnBlocking);
}
+ private static ThreadLocal<SomeArgs> sIdentity$ravenwood;
+
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
+ private static class IdentitySupplier implements Supplier<SomeArgs> {
+ @Override
+ public SomeArgs get() {
+ final SomeArgs args = SomeArgs.obtain();
+ // Match IPCThreadState behavior
+ args.arg1 = Boolean.FALSE;
+ args.argi1 = android.os.Process.myUid();
+ args.argi2 = android.os.Process.myPid();
+ return args;
+ }
+ }
+
+ /** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
+ public static void init$ravenwood() {
+ sIdentity$ravenwood = ThreadLocal.withInitial(new IdentitySupplier());
+ }
+
+ /** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
+ public static void reset$ravenwood() {
+ sIdentity$ravenwood = null;
+ }
+
/**
* Raw native pointer to JavaBBinderHolder object. Owned by this Java object. Not null.
*/
@@ -312,8 +342,14 @@
* Warning: oneway transactions do not receive PID.
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native int getCallingPid();
+ /** @hide */
+ public static final int getCallingPid$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi2;
+ }
+
/**
* Return the Linux UID assigned to the process that sent you the
* current transaction that is being processed. This UID can be used with
@@ -322,8 +358,14 @@
* incoming transaction, then its own UID is returned.
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native int getCallingUid();
+ /** @hide */
+ public static final int getCallingUid$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi1;
+ }
+
/**
* Returns {@code true} if the current thread is currently executing an
* incoming transaction.
@@ -331,6 +373,7 @@
* @hide
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native boolean isDirectlyHandlingTransactionNative();
/** @hide */
@@ -344,6 +387,7 @@
/**
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static final boolean isDirectlyHandlingTransaction() {
return sIsHandlingBinderTransaction || isDirectlyHandlingTransactionNative();
}
@@ -363,8 +407,15 @@
* @hide
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
private static native boolean hasExplicitIdentity();
+ /** @hide */
+ private static boolean hasExplicitIdentity$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().arg1
+ == Boolean.TRUE;
+ }
+
/**
* Return the Linux UID assigned to the process that sent the transaction
* currently being processed.
@@ -373,6 +424,7 @@
* executing an incoming transaction and the calling identity has not been
* explicitly set with {@link #clearCallingIdentity()}
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static final int getCallingUidOrThrow() {
if (!isDirectlyHandlingTransaction() && !hasExplicitIdentity()) {
throw new IllegalStateException(
@@ -434,8 +486,26 @@
* @see #restoreCallingIdentity(long)
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native long clearCallingIdentity();
+ /** @hide */
+ public static final long clearCallingIdentity$ravenwood() {
+ final SomeArgs args = Preconditions.requireNonNullViaRavenwoodRule(
+ sIdentity$ravenwood).get();
+ long res = ((long) args.argi1 << 32) | args.argi2;
+ if (args.arg1 == Boolean.TRUE) {
+ res |= (0x1 << 30);
+ } else {
+ res &= ~(0x1 << 30);
+ }
+ // Match IPCThreadState behavior
+ args.arg1 = Boolean.TRUE;
+ args.argi1 = android.os.Process.myUid();
+ args.argi2 = android.os.Process.myPid();
+ return res;
+ }
+
/**
* Restore the identity of the incoming IPC on the current thread
* back to a previously identity that was returned by {@link
@@ -447,8 +517,18 @@
* @see #clearCallingIdentity
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native void restoreCallingIdentity(long token);
+ /** @hide */
+ public static final void restoreCallingIdentity$ravenwood(long token) {
+ final SomeArgs args = Preconditions.requireNonNullViaRavenwoodRule(
+ sIdentity$ravenwood).get();
+ args.arg1 = ((token & (0x1 << 30)) != 0) ? Boolean.TRUE : Boolean.FALSE;
+ args.argi1 = (int) (token >> 32);
+ args.argi2 = (int) (token & ~(0x1 << 30));
+ }
+
/**
* Convenience method for running the provided action enclosed in
* {@link #clearCallingIdentity}/{@link #restoreCallingIdentity}.
@@ -644,8 +724,14 @@
* in order to prevent the process from holding on to objects longer than
* it needs to.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native void flushPendingCommands();
+ /** @hide */
+ public static final void flushPendingCommands$ravenwood() {
+ // Ravenwood doesn't support IPC; ignored
+ }
+
/**
* Add the calling thread to the IPC thread pool. This function does
* not return until the current process is exiting.
@@ -703,6 +789,7 @@
* <p>If you're creating a Binder token (a Binder object without an attached interface),
* you should use {@link #Binder(String)} instead.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public Binder() {
this(null);
}
@@ -719,6 +806,7 @@
* Instead of creating multiple tokens with the same descriptor, consider adding a suffix to
* help identify them.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public Binder(@Nullable String descriptor) {
mObject = getNativeBBinderHolder();
if (mObject != 0L) {
@@ -742,6 +830,7 @@
* will be implemented for you to return the given owner IInterface when
* the corresponding descriptor is requested.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
@@ -750,6 +839,7 @@
/**
* Default implementation returns an empty interface name.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public @Nullable String getInterfaceDescriptor() {
return mDescriptor;
}
@@ -758,6 +848,7 @@
* Default implementation always returns true -- if you got here,
* the object is alive.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public boolean pingBinder() {
return true;
}
@@ -768,6 +859,7 @@
* Note that if you're calling on a local binder, this always returns true
* because your process is alive if you're calling it.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public boolean isBinderAlive() {
return true;
}
@@ -777,6 +869,7 @@
* to return the associated {@link IInterface} if it matches the requested
* descriptor.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
@@ -1250,12 +1343,14 @@
/**
* Local implementation is a no-op.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
}
/**
* Local implementation is a no-op.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {
return true;
}
@@ -1283,6 +1378,7 @@
}
}
+ @android.ravenwood.annotation.RavenwoodReplace
private static native long getNativeBBinderHolder();
private static long getNativeBBinderHolder$ravenwood() {
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 90e4b17..91c2965 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -194,6 +194,7 @@
* Limit that should be placed on IPC sizes, in bytes, to keep them safely under the transaction
* buffer limit.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
static int getSuggestedMaxIpcSizeBytes() {
return MAX_IPC_SIZE;
}
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl
index f30dd20..0f27569 100644
--- a/core/java/android/os/IVibratorManagerService.aidl
+++ b/core/java/android/os/IVibratorManagerService.aidl
@@ -33,13 +33,13 @@
boolean unregisterVibratorStateListener(int vibratorId, in IVibratorStateListener listener);
boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
in CombinedVibration vibration, in VibrationAttributes attributes);
- void vibrate(int uid, int displayId, String opPkg, in CombinedVibration vibration,
+ void vibrate(int uid, int deviceId, String opPkg, in CombinedVibration vibration,
in VibrationAttributes attributes, String reason, IBinder token);
void cancelVibrate(int usageFilter, IBinder token);
// Async oneway APIs.
// There is no order guarantee with respect to the two-way APIs above like
// vibrate/isVibrating/cancel.
- oneway void performHapticFeedback(int uid, int displayId, String opPkg, int constant,
+ oneway void performHapticFeedback(int uid, int deviceId, String opPkg, int constant,
boolean always, String reason, IBinder token);
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 677143a..daec1721 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -34,6 +34,9 @@
import android.util.Pair;
import android.webkit.WebViewZygote;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.util.Preconditions;
+
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
@@ -833,14 +836,37 @@
return VMRuntime.getRuntime().is64Bit();
}
+ private static SomeArgs sIdentity$ravenwood;
+
+ /** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
+ public static void init$ravenwood(int uid, int pid) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.argi1 = uid;
+ args.argi2 = pid;
+ sIdentity$ravenwood = args;
+ }
+
+ /** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
+ public static void reset$ravenwood() {
+ sIdentity$ravenwood = null;
+ }
+
/**
* Returns the identifier of this process, which can be used with
* {@link #killProcess} and {@link #sendSignal}.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static final int myPid() {
return Os.getpid();
}
+ /** @hide */
+ public static final int myPid$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).argi2;
+ }
+
/**
* Returns the identifier of this process' parent.
* @hide
@@ -864,10 +890,16 @@
* app-specific sandbox. It is different from {@link #myUserHandle} in that
* a uid identifies a specific app sandbox in a specific user.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static final int myUid() {
return Os.getuid();
}
+ /** @hide */
+ public static final int myUid$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).argi1;
+ }
+
/**
* Returns this process's user handle. This is the
* user the process is running under. It is distinct from
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 831ca86..49a0bd3 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.location.ILocationManager;
import android.location.LocationTime;
+import android.text.format.DateUtils;
import android.util.Slog;
import dalvik.annotation.optimization.CriticalNative;
@@ -125,6 +126,7 @@
*
* @param ms to sleep before returning, in milliseconds of uptime.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static void sleep(long ms)
{
long start = uptimeMillis();
@@ -186,8 +188,16 @@
* @return milliseconds of non-sleep uptime since boot.
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
native public static long uptimeMillis();
+ /** @hide */
+ public static long uptimeMillis$ravenwood() {
+ // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
+ return System.currentTimeMillis() - (1672556400L * 1_000)
+ - (DateUtils.WEEK_IN_MILLIS * 1_000);
+ }
+
/**
* Returns nanoseconds since boot, not counting time spent in deep sleep.
*
@@ -195,8 +205,16 @@
* @hide
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static native long uptimeNanos();
+ /** @hide */
+ public static long uptimeNanos$ravenwood() {
+ // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
+ return System.nanoTime() - (1672556400L * 1_000_000_000)
+ - (DateUtils.WEEK_IN_MILLIS * 1_000_000_000);
+ }
+
/**
* Return {@link Clock} that starts at system boot, not counting time spent
* in deep sleep.
@@ -218,8 +236,15 @@
* @return elapsed milliseconds since boot.
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
native public static long elapsedRealtime();
+ /** @hide */
+ public static long elapsedRealtime$ravenwood() {
+ // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
+ return System.currentTimeMillis() - (1672556400L * 1_000);
+ }
+
/**
* Return {@link Clock} that starts at system boot, including time spent in
* sleep.
@@ -241,8 +266,15 @@
* @return elapsed nanoseconds since boot.
*/
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static native long elapsedRealtimeNanos();
+ /** @hide */
+ public static long elapsedRealtimeNanos$ravenwood() {
+ // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
+ return System.nanoTime() - (1672556400L * 1_000_000_000);
+ }
+
/**
* Returns milliseconds running in the current thread.
*
@@ -271,8 +303,15 @@
*/
@UnsupportedAppUsage
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
public static native long currentTimeMicro();
+ /** @hide */
+ public static long currentTimeMicro$ravenwood() {
+ // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
+ return System.nanoTime() / 1000L;
+ }
+
/**
* Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized
* using a remote network source outside the device.
diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java
index ee90834..bc85412e 100644
--- a/core/java/android/os/SystemVibratorManager.java
+++ b/core/java/android/os/SystemVibratorManager.java
@@ -137,8 +137,8 @@
return;
}
try {
- mService.vibrate(uid, mContext.getAssociatedDisplayId(), opPkg, effect, attributes,
- reason, mToken);
+ mService.vibrate(uid, mContext.getDeviceId(), opPkg, effect, attributes, reason,
+ mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to vibrate.", e);
}
@@ -152,8 +152,8 @@
}
try {
mService.performHapticFeedback(
- Process.myUid(), mContext.getAssociatedDisplayId(), mPackageName, constant,
- always, reason, mToken);
+ Process.myUid(), mContext.getDeviceId(), mPackageName, constant, always, reason,
+ mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to perform haptic feedback.", e);
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index ff4dfc7..305b751 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1004,6 +1004,8 @@
priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
conversationSenders = getConversationSendersWithDefault(
zenPolicy.getPriorityConversationSenders(), conversationSenders);
+ } else {
+ conversationSenders = CONVERSATION_SENDERS_NONE;
}
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS,
@@ -1102,7 +1104,7 @@
return (policy.suppressedVisualEffects & visualEffect) == 0;
}
- private int getNotificationPolicySenders(@ZenPolicy.PeopleType int senders,
+ private static int getNotificationPolicySenders(@ZenPolicy.PeopleType int senders,
int defaultPolicySender) {
switch (senders) {
case ZenPolicy.PEOPLE_TYPE_ANYONE:
@@ -1116,7 +1118,7 @@
}
}
- private int getConversationSendersWithDefault(@ZenPolicy.ConversationSenders int senders,
+ private static int getConversationSendersWithDefault(@ZenPolicy.ConversationSenders int senders,
int defaultPolicySender) {
switch (senders) {
case ZenPolicy.CONVERSATION_SENDERS_ANYONE:
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
new file mode 100644
index 0000000..b63a969
--- /dev/null
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.window.flags"
+
+flag {
+ name: "allows_screen_size_decoupled_from_status_bar_and_cutout"
+ namespace: "large_screen_experiences_app_compat"
+ description: "When necessary, configuration decoupled from status bar and display cutout"
+ bug: "291870756"
+ is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 686e1fc..9d0be4b 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -40,6 +40,7 @@
/**
* Static utility methods for arrays that aren't already included in {@link java.util.Arrays}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ArrayUtils {
private static final int CACHE_SIZE = 73;
private static Object[] sCache = new Object[CACHE_SIZE];
@@ -48,35 +49,43 @@
private ArrayUtils() { /* cannot be instantiated */ }
+ @android.ravenwood.annotation.RavenwoodReplace
public static byte[] newUnpaddedByteArray(int minLen) {
return (byte[])VMRuntime.getRuntime().newUnpaddedArray(byte.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
public static char[] newUnpaddedCharArray(int minLen) {
return (char[])VMRuntime.getRuntime().newUnpaddedArray(char.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static int[] newUnpaddedIntArray(int minLen) {
return (int[])VMRuntime.getRuntime().newUnpaddedArray(int.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
public static boolean[] newUnpaddedBooleanArray(int minLen) {
return (boolean[])VMRuntime.getRuntime().newUnpaddedArray(boolean.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
public static long[] newUnpaddedLongArray(int minLen) {
return (long[])VMRuntime.getRuntime().newUnpaddedArray(long.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
public static float[] newUnpaddedFloatArray(int minLen) {
return (float[])VMRuntime.getRuntime().newUnpaddedArray(float.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
public static Object[] newUnpaddedObjectArray(int minLen) {
return (Object[])VMRuntime.getRuntime().newUnpaddedArray(Object.class, minLen);
}
+ @android.ravenwood.annotation.RavenwoodReplace
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@SuppressWarnings("unchecked")
public static <T> T[] newUnpaddedArray(Class<T> clazz, int minLen) {
diff --git a/core/res/OWNERS b/core/res/OWNERS
index 0df7c20..f24c3f5 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -1,5 +1,6 @@
adamp@google.com
asc@google.com
+austindelgado@google.com
cinek@google.com
dsandler@android.com
dsandler@google.com
@@ -8,6 +9,7 @@
hackbod@google.com
ilyamaty@google.com
jaggies@google.com
+jbolinger@google.com
jsharkey@android.com
jsharkey@google.com
juliacr@google.com
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 15c188d..cec83de 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5245,6 +5245,11 @@
<!-- Zen mode - name of default automatic calendar time-based rule that is triggered every night (when sleeping). [CHAR LIMIT=40] -->
<string name="zen_mode_default_every_night_name">Sleeping</string>
+ <!-- Zen mode - Condition summary when a rule is activated due to a call to setInterruptionFilter(). [CHAR_LIMIT=NONE] -->
+ <string name="zen_mode_implicit_activated">On</string>
+ <!-- Zen mode - Condition summary when a rule is deactivated due to a call to setInterruptionFilter(). [CHAR_LIMIT=NONE] -->
+ <string name="zen_mode_implicit_deactivated">Off</string>
+
<!-- Indication that the current volume and other effects (vibration) are being suppressed by a third party, such as a notification listener. [CHAR LIMIT=30] -->
<string name="muted_by"><xliff:g id="third_party">%1$s</xliff:g> is muting some sounds</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e19d548..16ad5c9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2574,6 +2574,8 @@
<java-symbol type="string" name="zen_mode_default_weekends_name" />
<java-symbol type="string" name="zen_mode_default_events_name" />
<java-symbol type="string" name="zen_mode_default_every_night_name" />
+ <java-symbol type="string" name="zen_mode_implicit_activated" />
+ <java-symbol type="string" name="zen_mode_implicit_deactivated" />
<java-symbol type="string" name="display_rotation_camera_compat_toast_after_rotation" />
<java-symbol type="string" name="display_rotation_camera_compat_toast_in_multi_window" />
<java-symbol type="array" name="config_system_condition_providers" />
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 083cd08..c05601b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -63,6 +63,7 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.view.Choreographer;
import android.view.Display;
import android.view.Surface;
@@ -120,6 +121,10 @@
*/
private static final int CONTENT_OVERLAY_FADE_OUT_DELAY_MS = 500;
+ private static final int EXTRA_CONTENT_OVERLAY_FADE_OUT_DELAY_MS =
+ SystemProperties.getInt(
+ "persist.wm.debug.extra_content_overlay_fade_out_delay_ms", 0);
+
private final Context mContext;
private final SyncTransactionQueue mSyncTransactionQueue;
private final PipBoundsState mPipBoundsState;
@@ -1863,7 +1868,9 @@
removeContentOverlay(surface, callback);
}
});
- animator.setStartDelay(withStartDelay ? CONTENT_OVERLAY_FADE_OUT_DELAY_MS : 0);
+ animator.setStartDelay(withStartDelay
+ ? CONTENT_OVERLAY_FADE_OUT_DELAY_MS
+ : EXTRA_CONTENT_OVERLAY_FADE_OUT_DELAY_MS);
animator.start();
}
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 d5b29e3..d5fab44 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
@@ -822,14 +822,23 @@
+ "participant");
}
- // Make sure other open changes are visible as entering PIP. Some may be hidden in
- // Transitions#setupStartState because the transition type is OPEN (such as auto-enter).
+ // Make sure other non-pip changes are handled correctly.
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change == enterPip) continue;
if (TransitionUtil.isOpeningType(change.getMode())) {
+ // For other open changes that are visible when entering PIP, some may be hidden in
+ // Transitions#setupStartState because the transition type is OPEN (such as
+ // auto-enter).
final SurfaceControl leash = change.getLeash();
startTransaction.show(leash).setAlpha(leash, 1.f);
+ } else if (TransitionUtil.isClosingType(change.getMode())) {
+ // For other close changes that are invisible as entering PIP, hide them immediately
+ // to avoid showing a freezing surface.
+ // Ideally, we should let other handler to handle them (likely RemoteHandler by
+ // Launcher).
+ final SurfaceControl leash = change.getLeash();
+ startTransaction.hide(leash);
}
}
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index e706eb0..d55d28d 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -527,52 +527,65 @@
return Frame(surface->logicalWidth(), surface->logicalHeight(), bufferAge);
}
-struct DestroySemaphoreInfo {
+class SharedSemaphoreInfo : public LightRefBase<SharedSemaphoreInfo> {
PFN_vkDestroySemaphore mDestroyFunction;
VkDevice mDevice;
VkSemaphore mSemaphore;
+ GrBackendSemaphore mGrBackendSemaphore;
- DestroySemaphoreInfo(PFN_vkDestroySemaphore destroyFunction, VkDevice device,
- VkSemaphore semaphore)
- : mDestroyFunction(destroyFunction), mDevice(device), mSemaphore(semaphore) {}
+ SharedSemaphoreInfo(PFN_vkDestroySemaphore destroyFunction, VkDevice device,
+ VkSemaphore semaphore)
+ : mDestroyFunction(destroyFunction), mDevice(device), mSemaphore(semaphore) {
+ mGrBackendSemaphore.initVulkan(semaphore);
+ }
- ~DestroySemaphoreInfo() { mDestroyFunction(mDevice, mSemaphore, nullptr); }
+ ~SharedSemaphoreInfo() { mDestroyFunction(mDevice, mSemaphore, nullptr); }
+
+ friend class LightRefBase<SharedSemaphoreInfo>;
+ friend class sp<SharedSemaphoreInfo>;
+
+public:
+ VkSemaphore semaphore() const { return mSemaphore; }
+
+ GrBackendSemaphore* grBackendSemaphore() { return &mGrBackendSemaphore; }
};
static void destroy_semaphore(void* context) {
- DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(context);
- delete info;
+ SharedSemaphoreInfo* info = reinterpret_cast<SharedSemaphoreInfo*>(context);
+ info->decStrong(0);
}
VulkanManager::VkDrawResult VulkanManager::finishFrame(SkSurface* surface) {
ATRACE_NAME("Vulkan finish frame");
- VkExportSemaphoreCreateInfo exportInfo;
- exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
- exportInfo.pNext = nullptr;
- exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
-
- VkSemaphoreCreateInfo semaphoreInfo;
- semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
- semaphoreInfo.pNext = &exportInfo;
- semaphoreInfo.flags = 0;
- VkSemaphore semaphore;
- VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
- ALOGE_IF(VK_SUCCESS != err, "VulkanManager::makeSwapSemaphore(): Failed to create semaphore");
-
- GrBackendSemaphore backendSemaphore;
- backendSemaphore.initVulkan(semaphore);
-
+ sp<SharedSemaphoreInfo> sharedSemaphore;
GrFlushInfo flushInfo;
- if (err == VK_SUCCESS) {
- flushInfo.fNumSemaphores = 1;
- flushInfo.fSignalSemaphores = &backendSemaphore;
- flushInfo.fFinishedProc = destroy_semaphore;
- flushInfo.fFinishedContext =
- new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore);
- } else {
- semaphore = VK_NULL_HANDLE;
+
+ {
+ VkExportSemaphoreCreateInfo exportInfo;
+ exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
+ exportInfo.pNext = nullptr;
+ exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+ VkSemaphoreCreateInfo semaphoreInfo;
+ semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphoreInfo.pNext = &exportInfo;
+ semaphoreInfo.flags = 0;
+ VkSemaphore semaphore;
+ VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+ ALOGE_IF(VK_SUCCESS != err,
+ "VulkanManager::makeSwapSemaphore(): Failed to create semaphore");
+
+ if (err == VK_SUCCESS) {
+ sharedSemaphore = sp<SharedSemaphoreInfo>::make(mDestroySemaphore, mDevice, semaphore);
+ flushInfo.fNumSemaphores = 1;
+ flushInfo.fSignalSemaphores = sharedSemaphore->grBackendSemaphore();
+ flushInfo.fFinishedProc = destroy_semaphore;
+ sharedSemaphore->incStrong(0);
+ flushInfo.fFinishedContext = sharedSemaphore.get();
+ }
}
+
GrDirectContext* context = GrAsDirectContext(surface->recordingContext());
ALOGE_IF(!context, "Surface is not backed by gpu");
GrSemaphoresSubmitted submitted = context->flush(
@@ -581,37 +594,34 @@
VkDrawResult drawResult{
.submissionTime = systemTime(),
};
- if (semaphore != VK_NULL_HANDLE) {
- if (submitted == GrSemaphoresSubmitted::kYes) {
- if (mFrameBoundaryANDROID) {
- // retrieve VkImage used as render target
- VkImage image = VK_NULL_HANDLE;
- GrBackendRenderTarget backendRenderTarget =
- SkSurfaces::GetBackendRenderTarget(
- surface, SkSurfaces::BackendHandleAccess::kFlushRead);
- if (backendRenderTarget.isValid()) {
- GrVkImageInfo info;
- if (GrBackendRenderTargets::GetVkImageInfo(backendRenderTarget, &info)) {
- image = info.fImage;
- } else {
- ALOGE("Frame boundary: backend is not vulkan");
- }
+ if (sharedSemaphore) {
+ if (submitted == GrSemaphoresSubmitted::kYes && mFrameBoundaryANDROID) {
+ // retrieve VkImage used as render target
+ VkImage image = VK_NULL_HANDLE;
+ GrBackendRenderTarget backendRenderTarget = SkSurfaces::GetBackendRenderTarget(
+ surface, SkSurfaces::BackendHandleAccess::kFlushRead);
+ if (backendRenderTarget.isValid()) {
+ GrVkImageInfo info;
+ if (GrBackendRenderTargets::GetVkImageInfo(backendRenderTarget, &info)) {
+ image = info.fImage;
} else {
- ALOGE("Frame boundary: invalid backend render target");
+ ALOGE("Frame boundary: backend is not vulkan");
}
- // frameBoundaryANDROID needs to know about mSwapSemaphore, but
- // it won't wait on it.
- mFrameBoundaryANDROID(mDevice, semaphore, image);
+ } else {
+ ALOGE("Frame boundary: invalid backend render target");
}
+ // frameBoundaryANDROID needs to know about mSwapSemaphore, but
+ // it won't wait on it.
+ mFrameBoundaryANDROID(mDevice, sharedSemaphore->semaphore(), image);
}
VkSemaphoreGetFdInfoKHR getFdInfo;
getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
getFdInfo.pNext = nullptr;
- getFdInfo.semaphore = semaphore;
+ getFdInfo.semaphore = sharedSemaphore->semaphore();
getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
int fenceFd = -1;
- err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
+ VkResult err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to get semaphore Fd");
drawResult.presentFence.reset(fenceFd);
} else {
@@ -732,15 +742,15 @@
return INVALID_OPERATION;
}
- GrBackendSemaphore backendSemaphore;
- backendSemaphore.initVulkan(semaphore);
+ auto sharedSemaphore = sp<SharedSemaphoreInfo>::make(mDestroySemaphore, mDevice, semaphore);
// Even if Skia fails to submit the semaphore, it will still call the destroy_semaphore callback
GrFlushInfo flushInfo;
flushInfo.fNumSemaphores = 1;
- flushInfo.fSignalSemaphores = &backendSemaphore;
+ flushInfo.fSignalSemaphores = sharedSemaphore->grBackendSemaphore();
flushInfo.fFinishedProc = destroy_semaphore;
- flushInfo.fFinishedContext = new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore);
+ sharedSemaphore->incStrong(0);
+ flushInfo.fFinishedContext = sharedSemaphore.get();
GrSemaphoresSubmitted submitted = grContext->flush(flushInfo);
grContext->submit();
diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java
index 5a27435..2b349d4 100644
--- a/media/java/android/media/AudioDeviceAttributes.java
+++ b/media/java/android/media/AudioDeviceAttributes.java
@@ -325,7 +325,7 @@
+ " role:" + roleToString(mRole)
+ " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(mNativeType)
: AudioSystem.getInputDeviceName(mNativeType))
- + " addr:" + mAddress
+ + " addr:" + Utils.anonymizeBluetoothAddress(mNativeType, mAddress)
+ " name:" + mName
+ " profiles:" + mAudioProfiles.toString()
+ " descriptors:" + mAudioDescriptors.toString());
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
index 9211c53..73bc6f9 100644
--- a/media/java/android/media/AudioDevicePort.java
+++ b/media/java/android/media/AudioDevicePort.java
@@ -156,7 +156,7 @@
AudioSystem.getOutputDeviceName(mType));
return "{" + super.toString()
+ ", mType: " + type
- + ", mAddress: " + mAddress
+ + ", mAddress: " + Utils.anonymizeBluetoothAddress(mType, mAddress)
+ "}";
}
}
diff --git a/media/java/android/media/Utils.java b/media/java/android/media/Utils.java
index ecb6b3d..d07f611 100644
--- a/media/java/android/media/Utils.java
+++ b/media/java/android/media/Utils.java
@@ -657,4 +657,35 @@
// on the fly.
private final boolean mForceRemoveConsistency; // default false
}
+
+ /**
+ * Convert a Bluetooth MAC address to an anonymized one when exposed to a non privileged app
+ * Must match the implementation of BluetoothUtils.toAnonymizedAddress()
+ * @param address MAC address to be anonymized
+ * @return anonymized MAC address
+ */
+ public static @Nullable String anonymizeBluetoothAddress(@Nullable String address) {
+ if (address == null) {
+ return null;
+ }
+ if (address.length() != "AA:BB:CC:DD:EE:FF".length()) {
+ return address;
+ }
+ return "XX:XX:XX:XX" + address.substring("XX:XX:XX:XX".length());
+ }
+
+ /**
+ * Convert a Bluetooth MAC address to an anonymized one if the internal device type corresponds
+ * to a Bluetooth.
+ * @param deviceType the internal type of the audio device
+ * @param address MAC address to be anonymized
+ * @return anonymized MAC address
+ */
+ public static @Nullable String anonymizeBluetoothAddress(
+ int deviceType, @Nullable String address) {
+ if (!AudioSystem.isBluetoothDevice(deviceType)) {
+ return address;
+ }
+ return anonymizeBluetoothAddress(address);
+ }
}
diff --git a/media/java/android/media/tv/ad/TvAdServiceInfo.aidl b/media/java/android/media/tv/ad/TvAdServiceInfo.aidl
new file mode 100644
index 0000000..a5e631f
--- /dev/null
+++ b/media/java/android/media/tv/ad/TvAdServiceInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.media.tv.ad;
+
+parcelable TvAdServiceInfo;
diff --git a/media/java/android/media/tv/ad/TvAdServiceInfo.java b/media/java/android/media/tv/ad/TvAdServiceInfo.java
new file mode 100644
index 0000000..ed04f1f
--- /dev/null
+++ b/media/java/android/media/tv/ad/TvAdServiceInfo.java
@@ -0,0 +1,194 @@
+/*
+ * 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.media.tv.ad;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import androidx.annotation.NonNull;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is used to specify meta information of a TV AD service.
+ * @hide
+ */
+public final class TvAdServiceInfo implements Parcelable {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "TvAdServiceInfo";
+
+ private static final String XML_START_TAG_NAME = "tv-ad-service";
+
+ private final ResolveInfo mService;
+ private final String mId;
+ private final List<String> mTypes = new ArrayList<>();
+
+ /**
+ * Constructs a TvAdServiceInfo object.
+ *
+ * @param context the application context
+ * @param component the component name of the TvAdService
+ */
+ public TvAdServiceInfo(@NonNull Context context, @NonNull ComponentName component) {
+ if (context == null) {
+ throw new IllegalArgumentException("context cannot be null.");
+ }
+ // TODO: use a constant
+ Intent intent = new Intent("android.media.tv.ad.TvAdService").setComponent(component);
+ ResolveInfo resolveInfo = context.getPackageManager().resolveService(
+ intent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+ if (resolveInfo == null) {
+ throw new IllegalArgumentException("Invalid component. Can't find the service.");
+ }
+
+ ComponentName componentName = new ComponentName(resolveInfo.serviceInfo.packageName,
+ resolveInfo.serviceInfo.name);
+ String id;
+ id = generateAdServiceId(componentName);
+ List<String> types = new ArrayList<>();
+ parseServiceMetadata(resolveInfo, context, types);
+
+ mService = resolveInfo;
+ mId = id;
+ }
+
+ private TvAdServiceInfo(ResolveInfo service, String id, List<String> types) {
+ mService = service;
+ mId = id;
+ mTypes.addAll(types);
+ }
+
+ private TvAdServiceInfo(@NonNull Parcel in) {
+ mService = ResolveInfo.CREATOR.createFromParcel(in);
+ mId = in.readString();
+ in.readStringList(mTypes);
+ }
+
+ public static final Creator<TvAdServiceInfo> CREATOR = new Creator<TvAdServiceInfo>() {
+ @Override
+ public TvAdServiceInfo createFromParcel(Parcel in) {
+ return new TvAdServiceInfo(in);
+ }
+
+ @Override
+ public TvAdServiceInfo[] newArray(int size) {
+ return new TvAdServiceInfo[size];
+ }
+ };
+
+ /**
+ * Returns a unique ID for this TV AD service. The ID is generated from the package and class
+ * name implementing the TV AD service.
+ */
+ @NonNull
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * Returns the component of the TV AD service.
+ * @hide
+ */
+ public ComponentName getComponent() {
+ return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
+ }
+
+ /**
+ * Returns the information of the service that implements this AD service.
+ */
+ @Nullable
+ public ServiceInfo getServiceInfo() {
+ return mService.serviceInfo;
+ }
+
+ /**
+ * Gets supported TV AD types.
+ */
+ @NonNull
+ public List<String> getSupportedTypes() {
+ return mTypes;
+ }
+
+ private static String generateAdServiceId(ComponentName name) {
+ return name.flattenToShortString();
+ }
+
+ private static void parseServiceMetadata(
+ ResolveInfo resolveInfo, Context context, List<String> types) {
+ ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ PackageManager pm = context.getPackageManager();
+ // TODO: use constant for the metadata
+ try (XmlResourceParser parser =
+ serviceInfo.loadXmlMetaData(pm, "android.media.tv.ad.service")) {
+ if (parser == null) {
+ throw new IllegalStateException(
+ "No " + "android.media.tv.ad.service"
+ + " meta-data found for " + serviceInfo.name);
+ }
+
+ Resources resources = pm.getResourcesForApplication(serviceInfo.applicationInfo);
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ // move to the START_TAG
+ }
+
+ String nodeName = parser.getName();
+ if (!XML_START_TAG_NAME.equals(nodeName)) {
+ throw new IllegalStateException("Meta-data does not start with "
+ + XML_START_TAG_NAME + " tag for " + serviceInfo.name);
+ }
+
+ // TODO: parse attributes
+ } catch (IOException | XmlPullParserException e) {
+ throw new IllegalStateException(
+ "Failed reading meta-data for " + serviceInfo.packageName, e);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalStateException("No resources found for " + serviceInfo.packageName, e);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ mService.writeToParcel(dest, flags);
+ dest.writeString(mId);
+ dest.writeStringList(mTypes);
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
index 1c92696..223e99e 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -57,19 +57,24 @@
private fun UserManager.getUserGroups(): List<UserGroup> {
val userGroupList = mutableListOf<UserGroup>()
- val profileToShowInSettings = getProfiles(UserHandle.myUserId())
- .map { userInfo -> userInfo to getUserProperties(userInfo.userHandle) }
+ val showInSettingsMap = getProfiles(UserHandle.myUserId()).groupBy { showInSettings(it) }
- profileToShowInSettings
- .filter { it.second.showInSettings == UserProperties.SHOW_IN_SETTINGS_WITH_PARENT }
- .takeIf { it.isNotEmpty() }
- ?.map { it.first }
- ?.let { userInfos -> userGroupList += UserGroup(userInfos) }
+ showInSettingsMap[UserProperties.SHOW_IN_SETTINGS_WITH_PARENT]?.let {
+ userGroupList += UserGroup(it)
+ }
- profileToShowInSettings
- .filter { it.second.showInSettings == UserProperties.SHOW_IN_SETTINGS_SEPARATE &&
- (!it.second.hideInSettingsInQuietMode || !it.first.isQuietModeEnabled) }
- .forEach { userGroupList += UserGroup(userInfos = listOf(it.first)) }
+ showInSettingsMap[UserProperties.SHOW_IN_SETTINGS_SEPARATE]?.forEach {
+ userGroupList += UserGroup(listOf(it))
+ }
return userGroupList
}
+
+private fun UserManager.showInSettings(userInfo: UserInfo): Int {
+ val userProperties = getUserProperties(userInfo.userHandle)
+ return if (userInfo.isQuietModeEnabled && userProperties.hideInSettingsInQuietMode) {
+ UserProperties.SHOW_IN_SETTINGS_NO
+ } else {
+ userProperties.showInSettings
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/common/UserProfilePagerTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/common/UserProfilePagerTest.kt
new file mode 100644
index 0000000..e450364
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/common/UserProfilePagerTest.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.settingslib.spaprivileged.template.common
+
+import android.content.Context
+import android.content.pm.UserInfo
+import android.content.pm.UserProperties
+import android.os.UserManager
+import androidx.compose.material3.Text
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+
+@RunWith(AndroidJUnit4::class)
+class UserProfilePagerTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ private val mockUserManager = mock<UserManager> {
+ on { getProfiles(any()) } doReturn listOf(USER_0)
+ on { getUserProperties(USER_0.userHandle) } doReturn
+ UserProperties.Builder()
+ .setShowInSettings(UserProperties.SHOW_IN_LAUNCHER_WITH_PARENT)
+ .build()
+ }
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { userManager } doReturn mockUserManager
+ }
+
+ @Test
+ fun userProfilePager() {
+ composeTestRule.setContent {
+ CompositionLocalProvider(LocalContext provides context) {
+ UserProfilePager { userGroup ->
+ Text(text = userGroup.userInfos.joinToString { it.id.toString() })
+ }
+ }
+ }
+
+ composeTestRule.onNodeWithText(USER_0.id.toString()).assertIsDisplayed()
+ }
+
+ private companion object {
+ val USER_0 = UserInfo(0, "", 0)
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 1429782..0569431 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -25,7 +25,6 @@
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
@@ -164,11 +163,7 @@
@Composable
private fun Umo(viewModel: CommunalViewModel, modifier: Modifier = Modifier) {
AndroidView(
- modifier =
- modifier
- .width(Dimensions.CardWidth)
- .height(Dimensions.CardHeightThird)
- .padding(Dimensions.Spacing),
+ modifier = modifier,
factory = {
viewModel.mediaHost.expansion = MediaHostState.EXPANDED
viewModel.mediaHost.showsOnlyActiveMedia = false
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index aae61bd..b77a60b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -506,7 +506,10 @@
// There is no ongoing transition.
if (state !is TransitionState.Transition || state.fromScene == state.toScene) {
- return idleValue
+ // Even if this element SceneTransitionLayout is not animated, the layout itself might be
+ // animated (e.g. by another parent SceneTransitionLayout), in which case this element still
+ // need to participate in the layout phase.
+ return currentValue()
}
// A transition was started but it's not ready yet (not all elements have been composed/laid
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt
index 216608a..5d8eaf7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt
@@ -2,9 +2,10 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.unit.IntSize
interface DraggableHandler {
- fun onDragStarted(startedPosition: Offset, pointersDown: Int = 1)
+ fun onDragStarted(layoutSize: IntSize, startedPosition: Offset, pointersDown: Int = 1)
fun onDelta(pixels: Float)
fun onDragStopped(velocity: Float)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index d0a5f5b..d48781a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -38,6 +38,7 @@
import androidx.compose.ui.input.pointer.util.VelocityTracker
import androidx.compose.ui.input.pointer.util.addPointerInputChange
import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.util.fastForEach
@@ -60,7 +61,7 @@
orientation: Orientation,
enabled: Boolean,
startDragImmediately: Boolean,
- onDragStarted: (startedPosition: Offset, pointersDown: Int) -> Unit,
+ onDragStarted: (layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) -> Unit,
onDragDelta: (Float) -> Unit,
onDragStopped: (velocity: Float) -> Unit,
): Modifier = composed {
@@ -83,7 +84,7 @@
val onDragStart: (Offset, Int) -> Unit = { startedPosition, pointersDown ->
velocityTracker.resetTracking()
- onDragStarted(startedPosition, pointersDown)
+ onDragStarted(size, startedPosition, pointersDown)
}
val onDragCancel: () -> Unit = { onDragStopped(/* velocity= */ 0f) }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
index eb5168b..1a79522 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
@@ -25,8 +25,9 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.SnapshotStateMap
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.layout.intermediateLayout
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.zIndex
@@ -44,14 +45,24 @@
var content by mutableStateOf(content)
var userActions by mutableStateOf(actions)
var zIndex by mutableFloatStateOf(zIndex)
- var size by mutableStateOf(IntSize.Zero)
+ var targetSize by mutableStateOf(IntSize.Zero)
/** The shared values in this scene that are not tied to a specific element. */
val sharedValues = SnapshotStateMap<ValueKey, Element.SharedValue<*>>()
@Composable
+ @OptIn(ExperimentalComposeUiApi::class)
fun Content(modifier: Modifier = Modifier) {
- Box(modifier.zIndex(zIndex).onPlaced { size = it.size }.testTag(key.testTag)) {
+ Box(
+ modifier
+ .zIndex(zIndex)
+ .intermediateLayout { measurable, constraints ->
+ targetSize = lookaheadSize
+ val placeable = measurable.measure(constraints)
+ layout(placeable.width, placeable.height) { placeable.place(0, 0) }
+ }
+ .testTag(key.testTag)
+ ) {
scope.content()
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
index 9a3a0ae..9d71801 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
@@ -27,6 +27,7 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
@@ -90,7 +91,7 @@
internal var gestureWithPriority: Any? = null
- internal fun onDragStarted(pointersDown: Int, startedPosition: Offset?) {
+ internal fun onDragStarted(pointersDown: Int, layoutSize: IntSize, startedPosition: Offset?) {
if (isDrivingTransition) {
// This [transition] was already driving the animation: simply take over it.
// Stop animating and start from where the current offset.
@@ -126,14 +127,14 @@
// we will also have to make sure that we correctly handle overscroll.
swipeTransition.absoluteDistance =
when (orientation) {
- Orientation.Horizontal -> layoutImpl.size.width
- Orientation.Vertical -> layoutImpl.size.height
+ Orientation.Horizontal -> layoutSize.width
+ Orientation.Vertical -> layoutSize.height
}.toFloat()
val fromEdge =
startedPosition?.let { position ->
layoutImpl.edgeDetector.edge(
- layoutImpl.size,
+ layoutSize,
position.round(),
layoutImpl.density,
orientation,
@@ -513,9 +514,9 @@
private class SceneDraggableHandler(
private val gestureHandler: SceneGestureHandler,
) : DraggableHandler {
- override fun onDragStarted(startedPosition: Offset, pointersDown: Int) {
+ override fun onDragStarted(layoutSize: IntSize, startedPosition: Offset, pointersDown: Int) {
gestureHandler.gestureWithPriority = this
- gestureHandler.onDragStarted(pointersDown, startedPosition)
+ gestureHandler.onDragStarted(pointersDown, layoutSize, startedPosition)
}
override fun onDelta(pixels: Float) {
@@ -647,7 +648,11 @@
canContinueScroll = { true },
onStart = {
gestureHandler.gestureWithPriority = this
- gestureHandler.onDragStarted(pointersDown = 1, startedPosition = null)
+ gestureHandler.onDragStarted(
+ pointersDown = 1,
+ layoutSize = gestureHandler.currentScene.targetSize,
+ startedPosition = null,
+ )
},
onScroll = { offsetAvailable ->
if (gestureHandler.gestureWithPriority != this) {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 6edd1b6..0b06953 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -30,13 +30,15 @@
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.SnapshotStateMap
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.layout.LookaheadScope
-import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.layout.intermediateLayout
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.util.fastForEach
+import com.android.compose.ui.util.lerp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel
@@ -64,12 +66,6 @@
private val horizontalGestureHandler: SceneGestureHandler
private val verticalGestureHandler: SceneGestureHandler
- /**
- * The size of this layout. Note that this could be [IntSize.Zero] if this layour does not have
- * any scene configured or right before the first measure pass of the layout.
- */
- @VisibleForTesting var size by mutableStateOf(IntSize.Zero)
-
init {
setScenes(builder)
@@ -157,15 +153,46 @@
}
@Composable
+ @OptIn(ExperimentalComposeUiApi::class)
internal fun Content(modifier: Modifier) {
Box(
modifier
// Handle horizontal and vertical swipes on this layout.
// Note: order here is important and will give a slight priority to the vertical
// swipes.
- .swipeToScene(gestureHandler(Orientation.Horizontal))
- .swipeToScene(gestureHandler(Orientation.Vertical))
- .onSizeChanged { size = it }
+ .swipeToScene(horizontalGestureHandler)
+ .swipeToScene(verticalGestureHandler)
+ // Animate the size of this layout.
+ .intermediateLayout { measurable, constraints ->
+ // Measure content normally.
+ val placeable = measurable.measure(constraints)
+
+ val width: Int
+ val height: Int
+ val state = state.transitionState
+ if (state !is TransitionState.Transition || state.fromScene == state.toScene) {
+ width = placeable.width
+ height = placeable.height
+ } else {
+ // Interpolate the size.
+ val fromSize = scene(state.fromScene).targetSize
+ val toSize = scene(state.toScene).targetSize
+
+ // Optimization: make sure we don't read state.progress if fromSize ==
+ // toSize to avoid running this code every frame when the layout size does
+ // not change.
+ if (fromSize == toSize) {
+ width = fromSize.width
+ height = fromSize.height
+ } else {
+ val size = lerp(fromSize, toSize, state.progress)
+ width = size.width.coerceAtLeast(0)
+ height = size.height.coerceAtLeast(0)
+ }
+ }
+
+ layout(width, height) { placeable.place(0, 0) }
+ }
) {
LookaheadScope {
val scenesToCompose =
@@ -230,4 +257,9 @@
}
internal fun isSceneReady(scene: SceneKey): Boolean = readyScenes.containsKey(scene)
+
+ @VisibleForTesting
+ fun setScenesTargetSizeForTest(size: IntSize) {
+ scenes.values.forEach { it.targetSize = size }
+ }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
index 840800d..70534dd 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
@@ -38,7 +38,7 @@
transition: TransitionState.Transition,
value: Offset
): Offset {
- val sceneSize = scene.size
+ val sceneSize = scene.targetSize
val elementSize = sceneValues.targetSize
if (elementSize == Element.SizeUnspecified) {
return value
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
index 1e3d011..7ab2096 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
@@ -46,6 +46,7 @@
import org.junit.runner.RunWith
private const val SCREEN_SIZE = 100f
+private val LAYOUT_SIZE = IntSize(SCREEN_SIZE.toInt(), SCREEN_SIZE.toInt())
@RunWith(AndroidJUnit4::class)
class SceneGestureHandlerTest {
@@ -80,7 +81,7 @@
edgeDetector = DefaultEdgeDetector,
coroutineScope = coroutineScope,
)
- .also { it.size = IntSize(SCREEN_SIZE.toInt(), SCREEN_SIZE.toInt()) },
+ .apply { setScenesTargetSizeForTest(LAYOUT_SIZE) },
orientation = Orientation.Vertical,
coroutineScope = coroutineScope,
)
@@ -128,18 +129,21 @@
runMonotonicClockTest { TestGestureScope(coroutineScope = this).block() }
}
+ private fun DraggableHandler.onDragStarted() =
+ onDragStarted(layoutSize = LAYOUT_SIZE, startedPosition = Offset.Zero)
+
@Test
fun testPreconditions() = runGestureTest { assertScene(currentScene = SceneA, isIdle = true) }
@Test
fun onDragStarted_shouldStartATransition() = runGestureTest {
- draggable.onDragStarted(startedPosition = Offset.Zero)
+ draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
}
@Test
fun afterSceneTransitionIsStarted_interceptDragEvents() = runGestureTest {
- draggable.onDragStarted(startedPosition = Offset.Zero)
+ draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
val transition = transitionState as Transition
@@ -152,7 +156,7 @@
@Test
fun onDragStoppedAfterDrag_velocityLowerThanThreshold_remainSameScene() = runGestureTest {
- draggable.onDragStarted(startedPosition = Offset.Zero)
+ draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
draggable.onDelta(pixels = deltaInPixels10)
@@ -170,7 +174,7 @@
@Test
fun onDragStoppedAfterDrag_velocityAtLeastThreshold_goToNextScene() = runGestureTest {
- draggable.onDragStarted(startedPosition = Offset.Zero)
+ draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
draggable.onDelta(pixels = deltaInPixels10)
@@ -188,7 +192,7 @@
@Test
fun onDragStoppedAfterStarted_returnImmediatelyToIdle() = runGestureTest {
- draggable.onDragStarted(startedPosition = Offset.Zero)
+ draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
draggable.onDragStopped(velocity = 0f)
@@ -197,7 +201,7 @@
@Test
fun startGestureDuringAnimatingOffset_shouldImmediatelyStopTheAnimation() = runGestureTest {
- draggable.onDragStarted(startedPosition = Offset.Zero)
+ draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
draggable.onDelta(pixels = deltaInPixels10)
@@ -217,7 +221,7 @@
assertScene(currentScene = SceneC, isIdle = false)
// Start a new gesture while the offset is animating
- draggable.onDragStarted(startedPosition = Offset.Zero)
+ draggable.onDragStarted()
assertThat(sceneGestureHandler.isAnimatingOffset).isFalse()
}
@@ -421,6 +425,7 @@
draggable.onDelta(deltaInPixels10)
assertScene(currentScene = SceneA, isIdle = true)
}
+
@Test
fun beforeDraggableStart_stop_shouldBeIgnored() = runGestureTest {
draggable.onDragStopped(velocityThreshold)
@@ -437,7 +442,7 @@
@Test
fun startNestedScrollWhileDragging() = runGestureTest {
val nestedScroll = nestedScrollConnection(nestedScrollBehavior = Always)
- draggable.onDragStarted(Offset.Zero)
+ draggable.onDragStarted()
assertScene(currentScene = SceneA, isIdle = false)
val transition = transitionState as Transition
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index 5afd420..321cf63 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -18,6 +18,8 @@
import androidx.activity.ComponentActivity
import androidx.compose.animation.core.FastOutSlowInEasing
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
@@ -48,6 +50,7 @@
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.test.assertSizeIsEqualTo
import com.android.compose.test.subjects.DpOffsetSubject
import com.android.compose.test.subjects.assertThat
import com.google.common.truth.Truth.assertThat
@@ -307,6 +310,26 @@
assertThat(layoutState.transitionState.currentScene).isEqualTo(TestScenes.SceneA)
}
+ @Test
+ fun layoutSizeIsAnimated() {
+ val layoutTag = "layout"
+ rule.testTransition(
+ fromSceneContent = { Box(Modifier.size(200.dp, 100.dp)) },
+ toSceneContent = { Box(Modifier.size(120.dp, 140.dp)) },
+ transition = {
+ // 4 frames of animation.
+ spec = tween(4 * 16, easing = LinearEasing)
+ },
+ layoutModifier = Modifier.testTag(layoutTag),
+ ) {
+ before { rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(200.dp, 100.dp) }
+ at(16) { rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(180.dp, 110.dp) }
+ at(32) { rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(160.dp, 120.dp) }
+ at(48) { rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(140.dp, 130.dp) }
+ after { rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(120.dp, 140.dp) }
+ }
+ }
+
private fun SemanticsNodeInteraction.offsetRelativeTo(
other: SemanticsNodeInteraction,
): DpOffset {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EdgeTranslateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EdgeTranslateTest.kt
index 2a27763..8cffcf6 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EdgeTranslateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EdgeTranslateTest.kt
@@ -48,7 +48,7 @@
rule.testTransition(
// The layout under test is 300dp x 300dp.
layoutModifier = Modifier.size(300.dp),
- fromSceneContent = {},
+ fromSceneContent = { Box(Modifier.fillMaxSize()) },
toSceneContent = {
// Foo is 100dp x 100dp in the center of the layout, so at offset = (100dp, 100dp)
Box(Modifier.fillMaxSize()) {
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
index e0ae1be..06de296 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
@@ -16,7 +16,6 @@
package com.android.compose.animation.scene
-import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -104,7 +103,7 @@
currentScene,
onChangeScene,
transitions { from(fromScene, to = toScene, transition) },
- layoutModifier.fillMaxSize(),
+ layoutModifier,
) {
scene(fromScene, content = fromSceneContent)
scene(toScene, content = toSceneContent)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
index 15d4d20..67ce86b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
@@ -25,7 +25,7 @@
import com.android.internal.logging.testing.FakeMetricsLogger
import com.android.internal.util.EmergencyAffordanceManager
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER
import com.android.systemui.log.table.TableLogBuffer
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml b/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml
index be8fe8c..ff1146e 100644
--- a/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml
@@ -19,9 +19,14 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
- <path android:fillColor="@color/ksh_key_item_color"
- android:pathData="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91
-3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27 .28 v.79l5 4.99L20.49
-19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5
-14z" />
+ <path android:pathData="M5.5,5.5m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"
+ android:fillColor="@color/ksh_key_item_color" />
+ <path android:pathData="M5.5,16.5m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"
+ android:fillColor="@color/ksh_key_item_color" />
+ <path android:pathData="M16.5,5.5m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"
+ android:fillColor="@color/ksh_key_item_color" />
+ <path android:pathData="M18.5,16.5C18.5,15.4 17.6,14.5 16.5,14.5C15.4,14.5 14.5,15.4 14.5,16.5C14.5,17.6 15.4,18.5 16.5,18.5C17.6,18.5 18.5,17.6 18.5,16.5ZM12.5,16.5C12.5,14.29 14.29,12.5 16.5,12.5C18.71,12.5 20.5,14.29 20.5,16.5C20.5,17.241 20.299,17.934 19.948,18.529L23,21.59L21.59,23L18.529,19.948C17.934,20.299 17.241,20.5 16.5,20.5C14.29,20.5 12.5,18.71 12.5,16.5Z"
+ android:fillColor="@color/ksh_key_item_color"
+ android:fillType="evenOdd" />
</vector>
+
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8780f58..9a3c6d5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3266,4 +3266,9 @@
<string name="privacy_dialog_active_app_usage_2">In use by <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> (<xliff:g id="attribution_label" example="For Wallet">%2$s</xliff:g> \u2022 <xliff:g id="proxy_label" example="Speech services">%3$s</xliff:g>)</string>
<!-- Label for recent app usage of a phone sensor with sub-attribution and proxy label in the privacy dialog [CHAR LIMIT=NONE] -->
<string name="privacy_dialog_recent_app_usage_2">Recently used by <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> (<xliff:g id="attribution_label" example="For Wallet">%2$s</xliff:g> \u2022 <xliff:g id="proxy_label" example="Speech services">%3$s</xliff:g>)</string>
+
+ <!-- Content description for keyboard backlight brightness dialog [CHAR LIMIT=NONE] -->
+ <string name="keyboard_backlight_dialog_title">Keyboard backlight</string>
+ <!-- Content description for keyboard backlight brightness value [CHAR LIMIT=NONE] -->
+ <string name="keyboard_backlight_value">Level %1$d of %2$d</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index f091558..07359d1 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -60,10 +60,10 @@
import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.biometrics.UdfpsController;
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
-import com.android.systemui.bouncer.domain.interactor.BouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -127,7 +127,7 @@
@NonNull private final KeyguardTransitionInteractor mTransitionInteractor;
@NonNull private final KeyguardInteractor mKeyguardInteractor;
@NonNull private final View.AccessibilityDelegate mAccessibilityDelegate;
- @NonNull private final Lazy<BouncerInteractor> mBouncerInteractor;
+ @NonNull private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractor;
@NonNull private final SceneContainerFlags mSceneContainerFlags;
// Tracks the velocity of a touch to help filter out the touches that move too fast.
@@ -205,7 +205,7 @@
@NonNull FeatureFlags featureFlags,
PrimaryBouncerInteractor primaryBouncerInteractor,
Context context,
- Lazy<BouncerInteractor> bouncerInteractor,
+ Lazy<DeviceEntryInteractor> deviceEntryInteractor,
SceneContainerFlags sceneContainerFlags
) {
mStatusBarStateController = statusBarStateController;
@@ -232,7 +232,7 @@
dumpManager.registerDumpable(TAG, this);
mResources = resources;
mContext = context;
- mBouncerInteractor = bouncerInteractor;
+ mDeviceEntryInteractor = deviceEntryInteractor;
mSceneContainerFlags = sceneContainerFlags;
mAccessibilityDelegate = new View.AccessibilityDelegate() {
@@ -747,7 +747,7 @@
vibrateOnLongPress();
if (mSceneContainerFlags.isEnabled()) {
- mBouncerInteractor.get().showOrUnlockDevice(null);
+ mDeviceEntryInteractor.get().attemptDeviceEntry();
} else {
mKeyguardViewController.showPrimaryBouncer(/* scrim */ true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index 80be008..ee3a55f 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -25,7 +25,7 @@
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockscreenCredential
import com.android.keyguard.KeyguardSecurityModel
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationResultModel
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -45,7 +45,9 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
@@ -65,7 +67,13 @@
* Note that the length of the PIN is also important to take into consideration, please see
* [hintedPinLength].
*/
- val isAutoConfirmEnabled: StateFlow<Boolean>
+ val isAutoConfirmFeatureEnabled: StateFlow<Boolean>
+
+ /**
+ * Emits the result whenever a PIN/Pattern/Password security challenge is attempted by the user
+ * in order to unlock the device.
+ */
+ val authenticationChallengeResult: SharedFlow<Boolean>
/**
* The exact length a PIN should be for us to enable PIN length hinting.
@@ -152,18 +160,19 @@
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>,
@Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>,
private val userRepository: UserRepository,
private val lockPatternUtils: LockPatternUtils,
broadcastDispatcher: BroadcastDispatcher,
) : AuthenticationRepository {
- override val isAutoConfirmEnabled: StateFlow<Boolean> =
+ override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
refreshingFlow(
initialValue = false,
getFreshValue = lockPatternUtils::isAutoPinConfirmEnabled,
)
+ override val authenticationChallengeResult = MutableSharedFlow<Boolean>()
override val hintedPinLength: Int = 6
@@ -224,6 +233,7 @@
} else {
lockPatternUtils.reportFailedPasswordAttempt(selectedUserId)
}
+ authenticationChallengeResult.emit(isSuccessful)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 345f15c..1ede530 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -16,19 +16,16 @@
package com.android.systemui.authentication.domain.interactor
-import com.android.app.tracing.TraceUtils.Companion.async
import com.android.app.tracing.TraceUtils.Companion.withContext
import com.android.internal.widget.LockPatternView
import com.android.internal.widget.LockscreenCredential
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
import com.android.systemui.authentication.data.repository.AuthenticationRepository
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -40,13 +37,14 @@
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
/**
* Hosts application business logic related to user authentication.
@@ -63,7 +61,6 @@
private val repository: AuthenticationRepository,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val userRepository: UserRepository,
- private val deviceEntryRepository: DeviceEntryRepository,
private val clock: SystemClock,
) {
/**
@@ -84,8 +81,7 @@
* `true` even when the lockscreen is showing and still needs to be dismissed by the user to
* proceed.
*/
- val authenticationMethod: Flow<DomainLayerAuthenticationMethodModel> =
- repository.authenticationMethod.map { rawModel -> rawModel.toDomainLayer() }
+ val authenticationMethod: Flow<AuthenticationMethodModel> = repository.authenticationMethod
/** The current authentication throttling state, only meaningful if [isThrottled] is `true`. */
val throttling: StateFlow<AuthenticationThrottlingModel> = repository.throttling
@@ -103,9 +99,29 @@
initialValue = throttling.value.remainingMs > 0,
)
+ /**
+ * Whether the auto confirm feature is enabled for the currently-selected user.
+ *
+ * Note that the length of the PIN is also important to take into consideration, please see
+ * [hintedPinLength].
+ *
+ * During throttling, this is always disabled (`false`).
+ */
+ val isAutoConfirmEnabled: StateFlow<Boolean> =
+ combine(repository.isAutoConfirmFeatureEnabled, isThrottled) { featureEnabled, isThrottled
+ ->
+ // Disable auto-confirm during throttling.
+ featureEnabled && !isThrottled
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
/** The length of the hinted PIN, or `null` if pin length hint should not be shown. */
val hintedPinLength: StateFlow<Int?> =
- repository.isAutoConfirmEnabled
+ isAutoConfirmEnabled
.map { isAutoConfirmEnabled ->
repository.getPinLength().takeIf {
isAutoConfirmEnabled && it == repository.hintedPinLength
@@ -119,12 +135,16 @@
initialValue = null,
)
- /** Whether the auto confirm feature is enabled for the currently-selected user. */
- val isAutoConfirmEnabled: StateFlow<Boolean> = repository.isAutoConfirmEnabled
-
/** Whether the pattern should be visible for the currently-selected user. */
val isPatternVisible: StateFlow<Boolean> = repository.isPatternVisible
+ /**
+ * Emits the outcome (successful or unsuccessful) whenever a PIN/Pattern/Password security
+ * challenge is attempted by the user in order to unlock the device.
+ */
+ val authenticationChallengeResult: SharedFlow<Boolean> =
+ repository.authenticationChallengeResult
+
private var throttlingCountdownJob: Job? = null
init {
@@ -147,17 +167,8 @@
* The flow should be used for code that wishes to stay up-to-date its logic as the
* authentication changes over time and this method should be used for simple code that only
* needs to check the current value.
- *
- * Note: this layer adds the synthetic authentication method of "swipe" which is special. When
- * the current authentication method is "swipe", the user does not need to complete any
- * authentication challenge to unlock the device; they just need to dismiss the lockscreen to
- * get past it. This also means that the value of `DeviceEntryInteractor#isUnlocked` remains
- * `true` even when the lockscreen is showing and still needs to be dismissed by the user to
- * proceed.
*/
- suspend fun getAuthenticationMethod(): DomainLayerAuthenticationMethodModel {
- return repository.getAuthenticationMethod().toDomainLayer()
- }
+ suspend fun getAuthenticationMethod() = repository.getAuthenticationMethod()
/**
* Attempts to authenticate the user and unlock the device.
@@ -187,13 +198,13 @@
// attempt.
isThrottled.value -> true
// The pattern is too short; skip the attempt.
- authMethod == DomainLayerAuthenticationMethodModel.Pattern &&
+ authMethod == AuthenticationMethodModel.Pattern &&
input.size < repository.minPatternLength -> true
// Auto-confirm attempt when the feature is not enabled; skip the attempt.
tryAutoConfirm && !isAutoConfirmEnabled.value -> true
// Auto-confirm should skip the attempt if the pin entered is too short.
tryAutoConfirm &&
- authMethod == DomainLayerAuthenticationMethodModel.Pin &&
+ authMethod == AuthenticationMethodModel.Pin &&
input.size < repository.getPinLength() -> true
else -> false
}
@@ -279,15 +290,15 @@
}
}
- private fun DomainLayerAuthenticationMethodModel.createCredential(
+ private fun AuthenticationMethodModel.createCredential(
input: List<Any>
): LockscreenCredential? {
return when (this) {
- is DomainLayerAuthenticationMethodModel.Pin ->
+ is AuthenticationMethodModel.Pin ->
LockscreenCredential.createPin(input.joinToString(""))
- is DomainLayerAuthenticationMethodModel.Password ->
+ is AuthenticationMethodModel.Password ->
LockscreenCredential.createPassword(input.joinToString(""))
- is DomainLayerAuthenticationMethodModel.Pattern ->
+ is AuthenticationMethodModel.Pattern ->
LockscreenCredential.createPattern(
input
.map { it as AuthenticationPatternCoordinate }
@@ -297,23 +308,6 @@
}
}
- private suspend fun DataLayerAuthenticationMethodModel.toDomainLayer():
- DomainLayerAuthenticationMethodModel {
- return when (this) {
- is DataLayerAuthenticationMethodModel.None ->
- if (deviceEntryRepository.isInsecureLockscreenEnabled()) {
- DomainLayerAuthenticationMethodModel.Swipe
- } else {
- DomainLayerAuthenticationMethodModel.None
- }
- is DataLayerAuthenticationMethodModel.Pin -> DomainLayerAuthenticationMethodModel.Pin
- is DataLayerAuthenticationMethodModel.Password ->
- DomainLayerAuthenticationMethodModel.Password
- is DataLayerAuthenticationMethodModel.Pattern ->
- DomainLayerAuthenticationMethodModel.Pattern
- }
- }
-
companion object {
const val TAG = "AuthenticationInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/model/AuthenticationMethodModel.kt
deleted file mode 100644
index d7e6099..0000000
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/model/AuthenticationMethodModel.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.authentication.domain.model
-
-/** Enumerates all known authentication methods. */
-sealed class AuthenticationMethodModel(
- /**
- * Whether the authentication method is considered to be "secure".
- *
- * "Secure" authentication methods require authentication to unlock the device. Non-secure auth
- * methods simply require user dismissal.
- */
- open val isSecure: Boolean,
-) {
- /** There is no authentication method on the device. We shouldn't even show the lock screen. */
- object None : AuthenticationMethodModel(isSecure = false)
-
- /** The most basic authentication method. The lock screen can be swiped away when displayed. */
- object Swipe : AuthenticationMethodModel(isSecure = false)
-
- object Pin : AuthenticationMethodModel(isSecure = true)
-
- object Password : AuthenticationMethodModel(isSecure = true)
-
- object Pattern : AuthenticationMethodModel(isSecure = true)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/authentication/data/model/AuthenticationMethodModel.kt
rename to packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
index 6d23b11..bb5b81d 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/model/AuthenticationMethodModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.authentication.data.model
+package com.android.systemui.authentication.shared.model
/** Enumerates all known authentication methods. */
sealed class AuthenticationMethodModel(
@@ -26,7 +26,10 @@
*/
open val isSecure: Boolean,
) {
- /** There is no authentication method on the device. We shouldn't even show the lock screen. */
+ /**
+ * Device doesn't use a secure authentication method. Either there is no lockscreen or the lock
+ * screen can be swiped away when displayed.
+ */
object None : AuthenticationMethodModel(isSecure = false)
object Pin : AuthenticationMethodModel(isSecure = true)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index b9a913e..4e1cddc 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -19,24 +19,22 @@
import android.content.Context
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.domain.interactor.AuthenticationResult
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.bouncer.data.repository.BouncerRepository
import com.android.systemui.classifier.FalsingClassifier
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.res.R
-import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
@@ -51,9 +49,7 @@
@Application private val applicationScope: CoroutineScope,
@Application private val applicationContext: Context,
private val repository: BouncerRepository,
- private val deviceEntryInteractor: DeviceEntryInteractor,
private val authenticationInteractor: AuthenticationInteractor,
- private val sceneInteractor: SceneInteractor,
flags: SceneContainerFlags,
private val falsingInteractor: FalsingInteractor,
) {
@@ -100,6 +96,10 @@
val isUserSwitcherVisible: Boolean
get() = repository.isUserSwitcherVisible
+ private val _onImeHidden = MutableSharedFlow<Unit>()
+ /** Provide the onImeHidden events from the bouncer */
+ val onImeHidden: SharedFlow<Unit> = _onImeHidden
+
init {
if (flags.isEnabled()) {
// Clear the message if moved from throttling to no-longer throttling.
@@ -142,32 +142,6 @@
}
/**
- * Either shows the bouncer or unlocks the device, if the bouncer doesn't need to be shown.
- *
- * @param message An optional message to show to the user in the bouncer.
- */
- fun showOrUnlockDevice(
- message: String? = null,
- ) {
- applicationScope.launch {
- if (deviceEntryInteractor.isAuthenticationRequired()) {
- repository.setMessage(
- message ?: promptMessage(authenticationInteractor.getAuthenticationMethod())
- )
- sceneInteractor.changeScene(
- scene = SceneModel(SceneKey.Bouncer),
- loggingReason = "request to unlock device while authentication required",
- )
- } else {
- sceneInteractor.changeScene(
- scene = SceneModel(SceneKey.Gone),
- loggingReason = "request to unlock device while authentication isn't required",
- )
- }
- }
- }
-
- /**
* Resets the user-facing message back to the default according to the current authentication
* method.
*/
@@ -212,17 +186,11 @@
return applicationScope
.async {
val authResult = authenticationInteractor.authenticate(input, tryAutoConfirm)
- when (authResult) {
- // Authentication succeeded.
- AuthenticationResult.SUCCEEDED ->
- sceneInteractor.changeScene(
- scene = SceneModel(SceneKey.Gone),
- loggingReason = "successful authentication",
- )
- // Authentication failed.
- AuthenticationResult.FAILED -> showErrorMessage()
- // Authentication skipped.
- AuthenticationResult.SKIPPED -> if (!tryAutoConfirm) showErrorMessage()
+ if (
+ authResult == AuthenticationResult.FAILED ||
+ (authResult == AuthenticationResult.SKIPPED && !tryAutoConfirm)
+ ) {
+ showErrorMessage()
}
authResult
}
@@ -242,16 +210,8 @@
}
/** Notifies the interactor that the input method editor has been hidden. */
- fun onImeHidden() {
- // If the bouncer is showing, hide it and return to the lockscreen scene.
- if (sceneInteractor.desiredScene.value.key != SceneKey.Bouncer) {
- return
- }
-
- sceneInteractor.changeScene(
- scene = SceneModel(SceneKey.Lockscreen),
- loggingReason = "IME hidden",
- )
+ suspend fun onImeHidden() {
+ _onImeHidden.emit(Unit)
}
private fun promptMessage(authMethod: AuthenticationMethodModel): String {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 55bc653..f46574c 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -18,7 +18,7 @@
import android.annotation.StringRes
import com.android.systemui.authentication.domain.interactor.AuthenticationResult
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
@@ -75,7 +75,7 @@
* Notifies that the input method editor (for example, the software keyboard) has been shown or
* hidden.
*/
- fun onImeVisibilityChanged(isVisible: Boolean) {
+ suspend fun onImeVisibilityChanged(isVisible: Boolean) {
if (isImeVisible && !isVisible) {
interactor.onImeHidden()
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 4767e21..09c94c8 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -20,7 +20,7 @@
import android.graphics.Bitmap
import androidx.core.graphics.drawable.toBitmap
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index fe77419..a15698e 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -16,7 +16,7 @@
package com.android.systemui.bouncer.ui.viewmodel
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.res.R
import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index d301085..ed6a48f 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -18,7 +18,7 @@
import android.content.Context
import android.util.TypedValue
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.res.R
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index b90e255..2ed0d5d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -18,7 +18,7 @@
import android.content.Context
import com.android.keyguard.PinShapeAdapter
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.res.R
import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index 6b27ce0..f7fee96 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -18,13 +18,11 @@
import android.appwidget.AppWidgetHost
import android.appwidget.AppWidgetManager
-import android.appwidget.AppWidgetProviderInfo
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
-import android.content.pm.PackageManager
import android.os.UserManager
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
@@ -32,13 +30,10 @@
import com.android.systemui.communal.data.db.CommunalWidgetDao
import com.android.systemui.communal.data.db.CommunalWidgetItem
import com.android.systemui.communal.shared.CommunalWidgetHost
-import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
@@ -58,9 +53,6 @@
/** Encapsulates the state of widgets for communal mode. */
interface CommunalWidgetRepository {
- /** A flow of provider info for the stopwatch widget, or null if widget is unavailable. */
- val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?>
-
/** A flow of information about active communal widgets stored in database. */
val communalWidgets: Flow<List<CommunalWidgetContentModel>>
@@ -84,15 +76,12 @@
communalRepository: CommunalRepository,
private val communalWidgetHost: CommunalWidgetHost,
private val communalWidgetDao: CommunalWidgetDao,
- private val packageManager: PackageManager,
private val userManager: UserManager,
private val userTracker: UserTracker,
@CommunalLog logBuffer: LogBuffer,
- featureFlags: FeatureFlagsClassic,
) : CommunalWidgetRepository {
companion object {
const val TAG = "CommunalWidgetRepository"
- const val WIDGET_LABEL = "Stopwatch"
}
private val logger = Logger(logBuffer, TAG)
@@ -100,9 +89,6 @@
// Whether the [AppWidgetHost] is listening for updates.
private var isHostListening = false
- // Widgets that should be rendered in communal mode.
- private val widgets: HashMap<Int, CommunalAppWidgetInfo> = hashMapOf()
-
private val isUserUnlocked: Flow<Boolean> =
callbackFlow {
if (!communalRepository.isCommunalEnabled) {
@@ -149,25 +135,6 @@
}
}
- override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> =
- isHostActive.map { isHostActive ->
- if (!isHostActive || !featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
- return@map null
- }
-
- val providerInfo =
- appWidgetManager.installedProviders.find {
- it.loadLabel(packageManager).equals(WIDGET_LABEL)
- }
-
- if (providerInfo == null) {
- logger.w("Cannot find app widget: $WIDGET_LABEL")
- return@map null
- }
-
- return@map addStopWatchWidget(providerInfo)
- }
-
override val communalWidgets: Flow<List<CommunalWidgetContentModel>> =
isHostActive.flatMapLatest { isHostActive ->
if (!isHostActive) {
@@ -226,21 +193,4 @@
appWidgetHost.stopListening()
isHostListening = false
}
-
- // TODO(b/306471933): remove this prototype that shows a stopwatch in the communal blueprint
- private fun addStopWatchWidget(providerInfo: AppWidgetProviderInfo): CommunalAppWidgetInfo {
- val existing = widgets.values.firstOrNull { it.providerInfo == providerInfo }
- if (existing != null) {
- return existing
- }
-
- val appWidgetId = appWidgetHost.allocateAppWidgetId()
- val widget =
- CommunalAppWidgetInfo(
- providerInfo,
- appWidgetId,
- )
- widgets[appWidgetId] = widget
- return widget
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index eb36b19..771dfbc 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -23,7 +23,6 @@
import com.android.systemui.communal.data.repository.CommunalRepository
import com.android.systemui.communal.data.repository.CommunalWidgetRepository
import com.android.systemui.communal.domain.model.CommunalContentModel
-import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.dagger.SysUISingleton
@@ -55,9 +54,6 @@
val isCommunalEnabled: Boolean
get() = communalRepository.isCommunalEnabled
- /** A flow of info about the widget to be displayed, or null if widget is unavailable. */
- val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = widgetRepository.stopwatchAppWidgetInfo
-
/**
* Target scene as requested by the underlying [SceneTransitionLayout] or through
* [onSceneChanged].
@@ -145,6 +141,7 @@
private val umoContent: Flow<List<CommunalContentModel.Umo>> =
mediaRepository.mediaPlaying.flatMapLatest { mediaPlaying ->
if (mediaPlaying) {
+ // TODO(b/310254801): support HALF and FULL layouts
flowOf(listOf(CommunalContentModel.Umo(CommunalContentSize.THIRD)))
} else {
flowOf(emptyList())
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt
deleted file mode 100644
index 65bf4b3..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalWidgetViewBinder.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.communal.ui.binder
-
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.res.R
-import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
-import com.android.systemui.communal.ui.view.CommunalWidgetWrapper
-import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.lifecycle.repeatWhenAttached
-import kotlinx.coroutines.launch
-
-/** Binds [CommunalWidgetViewModel] to the keyguard root view. */
-object CommunalWidgetViewBinder {
-
- @JvmStatic
- fun bind(
- rootView: KeyguardRootView,
- viewModel: CommunalWidgetViewModel,
- adapter: CommunalWidgetViewAdapter,
- keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
- ) {
- rootView.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch {
- adapter.adapt(viewModel.appWidgetInfo).collect {
- val oldView =
- rootView.findViewById<CommunalWidgetWrapper>(
- R.id.communal_widget_wrapper
- )
- var dirty = false
-
- if (oldView != null) {
- rootView.removeView(oldView)
- dirty = true
- }
-
- if (it != null) {
- rootView.addView(it)
- dirty = true
- }
-
- if (dirty) {
- keyguardBlueprintInteractor.refreshBlueprint()
- }
- }
- }
-
- launch { viewModel.alpha.collect { rootView.alpha = it } }
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
index 702554a..9198c7b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
@@ -17,7 +17,6 @@
package com.android.systemui.communal.ui.view.layout.blueprints
import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalHubSection
-import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
@@ -30,13 +29,11 @@
@Inject
constructor(
defaultCommunalHubSection: DefaultCommunalHubSection,
- defaultCommunalWidgetSection: DefaultCommunalWidgetSection,
) : KeyguardBlueprint {
override val id: String = COMMUNAL
override val sections: List<KeyguardSection> =
listOf(
defaultCommunalHubSection,
- defaultCommunalWidgetSection,
)
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
deleted file mode 100644
index c3e8a96..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.communal.ui.view.layout.sections
-
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
-import androidx.constraintlayout.widget.ConstraintSet.END
-import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import com.android.systemui.res.R
-import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
-import com.android.systemui.communal.ui.binder.CommunalWidgetViewBinder
-import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardSection
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import dagger.Lazy
-import javax.inject.Inject
-
-class DefaultCommunalWidgetSection
-@Inject
-constructor(
- private val featureFlags: FeatureFlags,
- private val keyguardRootView: KeyguardRootView,
- private val communalWidgetViewModel: CommunalWidgetViewModel,
- private val communalWidgetViewAdapter: CommunalWidgetViewAdapter,
- private val keyguardBlueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
-) : KeyguardSection() {
- private val widgetAreaViewId = R.id.communal_widget_wrapper
-
- override fun addViews(constraintLayout: ConstraintLayout) {}
-
- override fun bindData(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
- return
- }
-
- CommunalWidgetViewBinder.bind(
- keyguardRootView,
- communalWidgetViewModel,
- communalWidgetViewAdapter,
- keyguardBlueprintInteractor.get(),
- )
- }
-
- override fun applyConstraints(constraintSet: ConstraintSet) {
- constraintSet.apply {
- constrainWidth(widgetAreaViewId, WRAP_CONTENT)
- constrainHeight(widgetAreaViewId, WRAP_CONTENT)
- connect(widgetAreaViewId, BOTTOM, PARENT_ID, BOTTOM)
- connect(widgetAreaViewId, END, PARENT_ID, END)
- }
- }
-
- override fun removeViews(constraintLayout: ConstraintLayout) {}
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt
deleted file mode 100644
index d7bbea6..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.communal.ui.viewmodel
-
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-
-@SysUISingleton
-class CommunalWidgetViewModel
-@Inject
-constructor(
- communalInteractor: CommunalInteractor,
- keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel,
-) {
- /** An observable for the alpha level for the communal widget area. */
- val alpha: Flow<Float> = keyguardBottomAreaViewModel.alpha
-
- /** A flow of info about the widget to be displayed, or null if widget is unavailable. */
- val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = communalInteractor.appWidgetInfo
-}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
index 1e29e1f..f27bbe6 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
@@ -1,5 +1,6 @@
package com.android.systemui.deviceentry.data.repository
+import android.util.Log
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -15,9 +16,12 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
@@ -35,10 +39,14 @@
val isUnlocked: StateFlow<Boolean>
/**
- * Whether the lockscreen should be shown when the authentication method is not secure (e.g.
- * `None` or `Swipe`).
+ * Whether the lockscreen is enabled for the current user. This is `true` whenever the user has
+ * chosen any secure authentication method and even if they set the lockscreen to be dismissed
+ * when the user swipes on it.
*/
- suspend fun isInsecureLockscreenEnabled(): Boolean
+ suspend fun isLockscreenEnabled(): Boolean
+
+ /** Report successful authentication for device entry. */
+ fun reportSuccessfulAuthentication()
/**
* Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically
@@ -67,7 +75,9 @@
keyguardStateController: KeyguardStateController,
) : DeviceEntryRepository {
- override val isUnlocked =
+ private val _isUnlocked = MutableStateFlow(false)
+
+ private val isUnlockedReportedByLegacyKeyguard =
conflatedCallbackFlow {
val callback =
object : KeyguardStateController.Callback {
@@ -99,19 +109,27 @@
awaitClose { keyguardStateController.removeCallback(callback) }
}
.distinctUntilChanged()
+ .onEach { _isUnlocked.value = it }
.stateIn(
applicationScope,
SharingStarted.Eagerly,
initialValue = false,
)
- override suspend fun isInsecureLockscreenEnabled(): Boolean {
+ override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow()
+
+ override suspend fun isLockscreenEnabled(): Boolean {
return withContext(backgroundDispatcher) {
val selectedUserId = userRepository.getSelectedUserInfo().id
!lockPatternUtils.isLockScreenDisabled(selectedUserId)
}
}
+ override fun reportSuccessfulAuthentication() {
+ Log.d(TAG, "Successful authentication reported.")
+ _isUnlocked.value = true
+ }
+
override val isBypassEnabled: StateFlow<Boolean> =
conflatedCallbackFlow {
val listener =
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index e872d13..c3f3529 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -17,23 +17,28 @@
package com.android.systemui.deviceentry.domain.interactor
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.SceneModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
/**
* Hosts application business logic related to device entry.
@@ -48,9 +53,10 @@
@Application private val applicationScope: CoroutineScope,
repository: DeviceEntryRepository,
private val authenticationInteractor: AuthenticationInteractor,
- sceneInteractor: SceneInteractor,
+ private val sceneInteractor: SceneInteractor,
deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository,
trustRepository: TrustRepository,
+ flags: SceneContainerFlags,
) {
/**
* Whether the device is unlocked.
@@ -90,28 +96,33 @@
.map { it == SceneKey.Gone }
.stateIn(
scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
+ started = SharingStarted.Eagerly,
initialValue = false,
)
// Authenticated by a TrustAgent like trusted device, location, etc or by face auth.
private val passivelyAuthenticated =
merge(
- trustRepository.isCurrentUserTrusted,
- deviceEntryFaceAuthRepository.isAuthenticated,
- )
+ trustRepository.isCurrentUserTrusted,
+ deviceEntryFaceAuthRepository.isAuthenticated,
+ )
+ .onStart { emit(false) }
/**
* Whether it's currently possible to swipe up to enter the device without requiring
- * authentication. This returns `false` whenever the lockscreen has been dismissed.
+ * authentication or when the device is already authenticated using a passive authentication
+ * mechanism like face or trust manager. This returns `false` whenever the lockscreen has been
+ * dismissed.
*
* Note: `true` doesn't mean the lockscreen is visible. It may be occluded or covered by other
* UI.
*/
val canSwipeToEnter =
combine(
+ // This is true when the user has chosen to show the lockscreen but has not made it
+ // secure.
authenticationInteractor.authenticationMethod.map {
- it == AuthenticationMethodModel.Swipe
+ it == AuthenticationMethodModel.None && repository.isLockscreenEnabled()
},
passivelyAuthenticated,
isDeviceEntered
@@ -120,11 +131,37 @@
}
.stateIn(
scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
+ started = SharingStarted.Eagerly,
initialValue = false,
)
/**
+ * Attempt to enter the device and dismiss the lockscreen. If authentication is required to
+ * unlock the device it will transition to bouncer.
+ */
+ fun attemptDeviceEntry() {
+ // TODO (b/307768356),
+ // 1. Check if the device is already authenticated by trust agent/passive biometrics
+ // 2. show SPFS/UDFPS bouncer if it is available AlternateBouncerInteractor.show
+ // 3. For face auth only setups trigger face auth, delay transitioning to bouncer for
+ // a small amount of time.
+ // 4. Transition to bouncer scene
+ applicationScope.launch {
+ if (isAuthenticationRequired()) {
+ sceneInteractor.changeScene(
+ scene = SceneModel(SceneKey.Bouncer),
+ loggingReason = "request to unlock device while authentication required",
+ )
+ } else {
+ sceneInteractor.changeScene(
+ scene = SceneModel(SceneKey.Gone),
+ loggingReason = "request to unlock device while authentication isn't required",
+ )
+ }
+ }
+ }
+
+ /**
* Returns `true` if the device currently requires authentication before entry is granted;
* `false` if the device can be entered without authenticating first.
*/
@@ -133,10 +170,22 @@
}
/**
- * Whether lock screen bypass is enabled. When enabled, the lock screen will be automatically
+ * Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically
* dismissed once the authentication challenge is completed. For example, completing a biometric
* authentication challenge via face unlock or fingerprint sensor can automatically bypass the
- * lock screen.
+ * lockscreen.
*/
val isBypassEnabled: StateFlow<Boolean> = repository.isBypassEnabled
+
+ init {
+ if (flags.isEnabled()) {
+ applicationScope.launch {
+ authenticationInteractor.authenticationChallengeResult.collectLatest { successful ->
+ if (successful) {
+ repository.reportSuccessfulAuthentication()
+ }
+ }
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b237b87..cd3ecb3 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -239,8 +239,7 @@
/** Provide new auth messages on the bouncer. */
// TODO(b/277961132): Tracking bug.
- @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages",
- teamfood = true)
+ @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages")
/** Keyguard Migration */
@@ -255,9 +254,6 @@
// TODO(b/287268101): Tracking bug.
@JvmField val TRANSIT_CLOCK = releasedFlag("lockscreen_custom_transit_clock")
- // TODO(b/288276738): Tracking bug.
- @JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag("widget_on_keyguard")
-
/** Migrate the NSSL to the a sibling to both the panel and keyguard root view. */
// TODO(b/288074305): Tracking bug.
@JvmField val MIGRATE_NSSL = unreleasedFlag("migrate_nssl")
@@ -320,11 +316,6 @@
val SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
releasedFlag("smartspace_shared_element_transition_enabled")
- // TODO(b/258517050): Clean up after the feature is launched.
- @JvmField
- val SMARTSPACE_DATE_WEATHER_DECOUPLED =
- sysPropBooleanFlag("persist.sysui.ss.dw_decoupled", default = true)
-
// TODO(b/270223352): Tracking Bug
@JvmField
val HIDE_SMARTSPACE_ON_DREAM_OVERLAY = releasedFlag("hide_smartspace_on_dream_overlay")
@@ -445,9 +436,6 @@
// TODO(b/270437894): Tracking Bug
val MEDIA_REMOTE_RESUME = unreleasedFlag("media_remote_resume")
- // TODO(b/304506662): Tracking Bug
- val MEDIA_DEVICE_NAME_FIX = releasedFlag("media_device_name_fix")
-
// 1000 - dock
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag("simulate_dock_through_charging")
@@ -776,9 +764,4 @@
@JvmField
val COMMUNAL_SERVICE_ENABLED = resourceBooleanFlag(R.bool.config_communalServiceEnabled,
"communal_service_enabled")
-
- // TODO(b/303131306): Tracking Bug
- /** Whether communal hub features are enabled. */
- @JvmField
- val COMMUNAL_HUB = unreleasedFlag("communal_hub")
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
index e16bb0b..1e9be09 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
@@ -30,6 +30,7 @@
import android.view.ViewGroup.MarginLayoutParams
import android.view.Window
import android.view.WindowManager
+import android.view.accessibility.AccessibilityEvent
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
@@ -78,23 +79,29 @@
private lateinit var stepProperties: StepViewProperties
@ColorInt
- var filledRectangleColor = getColorFromStyle(com.android.internal.R.attr.materialColorPrimary)
- @ColorInt
- var emptyRectangleColor =
- getColorFromStyle(com.android.internal.R.attr.materialColorOutlineVariant)
- @ColorInt
- var backgroundColor = getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceBright)
- @ColorInt
- var defaultIconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary)
- @ColorInt
- var defaultIconBackgroundColor =
+ private val filledRectangleColor =
getColorFromStyle(com.android.internal.R.attr.materialColorPrimary)
@ColorInt
- var dimmedIconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnSurface)
+ private val emptyRectangleColor =
+ getColorFromStyle(com.android.internal.R.attr.materialColorOutlineVariant)
@ColorInt
- var dimmedIconBackgroundColor =
+ private val backgroundColor =
+ getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceBright)
+ @ColorInt
+ private val defaultIconColor =
+ getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary)
+ @ColorInt
+ private val defaultIconBackgroundColor =
+ getColorFromStyle(com.android.internal.R.attr.materialColorPrimary)
+ @ColorInt
+ private val dimmedIconColor =
+ getColorFromStyle(com.android.internal.R.attr.materialColorOnSurface)
+ @ColorInt
+ private val dimmedIconBackgroundColor =
getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceDim)
+ private val levelContentDescription = context.getString(R.string.keyboard_backlight_value)
+
init {
currentLevel = initialCurrentLevel
maxLevel = initialMaxLevel
@@ -103,6 +110,8 @@
override fun onCreate(savedInstanceState: Bundle?) {
setUpWindowProperties(this)
setWindowPosition()
+ // title is used for a11y announcement
+ window?.setTitle(context.getString(R.string.keyboard_backlight_dialog_title))
updateResources()
rootView = buildRootView()
setContentView(rootView)
@@ -159,6 +168,12 @@
currentLevel = current
updateIconTile()
updateStepColors()
+ updateAccessibilityInfo()
+ }
+
+ private fun updateAccessibilityInfo() {
+ rootView.contentDescription = String.format(levelContentDescription, currentLevel, maxLevel)
+ rootView.sendAccessibilityEvent(AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION)
}
private fun updateIconTile() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
index 078feff..c2aedca 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -30,6 +30,7 @@
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import javax.inject.Inject
@@ -44,6 +45,7 @@
sharedNotificationContainer: SharedNotificationContainer,
sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
controller: NotificationStackScrollLayoutController,
+ notificationStackSizeCalculator: NotificationStackSizeCalculator,
private val smartspaceViewModel: KeyguardSmartspaceViewModel,
) :
NotificationStackScrollLayoutSection(
@@ -53,6 +55,7 @@
sharedNotificationContainer,
sharedNotificationContainerViewModel,
controller,
+ notificationStackSizeCalculator,
) {
override fun applyConstraints(constraintSet: ConstraintSet) {
if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
index 00966f2..ea2bdf7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
@@ -27,6 +27,7 @@
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
@@ -40,6 +41,7 @@
private val sharedNotificationContainer: SharedNotificationContainer,
private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
private val controller: NotificationStackScrollLayoutController,
+ private val notificationStackSizeCalculator: NotificationStackSizeCalculator,
) : KeyguardSection() {
private val placeHolderId = R.id.nssl_placeholder
private var disposableHandle: DisposableHandle? = null
@@ -69,6 +71,7 @@
sharedNotificationContainer,
sharedNotificationContainerViewModel,
controller,
+ notificationStackSizeCalculator,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
index bf95c77..dc2ad8d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
@@ -30,6 +30,7 @@
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import javax.inject.Inject
@@ -44,6 +45,7 @@
sharedNotificationContainer: SharedNotificationContainer,
sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
controller: NotificationStackScrollLayoutController,
+ notificationStackSizeCalculator: NotificationStackSizeCalculator,
private val smartspaceViewModel: KeyguardSmartspaceViewModel,
) :
NotificationStackScrollLayoutSection(
@@ -53,6 +55,7 @@
sharedNotificationContainer,
sharedNotificationContainerViewModel,
controller,
+ notificationStackSizeCalculator,
) {
override fun applyConstraints(constraintSet: ConstraintSet) {
if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
index 2034d97..dcbf670 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
@@ -37,8 +37,6 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.media.controls.models.player.MediaData
import com.android.systemui.media.controls.models.player.MediaDeviceData
import com.android.systemui.media.controls.util.MediaControllerFactory
@@ -70,7 +68,6 @@
@Main private val fgExecutor: Executor,
@Background private val bgExecutor: Executor,
dumpManager: DumpManager,
- private val featureFlags: FeatureFlagsClassic,
) : MediaDataManager.Listener, Dumpable {
private val listeners: MutableSet<Listener> = mutableSetOf()
@@ -392,13 +389,6 @@
)
}
- if (!featureFlags.isEnabled(Flags.MEDIA_DEVICE_NAME_FIX)) {
- if (controller == null || routingSession != null) {
- return routingSession?.name?.toString() ?: device?.name
- }
- return null
- }
-
if (controller == null) {
// In resume state, we don't have a controller - just use the device name
return device?.name
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
index 1ec43c5..d277f32 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
@@ -60,7 +60,7 @@
}
companion object {
- @JvmField val GUTS_ANIMATION_DURATION = 500L
+ @JvmField val GUTS_ANIMATION_DURATION = 234L
}
/** A listener when the current dimensions of the player change */
@@ -234,7 +234,8 @@
currentStartLocation,
currentEndLocation,
currentTransitionProgress,
- applyImmediately = false
+ applyImmediately = false,
+ isGutsAnimation = true,
)
}
@@ -254,7 +255,8 @@
currentStartLocation,
currentEndLocation,
currentTransitionProgress,
- applyImmediately = immediate
+ applyImmediately = immediate,
+ isGutsAnimation = true,
)
}
@@ -414,7 +416,10 @@
* it's not available, it will recreate one by measuring, which may be expensive.
*/
@VisibleForTesting
- fun obtainViewState(state: MediaHostState?): TransitionViewState? {
+ fun obtainViewState(
+ state: MediaHostState?,
+ isGutsAnimation: Boolean = false
+ ): TransitionViewState? {
if (state == null || state.measurementInput == null) {
return null
}
@@ -423,7 +428,7 @@
val viewState = viewStates[cacheKey]
if (viewState != null) {
// we already have cached this measurement, let's continue
- if (state.squishFraction <= 1f) {
+ if (state.squishFraction <= 1f && !isGutsAnimation) {
return squishViewState(viewState, state.squishFraction)
}
return viewState
@@ -455,13 +460,14 @@
// Given that we have a measurement and a view, let's get (guaranteed) viewstates
// from the start and end state and interpolate them
- val startViewState = obtainViewState(startState) as TransitionViewState
+ val startViewState = obtainViewState(startState, isGutsAnimation) as TransitionViewState
val endState = state.copy().also { it.expansion = 1.0f }
- val endViewState = obtainViewState(endState) as TransitionViewState
+ val endViewState = obtainViewState(endState, isGutsAnimation) as TransitionViewState
result =
layoutController.getInterpolatedState(startViewState, endViewState, state.expansion)
}
- if (state.squishFraction <= 1f) {
+ // Skip the adjustments of squish view state if UMO changes due to guts animation.
+ if (state.squishFraction <= 1f && !isGutsAnimation) {
return squishViewState(result, state.squishFraction)
}
return result
@@ -521,7 +527,8 @@
@MediaLocation startLocation: Int,
@MediaLocation endLocation: Int,
transitionProgress: Float,
- applyImmediately: Boolean
+ applyImmediately: Boolean,
+ isGutsAnimation: Boolean = false,
) =
traceSection("MediaViewController#setCurrentState") {
currentEndLocation = endLocation
@@ -537,7 +544,7 @@
// Obtain the view state that we'd want to be at the end
// The view might not be bound yet or has never been measured and in that case will be
// reset once the state is fully available
- var endViewState = obtainViewState(endHostState) ?: return
+ var endViewState = obtainViewState(endHostState, isGutsAnimation) ?: return
endViewState = updateViewStateSize(endViewState, endLocation, tmpState2)!!
layoutController.setMeasureState(endViewState)
@@ -548,7 +555,7 @@
}
val result: TransitionViewState
- var startViewState = obtainViewState(startHostState)
+ var startViewState = obtainViewState(startHostState, isGutsAnimation)
startViewState = updateViewStateSize(startViewState, startLocation, tmpState3)
if (!endHostState.visible) {
@@ -602,7 +609,8 @@
applyImmediately,
shouldAnimate,
animationDuration,
- animationDelay
+ animationDelay,
+ isGutsAnimation,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
index 2074a14..0a9a6d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
@@ -33,7 +33,7 @@
class QSTileLogger
@Inject
constructor(
- @QSTilesLogBuffers logBuffers: Map<String, LogBuffer>,
+ @QSTilesLogBuffers logBuffers: Map<TileSpec, LogBuffer>,
private val factory: LogBufferFactory,
private val mStatusBarStateController: StatusBarStateController,
) {
@@ -162,7 +162,7 @@
private fun TileSpec.getLogBuffer(): LogBuffer =
synchronized(logBufferCache) {
- logBufferCache.getOrPut(this.spec) {
+ logBufferCache.getOrPut(this) {
factory.create(
this.getLogTag(),
BUFFER_MAX_SIZE /* maxSize */,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
index 736f7cf..ae554d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
@@ -26,6 +26,7 @@
import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
import com.android.systemui.qs.tiles.base.logging.QSTileLogger
import com.android.systemui.qs.tiles.impl.di.QSTileComponent
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.user.data.repository.UserRepository
@@ -60,12 +61,17 @@
) : QSTileViewModelFactory<T> {
/**
- * Creates [QSTileViewModelImpl] based on the interactors obtained from [component].
- * Reference of that [component] is then stored along the view model.
+ * Creates [QSTileViewModelImpl] based on the interactors obtained from [QSTileComponent].
+ * Reference of that [QSTileComponent] is then stored along the view model.
*/
- fun create(tileSpec: TileSpec, component: QSTileComponent<T>): QSTileViewModelImpl<T> =
- QSTileViewModelImpl(
- qsTileConfigProvider.getConfig(tileSpec.spec),
+ fun create(
+ tileSpec: TileSpec,
+ componentFactory: (config: QSTileConfig) -> QSTileComponent<T>
+ ): QSTileViewModelImpl<T> {
+ val config = qsTileConfigProvider.getConfig(tileSpec.spec)
+ val component = componentFactory(config)
+ return QSTileViewModelImpl(
+ config,
component::userActionInteractor,
component::dataInteractor,
component::dataToStateMapper,
@@ -77,6 +83,7 @@
systemClock,
backgroundDispatcher,
)
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
index 7d7af64..27007bb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
@@ -19,6 +19,11 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.qs.QSFactory
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent
+import com.android.systemui.qs.tiles.impl.custom.di.QSTileConfigModule
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModelAdapter
@@ -34,18 +39,31 @@
private val adapterFactory: QSTileViewModelAdapter.Factory,
private val tileMap:
Map<String, @JvmSuppressWildcards Provider<@JvmSuppressWildcards QSTileViewModel>>,
+ private val customTileComponentBuilder: CustomTileComponent.Builder,
+ private val customTileViewModelFactory: QSTileViewModelFactory.Component<CustomTileDataModel>,
) : QSFactory {
init {
for (viewModelTileSpec in tileMap.keys) {
- // throws an exception when there is no config for a tileSpec of an injected viewModel
- qsTileConfigProvider.getConfig(viewModelTileSpec)
+ require(qsTileConfigProvider.hasConfig(viewModelTileSpec)) {
+ "No config for $viewModelTileSpec"
+ }
}
}
- override fun createTile(tileSpec: String): QSTile? =
- tileMap[tileSpec]?.let {
- val tile = it.get()
- adapterFactory.create(tile)
+ override fun createTile(tileSpec: String): QSTile? {
+ val viewModel: QSTileViewModel =
+ when (val spec = TileSpec.create(tileSpec)) {
+ is TileSpec.CustomTileSpec -> createCustomTileViewModel(spec)
+ is TileSpec.PlatformTileSpec -> tileMap[tileSpec]?.get()
+ is TileSpec.Invalid -> null
+ }
+ ?: return null
+ return adapterFactory.create(viewModel)
+ }
+
+ private fun createCustomTileViewModel(spec: TileSpec.CustomTileSpec): QSTileViewModel =
+ customTileViewModelFactory.create(spec) { config ->
+ customTileComponentBuilder.qsTileConfigModule(QSTileConfigModule(config)).build()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt
index 761274e..14bf25d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt
@@ -19,17 +19,18 @@
import android.os.UserHandle
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.di.QSTileScope
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@QSTileScope
-class CustomTileInteractor @Inject constructor() : QSTileDataInteractor<CustomTileData> {
+class CustomTileInteractor @Inject constructor() : QSTileDataInteractor<CustomTileDataModel> {
override fun tileData(
user: UserHandle,
triggers: Flow<DataUpdateTrigger>
- ): Flow<CustomTileData> {
+ ): Flow<CustomTileDataModel> {
TODO("Not yet implemented")
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt
index f7bec02..e23a5c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt
@@ -17,15 +17,16 @@
package com.android.systemui.qs.tiles.impl.custom
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.di.QSTileScope
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import javax.inject.Inject
@QSTileScope
-class CustomTileMapper @Inject constructor() : QSTileDataToStateMapper<CustomTileData> {
+class CustomTileMapper @Inject constructor() : QSTileDataToStateMapper<CustomTileDataModel> {
- override fun map(config: QSTileConfig, data: CustomTileData): QSTileState {
+ override fun map(config: QSTileConfig, data: CustomTileDataModel): QSTileState {
TODO("Not yet implemented")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt
index 6c1c1a3..f34704b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt
@@ -18,14 +18,15 @@
import com.android.systemui.qs.tiles.base.interactor.QSTileInput
import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.di.QSTileScope
import javax.inject.Inject
@QSTileScope
class CustomTileUserActionInteractor @Inject constructor() :
- QSTileUserActionInteractor<CustomTileData> {
+ QSTileUserActionInteractor<CustomTileDataModel> {
- override suspend fun handleInput(input: QSTileInput<CustomTileData>) {
+ override suspend fun handleInput(input: QSTileInput<CustomTileDataModel>) {
TODO("Not yet implemented")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
index 01df906..88bc8fa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
@@ -16,13 +16,14 @@
package com.android.systemui.qs.tiles.impl.custom.di
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.di.QSTileComponent
import com.android.systemui.qs.tiles.impl.di.QSTileScope
import dagger.Subcomponent
@QSTileScope
@Subcomponent(modules = [QSTileConfigModule::class, CustomTileModule::class])
-interface CustomTileComponent : QSTileComponent<Any> {
+interface CustomTileComponent : QSTileComponent<CustomTileDataModel> {
@Subcomponent.Builder
interface Builder {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
index 482bf9b..83767aa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
@@ -19,13 +19,13 @@
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.impl.custom.CustomTileData
import com.android.systemui.qs.tiles.impl.custom.CustomTileInteractor
import com.android.systemui.qs.tiles.impl.custom.CustomTileMapper
import com.android.systemui.qs.tiles.impl.custom.CustomTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl
import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import dagger.Binds
import dagger.Module
@@ -36,15 +36,15 @@
@Binds
fun bindDataInteractor(
dataInteractor: CustomTileInteractor
- ): QSTileDataInteractor<CustomTileData>
+ ): QSTileDataInteractor<CustomTileDataModel>
@Binds
fun bindUserActionInteractor(
userActionInteractor: CustomTileUserActionInteractor
- ): QSTileUserActionInteractor<CustomTileData>
+ ): QSTileUserActionInteractor<CustomTileDataModel>
@Binds
- fun bindMapper(customTileMapper: CustomTileMapper): QSTileDataToStateMapper<CustomTileData>
+ fun bindMapper(customTileMapper: CustomTileMapper): QSTileDataToStateMapper<CustomTileDataModel>
@Binds
fun bindCustomTileDefaultsRepository(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileData.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileData.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
index bb5a229..f095c01 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileData.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.custom
+package com.android.systemui.qs.tiles.impl.custom.domain.entity
import android.content.ComponentName
import android.graphics.drawable.Icon
@@ -22,12 +22,11 @@
import android.service.quicksettings.Tile
import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent
-data class CustomTileData(
+data class CustomTileDataModel(
val user: UserHandle,
val componentName: ComponentName,
val tile: Tile,
val callingAppUid: Int,
- val isActive: Boolean,
val hasPendingBind: Boolean,
val shouldShowChevron: Boolean,
val defaultTileLabel: CharSequence?,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
index 3f3b94e..0609e79 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
@@ -18,20 +18,31 @@
import com.android.internal.util.Preconditions
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject
interface QSTileConfigProvider {
/**
- * Returns a [QSTileConfig] for a [tileSpec] or throws [IllegalArgumentException] if there is no
- * config for such [tileSpec].
+ * Returns a [QSTileConfig] for a [tileSpec]:
+ * - injected config for [TileSpec.PlatformTileSpec] or throws [IllegalArgumentException] if
+ * there is none
+ * - new config for [TileSpec.CustomTileSpec].
+ * - throws [IllegalArgumentException] for [TileSpec.Invalid]
*/
fun getConfig(tileSpec: String): QSTileConfig
+
+ fun hasConfig(tileSpec: String): Boolean
}
@SysUISingleton
-class QSTileConfigProviderImpl @Inject constructor(private val configs: Map<String, QSTileConfig>) :
- QSTileConfigProvider {
+class QSTileConfigProviderImpl
+@Inject
+constructor(
+ private val configs: Map<String, QSTileConfig>,
+ private val qsEventLogger: QsEventLogger,
+) : QSTileConfigProvider {
init {
for (entry in configs.entries) {
@@ -44,6 +55,26 @@
}
}
+ override fun hasConfig(tileSpec: String): Boolean =
+ when (TileSpec.create(tileSpec)) {
+ is TileSpec.PlatformTileSpec -> configs.containsKey(tileSpec)
+ is TileSpec.CustomTileSpec -> true
+ is TileSpec.Invalid -> false
+ }
+
override fun getConfig(tileSpec: String): QSTileConfig =
- configs[tileSpec] ?: throw IllegalArgumentException("There is no config for spec=$tileSpec")
+ when (val spec = TileSpec.create(tileSpec)) {
+ is TileSpec.PlatformTileSpec -> {
+ configs[tileSpec]
+ ?: throw IllegalArgumentException("There is no config for spec=$tileSpec")
+ }
+ is TileSpec.CustomTileSpec ->
+ QSTileConfig(
+ spec,
+ QSTileUIConfig.Empty,
+ qsEventLogger.getNewInstanceId(),
+ )
+ is TileSpec.Invalid ->
+ throw IllegalArgumentException("TileSpec.Invalid doesn't support configs")
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index 30b87cc..f9e0b16 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -18,7 +18,10 @@
import android.content.Context
import android.service.quicksettings.Tile
+import android.view.View
+import android.widget.Switch
import com.android.systemui.common.shared.model.Icon
+import kotlin.reflect.KClass
/**
* Represents current a state of the tile to be displayed in on the view. Consider using
@@ -111,7 +114,7 @@
var stateDescription: CharSequence? = null
var sideViewIcon: SideViewIcon = SideViewIcon.None
var enabledState: EnabledState = EnabledState.ENABLED
- var expandedAccessibilityClassName: String? = null
+ var expandedAccessibilityClass: KClass<out View>? = Switch::class
fun build(): QSTileState =
QSTileState(
@@ -124,7 +127,7 @@
stateDescription,
sideViewIcon,
enabledState,
- expandedAccessibilityClassName,
+ expandedAccessibilityClass?.qualifiedName,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
index 9edd2c6..5993cf1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
@@ -16,8 +16,8 @@
package com.android.systemui.qs.ui.viewmodel
-import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import javax.inject.Inject
@@ -26,11 +26,9 @@
class QuickSettingsSceneViewModel
@Inject
constructor(
- private val bouncerInteractor: BouncerInteractor,
+ private val deviceEntryInteractor: DeviceEntryInteractor,
val shadeHeaderViewModel: ShadeHeaderViewModel,
) {
/** Notifies that some content in quick settings was clicked. */
- fun onContentClicked() {
- bouncerInteractor.showOrUnlockDevice()
- }
+ fun onContentClicked() = deviceEntryInteractor.attemptDeviceEntry()
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 91b4d17..ca2828b 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -19,8 +19,7 @@
package com.android.systemui.scene.domain.startable
import com.android.systemui.CoreStartable
-import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorActual
import com.android.systemui.dagger.SysUISingleton
@@ -45,6 +44,7 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.emptyFlow
@@ -64,7 +64,7 @@
@Application private val applicationScope: CoroutineScope,
private val sceneInteractor: SceneInteractor,
private val deviceEntryInteractor: DeviceEntryInteractor,
- private val authenticationInteractor: AuthenticationInteractor,
+ private val bouncerInteractor: BouncerInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val flags: SceneContainerFlags,
private val sysUiState: SysUiState,
@@ -121,6 +121,17 @@
/** Switches between scenes based on ever-changing application state. */
private fun automaticallySwitchScenes() {
applicationScope.launch {
+ // TODO (b/308001302): Move this to a bouncer specific interactor.
+ bouncerInteractor.onImeHidden.collectLatest {
+ if (sceneInteractor.desiredScene.value.key == SceneKey.Bouncer) {
+ sceneInteractor.changeScene(
+ scene = SceneModel(SceneKey.Lockscreen),
+ loggingReason = "IME hidden",
+ )
+ }
+ }
+ }
+ applicationScope.launch {
deviceEntryInteractor.isUnlocked
.mapNotNull { isUnlocked ->
val renderedScenes =
@@ -132,41 +143,41 @@
transitionState.toScene,
)
}
+ val isOnLockscreen = renderedScenes.contains(SceneKey.Lockscreen)
+ val isOnBouncer = renderedScenes.contains(SceneKey.Bouncer)
+ if (!isUnlocked) {
+ return@mapNotNull if (isOnLockscreen || isOnBouncer) {
+ // Already on lockscreen or bouncer, no need to change scenes.
+ null
+ } else {
+ // The device locked while on a scene that's not Lockscreen or Bouncer,
+ // go to Lockscreen.
+ SceneKey.Lockscreen to
+ "device locked in non-Lockscreen and non-Bouncer scene"
+ }
+ }
+
+ val isBypassEnabled = deviceEntryInteractor.isBypassEnabled.value
+ val canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value
when {
- isUnlocked ->
- when {
- // When the device becomes unlocked in Bouncer, go to Gone.
- renderedScenes.contains(SceneKey.Bouncer) ->
- SceneKey.Gone to "device unlocked in Bouncer scene"
-
- // When the device becomes unlocked in Lockscreen, go to Gone if
- // bypass is enabled.
- renderedScenes.contains(SceneKey.Lockscreen) ->
- if (deviceEntryInteractor.isBypassEnabled.value) {
- SceneKey.Gone to
- "device unlocked in Lockscreen scene with bypass"
- } else {
- null
- }
-
- // We got unlocked while on a scene that's not Lockscreen or
- // Bouncer, no need to change scenes.
- else -> null
- }
-
- // When the device becomes locked, to Lockscreen.
- !isUnlocked ->
- when {
- // Already on lockscreen or bouncer, no need to change scenes.
- renderedScenes.contains(SceneKey.Lockscreen) ||
- renderedScenes.contains(SceneKey.Bouncer) -> null
-
- // We got locked while on a scene that's not Lockscreen or Bouncer,
- // go to Lockscreen.
- else ->
- SceneKey.Lockscreen to
- "device locked in non-Lockscreen and non-Bouncer scene"
- }
+ isOnBouncer ->
+ // When the device becomes unlocked in Bouncer, go to Gone.
+ SceneKey.Gone to "device was unlocked in Bouncer scene"
+ isOnLockscreen ->
+ // The lockscreen should be dismissed automatically in 2 scenarios:
+ // 1. When face auth bypass is enabled and authentication happens while
+ // the user is on the lockscreen.
+ // 2. Whenever the user authenticates using an active authentication
+ // mechanism like fingerprint auth. Since canSwipeToEnter is true
+ // when the user is passively authenticated, the false value here
+ // when the unlock state changes indicates this is an active
+ // authentication attempt.
+ if (isBypassEnabled || !canSwipeToEnter)
+ SceneKey.Gone to
+ "device has been unlocked on lockscreen with either " +
+ "bypass enabled or using an active authentication mechanism"
+ else null
+ // Not on lockscreen or bouncer, so remain in the current scene.
else -> null
}
}
@@ -186,24 +197,15 @@
loggingReason = "device is starting to sleep",
)
} else {
- val authMethod = authenticationInteractor.getAuthenticationMethod()
+ val canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value
val isUnlocked = deviceEntryInteractor.isUnlocked.value
- when {
- authMethod == AuthenticationMethodModel.None -> {
- switchToScene(
- targetSceneKey = SceneKey.Gone,
- loggingReason =
- "device is starting to wake up while auth method is" + " none",
- )
- }
- authMethod.isSecure && isUnlocked -> {
- switchToScene(
- targetSceneKey = SceneKey.Gone,
- loggingReason =
- "device is starting to wake up while unlocked with a" +
- " secure auth method",
- )
- }
+ if (isUnlocked && !canSwipeToEnter) {
+ switchToScene(
+ targetSceneKey = SceneKey.Gone,
+ loggingReason =
+ "device is waking up while unlocked without the ability" +
+ " to swipe up on lockscreen to enter.",
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 285cb5a..e9c930a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1483,16 +1483,16 @@
}
private void updateMaxDisplayedNotifications(boolean recompute) {
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ return;
+ }
+
if (recompute) {
setMaxDisplayedNotifications(Math.max(computeMaxKeyguardNotifications(), 1));
} else {
if (SPEW_LOGCAT) Log.d(TAG, "Skipping computeMaxKeyguardNotifications() by request");
}
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
- return;
- }
-
if (isKeyguardShowing() && !mKeyguardBypassController.getBypassEnabled()) {
mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(
mMaxAllowedKeyguardNotifications);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index e2e4556..8bab669 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -65,6 +65,10 @@
*/
@Deprecated("Use ShadeInteractor instead") val legacyShadeTracking: StateFlow<Boolean>
+ /** Specifically tracks the user expanding the shade on the lockscreen only */
+ @Deprecated("Use ShadeInteractor.isUserInteractingWithShade instead")
+ val legacyLockscreenShadeTracking: MutableStateFlow<Boolean>
+
/**
* QuickSettingsController.mTracking as a flow. "Tracking" means that the user is moving quick
* settings up or down with a pointer. Going forward, this concept will be replaced by checks
@@ -106,6 +110,9 @@
/** Sets whether the user is moving the shade with a pointer */
fun setLegacyShadeTracking(tracking: Boolean)
+ /** Sets whether the user is moving the shade with a pointer, on lockscreen only */
+ fun setLegacyLockscreenShadeTracking(tracking: Boolean)
+
/** Amount shade has expanded with regard to the UDFPS location */
val udfpsTransitionToFullShadeProgress: StateFlow<Float>
@@ -177,6 +184,8 @@
@Deprecated("Use ShadeInteractor instead")
override val legacyShadeTracking: StateFlow<Boolean> = _legacyShadeTracking.asStateFlow()
+ override val legacyLockscreenShadeTracking = MutableStateFlow(false)
+
private val _legacyQsTracking = MutableStateFlow(false)
@Deprecated("Use ShadeInteractor instead")
override val legacyQsTracking: StateFlow<Boolean> = _legacyQsTracking.asStateFlow()
@@ -212,6 +221,11 @@
_legacyShadeTracking.value = tracking
}
+ @Deprecated("Should only be called by NPVC and tests")
+ override fun setLegacyLockscreenShadeTracking(tracking: Boolean) {
+ legacyLockscreenShadeTracking.value = tracking
+ }
+
override fun setQsExpansion(qsExpansion: Float) {
_qsExpansion.value = qsExpansion
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index b2ffeb3..d687ef6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -72,7 +72,7 @@
userSetupRepository: UserSetupRepository,
userSwitcherInteractor: UserSwitcherInteractor,
sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
- repository: ShadeRepository,
+ private val repository: ShadeRepository,
) {
/** Emits true if the shade is currently allowed and false otherwise. */
val isShadeEnabled: StateFlow<Boolean> =
@@ -185,7 +185,15 @@
if (sceneContainerFlags.isEnabled()) {
sceneBasedInteracting(sceneInteractorProvider.get(), SceneKey.Shade)
} else {
- userInteractingFlow(repository.legacyShadeTracking, repository.legacyShadeExpansion)
+ combine(
+ userInteractingFlow(
+ repository.legacyShadeTracking,
+ repository.legacyShadeExpansion
+ ),
+ repository.legacyLockscreenShadeTracking
+ ) { legacyShadeTracking, legacyLockscreenShadeTracking ->
+ legacyShadeTracking || legacyLockscreenShadeTracking
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index 9c5a201..20b9ede 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -16,7 +16,6 @@
package com.android.systemui.shade.ui.viewmodel
-import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -34,8 +33,7 @@
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- deviceEntryInteractor: DeviceEntryInteractor,
- private val bouncerInteractor: BouncerInteractor,
+ private val deviceEntryInteractor: DeviceEntryInteractor,
val shadeHeaderViewModel: ShadeHeaderViewModel,
) {
/** The key of the scene we should switch to when swiping up. */
@@ -60,9 +58,7 @@
)
/** Notifies that some content in the shade was clicked. */
- fun onContentClicked() {
- bouncerInteractor.showOrUnlockDevice()
- }
+ fun onContentClicked() = deviceEntryInteractor.attemptDeviceEntry()
private fun upDestinationSceneKey(
isUnlocked: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
index ab0d6e3..922560f 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
@@ -17,11 +17,10 @@
package com.android.systemui.smartspace.config
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.plugins.BcSmartspaceConfigPlugin
class BcSmartspaceConfigProvider(private val featureFlags: FeatureFlags) :
BcSmartspaceConfigPlugin {
override val isDefaultDateWeatherDisabled: Boolean
- get() = featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)
+ get() = true
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index bf722af..2e3f3f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -340,6 +340,7 @@
)
nsslController.resetScrollPosition()
nsslController.resetCheckSnoozeLeavebehind()
+ shadeRepository.setLegacyLockscreenShadeTracking(false)
setDragDownAmountAnimated(0f)
}
@@ -366,6 +367,7 @@
cancel()
}
}
+ shadeRepository.setLegacyLockscreenShadeTracking(true)
}
/** Do we need a falsing check currently? */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 3f080c2..0e83c78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -680,7 +680,7 @@
}
NotificationEntry entry = mCommonNotifCollectionLazy.get().getEntry(key);
if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- return entry != null
+ return entry != null && entry.getRanking().getChannel() != null
&& entry.getRanking().getChannel().getLockscreenVisibility()
== Notification.VISIBILITY_PRIVATE;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 2cd5560..ef87406 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -269,8 +269,7 @@
fun isDateWeatherDecoupled(): Boolean {
execution.assertIsMainThread()
- return featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED) &&
- datePlugin != null && weatherPlugin != null
+ return datePlugin != null && weatherPlugin != null
}
fun isWeatherEnabled(): Boolean {
@@ -501,8 +500,8 @@
}
private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
- if (isDateWeatherDecoupled()) {
- return t.featureType != SmartspaceTarget.FEATURE_WEATHER
+ if (isDateWeatherDecoupled() && t.featureType == SmartspaceTarget.FEATURE_WEATHER) {
+ return false
}
if (!showNotifications) {
return t.featureType == SmartspaceTarget.FEATURE_WEATHER
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt
new file mode 100644
index 0000000..6af2543
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt
@@ -0,0 +1,158 @@
+/*
+ * 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.statusbar.notification.interruption
+
+import android.app.NotificationManager.IMPORTANCE_HIGH
+import android.os.PowerManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.StatusBarState.KEYGUARD
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_DREAMING
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_NOT_INTERACTIVE
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_NOT_PROVISIONED
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_KEYGUARD_OCCLUDED
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_KEYGUARD_SHOWING
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_LOCKED_SHADE
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_EXPECTED_TO_HUN
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NOT_IMPORTANT_ENOUGH
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NO_FULL_SCREEN_INTENT
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NO_HUN_OR_KEYGUARD
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_PACKAGE_SUSPENDED
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SHOW_STICKY_HUN
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSED_BY_DND
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSED_ONLY_BY_DND
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSIVE_BUBBLE_METADATA
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+
+class FullScreenIntentDecisionProvider(
+ private val deviceProvisionedController: DeviceProvisionedController,
+ private val keyguardStateController: KeyguardStateController,
+ private val powerManager: PowerManager,
+ private val statusBarStateController: StatusBarStateController
+) {
+ interface Decision {
+ val shouldFsi: Boolean
+ val wouldFsiWithoutDnd: Boolean
+ val logReason: String
+ }
+
+ private enum class DecisionImpl(
+ override val shouldFsi: Boolean,
+ override val logReason: String,
+ override val wouldFsiWithoutDnd: Boolean = shouldFsi,
+ val supersedesDnd: Boolean = false
+ ) : Decision {
+ NO_FSI_NO_FULL_SCREEN_INTENT(false, "no full-screen intent", supersedesDnd = true),
+ NO_FSI_SHOW_STICKY_HUN(false, "full-screen intents are disabled", supersedesDnd = true),
+ NO_FSI_NOT_IMPORTANT_ENOUGH(false, "not important enough"),
+ NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR(false, "suppressive group alert behavior"),
+ NO_FSI_SUPPRESSIVE_BUBBLE_METADATA(false, "suppressive bubble metadata"),
+ NO_FSI_PACKAGE_SUSPENDED(false, "package suspended"),
+ FSI_DEVICE_NOT_INTERACTIVE(true, "device is not interactive"),
+ FSI_DEVICE_DREAMING(true, "device is dreaming"),
+ FSI_KEYGUARD_SHOWING(true, "keyguard is showing"),
+ NO_FSI_EXPECTED_TO_HUN(false, "expected to heads-up instead"),
+ FSI_KEYGUARD_OCCLUDED(true, "keyguard is occluded"),
+ FSI_LOCKED_SHADE(true, "locked shade"),
+ FSI_DEVICE_NOT_PROVISIONED(true, "device not provisioned"),
+ NO_FSI_NO_HUN_OR_KEYGUARD(false, "no HUN or keyguard"),
+ NO_FSI_SUPPRESSED_BY_DND(false, "suppressed by DND", wouldFsiWithoutDnd = false),
+ NO_FSI_SUPPRESSED_ONLY_BY_DND(false, "suppressed only by DND", wouldFsiWithoutDnd = true)
+ }
+
+ fun makeFullScreenIntentDecision(entry: NotificationEntry, couldHeadsUp: Boolean): Decision {
+ val reasonWithoutDnd = makeDecisionWithoutDnd(entry, couldHeadsUp)
+
+ val suppressedWithoutDnd = !reasonWithoutDnd.shouldFsi
+ val suppressedByDnd = entry.shouldSuppressFullScreenIntent()
+
+ val reasonWithDnd =
+ when {
+ reasonWithoutDnd.supersedesDnd -> reasonWithoutDnd
+ suppressedByDnd && !suppressedWithoutDnd -> NO_FSI_SUPPRESSED_ONLY_BY_DND
+ suppressedByDnd -> NO_FSI_SUPPRESSED_BY_DND
+ else -> reasonWithoutDnd
+ }
+
+ return reasonWithDnd
+ }
+
+ private fun makeDecisionWithoutDnd(
+ entry: NotificationEntry,
+ couldHeadsUp: Boolean
+ ): DecisionImpl {
+ val sbn = entry.sbn
+ val notification = sbn.notification!!
+
+ if (notification.fullScreenIntent == null) {
+ return if (entry.isStickyAndNotDemoted) {
+ NO_FSI_SHOW_STICKY_HUN
+ } else {
+ NO_FSI_NO_FULL_SCREEN_INTENT
+ }
+ }
+
+ if (entry.importance < IMPORTANCE_HIGH) {
+ return NO_FSI_NOT_IMPORTANT_ENOUGH
+ }
+
+ if (sbn.isGroup && notification.suppressAlertingDueToGrouping()) {
+ return NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR
+ }
+
+ val bubbleMetadata = notification.bubbleMetadata
+ if (bubbleMetadata != null && bubbleMetadata.isNotificationSuppressed) {
+ return NO_FSI_SUPPRESSIVE_BUBBLE_METADATA
+ }
+
+ if (entry.ranking.isSuspended) {
+ return NO_FSI_PACKAGE_SUSPENDED
+ }
+
+ if (!powerManager.isInteractive) {
+ return FSI_DEVICE_NOT_INTERACTIVE
+ }
+
+ if (statusBarStateController.isDreaming) {
+ return FSI_DEVICE_DREAMING
+ }
+
+ if (statusBarStateController.state == KEYGUARD) {
+ return FSI_KEYGUARD_SHOWING
+ }
+
+ if (couldHeadsUp) {
+ return NO_FSI_EXPECTED_TO_HUN
+ }
+
+ if (keyguardStateController.isShowing) {
+ return if (keyguardStateController.isOccluded) {
+ FSI_KEYGUARD_OCCLUDED
+ } else {
+ FSI_LOCKED_SHADE
+ }
+ }
+
+ if (!deviceProvisionedController.isDeviceProvisioned) {
+ return FSI_DEVICE_NOT_PROVISIONED
+ }
+
+ return NO_FSI_NO_HUN_OR_KEYGUARD
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index 2730683..9640682 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -18,7 +18,6 @@
import android.hardware.display.AmbientDisplayConfiguration
import android.os.Handler
import android.os.PowerManager
-import android.util.Log
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -30,7 +29,9 @@
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE
import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.settings.GlobalSettings
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -40,9 +41,11 @@
constructor(
private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
private val batteryController: BatteryController,
+ deviceProvisionedController: DeviceProvisionedController,
private val globalSettings: GlobalSettings,
private val headsUpManager: HeadsUpManager,
private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
+ keyguardStateController: KeyguardStateController,
private val logger: NotificationInterruptLogger,
@Main private val mainHandler: Handler,
private val powerManager: PowerManager,
@@ -50,6 +53,36 @@
private val systemClock: SystemClock,
private val userTracker: UserTracker,
) : VisualInterruptionDecisionProvider {
+ private class DecisionImpl(
+ override val shouldInterrupt: Boolean,
+ override val logReason: String
+ ) : Decision
+
+ private class FullScreenIntentDecisionImpl(
+ private val fsiDecision: FullScreenIntentDecisionProvider.Decision
+ ) : FullScreenIntentDecision {
+ override val shouldInterrupt
+ get() = fsiDecision.shouldFsi
+
+ override val wouldInterruptWithoutDnd
+ get() = fsiDecision.wouldFsiWithoutDnd
+
+ override val logReason
+ get() = fsiDecision.logReason
+ }
+
+ private val fullScreenIntentDecisionProvider =
+ FullScreenIntentDecisionProvider(
+ deviceProvisionedController,
+ keyguardStateController,
+ powerManager,
+ statusBarStateController
+ )
+
+ private val legacySuppressors = mutableSetOf<NotificationInterruptSuppressor>()
+ private val conditions = mutableListOf<VisualInterruptionCondition>()
+ private val filters = mutableListOf<VisualInterruptionFilter>()
+
private var started = false
override fun start() {
@@ -76,24 +109,6 @@
started = true
}
- private class DecisionImpl(
- override val shouldInterrupt: Boolean,
- override val logReason: String
- ) : Decision
-
- private class FullScreenIntentDecisionImpl(
- override val shouldInterrupt: Boolean,
- override val wouldInterruptWithoutDnd: Boolean,
- override val logReason: String,
- val originalEntry: NotificationEntry,
- ) : FullScreenIntentDecision {
- var hasBeenLogged = false
- }
-
- private val legacySuppressors = mutableSetOf<NotificationInterruptSuppressor>()
- private val conditions = mutableListOf<VisualInterruptionCondition>()
- private val filters = mutableListOf<VisualInterruptionFilter>()
-
override fun addLegacySuppressor(suppressor: NotificationInterruptSuppressor) {
legacySuppressors.add(suppressor)
}
@@ -132,32 +147,21 @@
return makeHeadsUpDecision(entry).also { logHeadsUpDecision(entry, it) }
}
+ override fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision {
+ check(started)
+ return makeBubbleDecision(entry).also { logBubbleDecision(entry, it) }
+ }
+
override fun makeUnloggedFullScreenIntentDecision(
entry: NotificationEntry
): FullScreenIntentDecision {
check(started)
- return makeFullScreenDecision(entry)
+ return makeFullScreenIntentDecision(entry)
}
override fun logFullScreenIntentDecision(decision: FullScreenIntentDecision) {
check(started)
- val decisionImpl =
- decision as? FullScreenIntentDecisionImpl
- ?: run {
- Log.wtf(TAG, "Wrong subclass of FullScreenIntentDecision: $decision")
- return
- }
- if (decision.hasBeenLogged) {
- Log.wtf(TAG, "Already logged decision: $decision")
- return
- }
- logFullScreenIntentDecision(decisionImpl)
- decision.hasBeenLogged = true
- }
-
- override fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision {
- check(started)
- return makeBubbleDecision(entry).also { logBubbleDecision(entry, it) }
+ // Not yet implemented.
}
private fun makeHeadsUpDecision(entry: NotificationEntry): DecisionImpl {
@@ -234,16 +238,6 @@
return DecisionImpl(shouldInterrupt = true, logReason = "not suppressed")
}
- private fun makeFullScreenDecision(entry: NotificationEntry): FullScreenIntentDecisionImpl {
- // Not yet implemented.
- return FullScreenIntentDecisionImpl(
- shouldInterrupt = true,
- wouldInterruptWithoutDnd = true,
- logReason = "FSI logic not yet implemented in VisualInterruptionDecisionProviderImpl",
- originalEntry = entry
- )
- }
-
private fun logHeadsUpDecision(entry: NotificationEntry, decision: DecisionImpl) {
// Not yet implemented.
}
@@ -252,8 +246,11 @@
// Not yet implemented.
}
- private fun logFullScreenIntentDecision(decision: FullScreenIntentDecisionImpl) {
- // Not yet implemented.
+ private fun makeFullScreenIntentDecision(entry: NotificationEntry): FullScreenIntentDecision {
+ val wouldHeadsUp = makeUnloggedHeadsUpDecision(entry).shouldInterrupt
+ val fsiDecision =
+ fullScreenIntentDecisionProvider.makeFullScreenIntentDecision(entry, wouldHeadsUp)
+ return FullScreenIntentDecisionImpl(fsiDecision)
}
private fun checkSuppressors(entry: NotificationEntry) =
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 a0ffba3..14ec08f35 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
@@ -253,6 +253,7 @@
private NotificationLogger.OnChildLocationsChangedListener mListener;
private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
+ private Runnable mOnHeightChangedRunnable;
private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
private boolean mNeedsAnimation;
private boolean mTopPaddingNeedsAnimation;
@@ -1121,6 +1122,10 @@
if (mOnHeightChangedListener != null) {
mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
}
+
+ if (mOnHeightChangedRunnable != null) {
+ mOnHeightChangedRunnable.run();
+ }
}
public boolean isPulseExpanding() {
@@ -4252,6 +4257,10 @@
this.mOnHeightChangedListener = onHeightChangedListener;
}
+ void setOnHeightChangedRunnable(Runnable r) {
+ this.mOnHeightChangedRunnable = r;
+ }
+
void onChildAnimationFinished() {
setAnimationRunning(false);
requestChildrenUpdate();
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 99b3a00..2cf0c26 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
@@ -20,6 +20,7 @@
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
import static com.android.app.animation.Interpolators.STANDARD;
+
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
@@ -34,7 +35,6 @@
import android.animation.ObjectAnimator;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Point;
import android.os.Trace;
import android.os.UserHandle;
@@ -65,9 +65,7 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.common.ui.ConfigurationState;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.flags.Flags;
@@ -94,7 +92,6 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -112,7 +109,6 @@
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.dagger.SilentHeader;
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -122,11 +118,9 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
-import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -960,6 +954,13 @@
mView.setOnHeightChangedListener(listener);
}
+ /**
+ * Invoked in addition to {@see #setOnHeightChangedListener}
+ */
+ public void setOnHeightChangedRunnable(Runnable r) {
+ mView.setOnHeightChangedRunnable(r);
+ }
+
public void setOverscrollTopChangedListener(
OnOverscrollTopChangedListener listener) {
mView.setOverscrollTopChangedListener(listener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index 57cea5d..eb1c17a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -18,13 +18,15 @@
package com.android.systemui.statusbar.notification.stack.domain.interactor
import android.content.Context
-import com.android.systemui.res.R
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.SplitShadeStateController
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -43,6 +45,10 @@
private val _topPosition = MutableStateFlow(0f)
val topPosition = _topPosition.asStateFlow()
+ private val _notificationStackChanged = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
+ /** An internal modification was made to notifications */
+ val notificationStackChanged = _notificationStackChanged.asSharedFlow()
+
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
configurationRepository.onAnyConfigurationChange
.onStart { emit(Unit) }
@@ -72,6 +78,11 @@
_topPosition.value = top
}
+ /** An internal modification was made to notifications */
+ fun notificationStackChanged() {
+ _notificationStackChanged.tryEmit(Unit)
+ }
+
data class ConfigurationBasedDimensions(
val useSplitShade: Boolean,
val useLargeScreenHeader: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index a1a0cca..0ff1bec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -20,6 +20,7 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import kotlinx.coroutines.DisposableHandle
@@ -33,6 +34,7 @@
view: SharedNotificationContainer,
viewModel: SharedNotificationContainerViewModel,
controller: NotificationStackScrollLayoutController,
+ notificationStackSizeCalculator: NotificationStackSizeCalculator,
): DisposableHandle {
val disposableHandle =
view.repeatWhenAttached {
@@ -54,9 +56,16 @@
}
launch {
- viewModel.maxNotifications.collect {
- controller.setMaxDisplayedNotifications(it)
- }
+ viewModel
+ .getMaxNotifications { space ->
+ notificationStackSizeCalculator.computeMaxKeyguardNotifications(
+ controller.getView(),
+ space,
+ 0f, // Vertical space for shelf is already accounted for
+ controller.getShelfHeight().toFloat(),
+ )
+ }
+ .collect { controller.setMaxDisplayedNotifications(it) }
}
launch {
@@ -70,9 +79,12 @@
}
}
+ controller.setOnHeightChangedRunnable(Runnable { viewModel.notificationStackChanged() })
+
return object : DisposableHandle {
override fun dispose() {
disposableHandle.dispose()
+ controller.setOnHeightChangedRunnable(null)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index b86b5dc..d6b6f75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -22,28 +22,26 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
/** View-model for the shared notification container, used by both the shade and keyguard spaces */
class SharedNotificationContainerViewModel
@Inject
constructor(
- interactor: SharedNotificationContainerInteractor,
+ private val interactor: SharedNotificationContainerInteractor,
keyguardInteractor: KeyguardInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
- notificationStackSizeCalculator: NotificationStackSizeCalculator,
- controller: NotificationStackScrollLayoutController,
- shadeInteractor: ShadeInteractor,
+ private val shadeInteractor: ShadeInteractor,
) {
private val statesForConstrainedNotifications =
setOf(
@@ -151,24 +149,46 @@
* When on keyguard, there is limited space to display notifications so calculate how many could
* be shown. Otherwise, there is no limit since the vertical space will be scrollable.
*
- * TODO: b/296606746 - Need to rerun logic when notifs change
+ * When expanding or when the user is interacting with the shade, keep the count stable; do not
+ * emit a value.
*/
- val maxNotifications: Flow<Int> =
- combine(isOnLockscreen, shadeInteractor.shadeExpansion, position) {
- onLockscreen,
- shadeExpansion,
- positionInfo ->
- if (onLockscreen && shadeExpansion < 1f) {
- notificationStackSizeCalculator.computeMaxKeyguardNotifications(
- controller.getView(),
- positionInfo.bottom - positionInfo.top,
- 0f, // Vertical space for shelf is already accounted for
- controller.getShelfHeight().toFloat(),
- )
- } else {
- -1 // No limit
+ fun getMaxNotifications(calculateSpace: (Float) -> Int): Flow<Int> {
+ // When to limit notifications: on lockscreen with an unexpanded shade. Also, recalculate
+ // when the notification stack has changed internally
+ val limitedNotifications =
+ combineTransform(
+ isOnLockscreen,
+ position,
+ shadeInteractor.shadeExpansion,
+ interactor.notificationStackChanged.onStart { emit(Unit) },
+ ) { isOnLockscreen, position, shadeExpansion, _ ->
+ if (isOnLockscreen && shadeExpansion == 0f) {
+ emit(calculateSpace(position.bottom - position.top))
+ }
}
- }
+
+ // When to show unlimited notifications: When the shade is fully expanded and the user is
+ // not actively dragging the shade
+ val unlimitedNotifications =
+ combineTransform(
+ shadeInteractor.shadeExpansion,
+ shadeInteractor.isUserInteracting,
+ ) { shadeExpansion, isUserInteracting ->
+ if (shadeExpansion == 1f && !isUserInteracting) {
+ emit(-1)
+ }
+ }
+
+ return merge(
+ limitedNotifications,
+ unlimitedNotifications,
+ )
+ .distinctUntilChanged()
+ }
+
+ fun notificationStackChanged() {
+ interactor.notificationStackChanged()
+ }
data class ConfigurationBasedDimensions(
val marginStart: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
index db4ab7e..5b91615 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
@@ -25,6 +25,16 @@
* The fraction after which we start fading in when going from a gone widget to a visible one
*/
private const val GONE_FADE_FRACTION = 0.8f
+/**
+ * The fraction after which we start fading in going from a gone widget to a visible one in guts
+ * animation.
+ */
+private const val GONE_FADE_GUTS_FRACTION = 0.286f
+/**
+ * The fraction before which we fade out when going from a visible widget to a gone one in guts
+ * animation.
+ */
+private const val VISIBLE_FADE_GUTS_FRACTION = 0.355f
/**
* The amont we're scaling appearing views
@@ -45,6 +55,7 @@
private var animationStartState: TransitionViewState? = null
private var state = TransitionViewState()
private var animator: ValueAnimator = ValueAnimator.ofFloat(0.0f, 1.0f)
+ private var isGutsAnimation = false
private var currentHeight: Int = 0
private var currentWidth: Int = 0
var sizeChangedListener: ((Int, Int) -> Unit)? = null
@@ -152,15 +163,6 @@
// this looks quite ugly
val nowGone: Boolean
if (widgetStart.gone) {
-
- // Only fade it in at the very end
- alphaProgress = MathUtils.map(GONE_FADE_FRACTION, 1.0f, 0.0f, 1.0f, progress)
- nowGone = progress < GONE_FADE_FRACTION
-
- // Scale it just a little, not all the way
- val endScale = widgetEnd.scale
- newScale = MathUtils.lerp(GONE_SCALE_AMOUNT * endScale, endScale, progress)
-
// don't clip
widthProgress = 1.0f
@@ -168,25 +170,52 @@
resultMeasureWidth = widgetEnd.measureWidth
resultMeasureHeight = widgetEnd.measureHeight
- // Let's make sure we're centering the view in the gone view instead of having
- // the left at 0
- resultX = MathUtils.lerp(widgetStart.x - resultMeasureWidth / 2.0f,
- widgetEnd.x,
- progress)
- resultY = MathUtils.lerp(widgetStart.y - resultMeasureHeight / 2.0f,
- widgetEnd.y,
- progress)
+ if (isGutsAnimation) {
+ // if animation is open/close guts, fade in starts early.
+ alphaProgress = MathUtils.map(
+ GONE_FADE_GUTS_FRACTION,
+ 1.0f,
+ 0.0f,
+ 1.0f,
+ progress
+ )
+ nowGone = progress < GONE_FADE_GUTS_FRACTION
+
+ // Do not change scale of widget.
+ newScale = 1.0f
+
+ // We do not want any horizontal or vertical movement.
+ resultX = widgetStart.x
+ resultY = widgetStart.y
+ } else {
+ // Only fade it in at the very end
+ alphaProgress = MathUtils.map(
+ GONE_FADE_FRACTION,
+ 1.0f,
+ 0.0f,
+ 1.0f,
+ progress
+ )
+ nowGone = progress < GONE_FADE_FRACTION
+
+ // Scale it just a little, not all the way
+ val endScale = widgetEnd.scale
+ newScale = MathUtils.lerp(GONE_SCALE_AMOUNT * endScale, endScale, progress)
+
+ // Let's make sure we're centering the view in the gone view instead of
+ // having the left at 0
+ resultX = MathUtils.lerp(
+ widgetStart.x - resultMeasureWidth / 2.0f,
+ widgetEnd.x,
+ progress
+ )
+ resultY = MathUtils.lerp(
+ widgetStart.y - resultMeasureHeight / 2.0f,
+ widgetEnd.y,
+ progress
+ )
+ }
} else {
-
- // Fadeout in the very beginning
- alphaProgress = MathUtils.map(0.0f, 1.0f - GONE_FADE_FRACTION, 0.0f, 1.0f,
- progress)
- nowGone = progress > 1.0f - GONE_FADE_FRACTION
-
- // Scale it just a little, not all the way
- val startScale = widgetStart.scale
- newScale = MathUtils.lerp(startScale, startScale * GONE_SCALE_AMOUNT, progress)
-
// Don't clip
widthProgress = 0.0f
@@ -194,14 +223,54 @@
resultMeasureWidth = widgetStart.measureWidth
resultMeasureHeight = widgetStart.measureHeight
- // Let's make sure we're centering the view in the gone view instead of having
- // the left at 0
- resultX = MathUtils.lerp(widgetStart.x,
- widgetEnd.x - resultMeasureWidth / 2.0f,
- progress)
- resultY = MathUtils.lerp(widgetStart.y,
- widgetEnd.y - resultMeasureHeight / 2.0f,
- progress)
+ // Fadeout in the very beginning
+ if (isGutsAnimation) {
+ alphaProgress = MathUtils.map(
+ 0.0f,
+ VISIBLE_FADE_GUTS_FRACTION,
+ 0.0f,
+ 1.0f,
+ progress
+ )
+ nowGone = progress > VISIBLE_FADE_GUTS_FRACTION
+
+ // Do not change scale of widget during open/close guts animation.
+ newScale = 1.0f
+
+ // We do not want any horizontal or vertical movement.
+ resultX = widgetEnd.x
+ resultY = widgetEnd.y
+ } else {
+ alphaProgress = MathUtils.map(
+ 0.0f,
+ 1.0f - GONE_FADE_FRACTION,
+ 0.0f,
+ 1.0f,
+ progress
+ )
+ nowGone = progress > 1.0f - GONE_FADE_FRACTION
+
+ // Scale it just a little, not all the way
+ val startScale = widgetStart.scale
+ newScale = MathUtils.lerp(
+ startScale,
+ startScale * GONE_SCALE_AMOUNT,
+ progress
+ )
+
+ // Let's make sure we're centering the view in the gone view instead of
+ // having the left at 0
+ resultX = MathUtils.lerp(
+ widgetStart.x,
+ widgetEnd.x - resultMeasureWidth / 2.0f,
+ progress
+ )
+ resultY = MathUtils.lerp(
+ widgetStart.y,
+ widgetEnd.y - resultMeasureHeight / 2.0f,
+ progress
+ )
+ }
}
resultWidgetState.gone = nowGone
} else {
@@ -279,8 +348,10 @@
applyImmediately: Boolean,
animate: Boolean,
duration: Long = 0,
- delay: Long = 0
+ delay: Long = 0,
+ isGuts: Boolean,
) {
+ isGutsAnimation = isGuts
val animated = animate && currentState.width != 0
this.state = state.copy()
if (applyImmediately || transitionLayout == null) {
@@ -291,6 +362,8 @@
animationStartState = currentState.copy()
animator.duration = duration
animator.startDelay = delay
+ animator.interpolator =
+ if (isGutsAnimation) Interpolators.LINEAR else Interpolators.FAST_OUT_SLOW_IN
animator.start()
} else if (!animator.isRunning) {
applyStateToLayout(this.state)
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
index ffbc10a..2336a8e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
@@ -16,6 +16,9 @@
package com.android.systemui.util.kotlin
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+
class Utils {
companion object {
fun <A, B, C> toTriple(a: A, bc: Pair<B, C>) = Triple(a, bc.first, bc.second)
@@ -27,6 +30,45 @@
fun <A, B, C, D, E> toQuint(a: A, bcde: Quad<B, C, D, E>) =
Quint(a, bcde.first, bcde.second, bcde.third, bcde.fourth)
+
+ /**
+ * Samples the provided flows, emitting a tuple of the original flow's value as well as each
+ * of the combined flows' values.
+ *
+ * Flow<A>.sample(Flow<B>, Flow<C>) -> (A, B, C)
+ */
+ fun <A, B, C> Flow<A>.sample(b: Flow<B>, c: Flow<C>): Flow<Triple<A, B, C>> {
+ return this.sample(combine(b, c, ::Pair), ::toTriple)
+ }
+
+ /**
+ * Samples the provided flows, emitting a tuple of the original flow's value as well as each
+ * of the combined flows' values.
+ *
+ * Flow<A>.sample(Flow<B>, Flow<C>, Flow<D>) -> (A, B, C, D)
+ */
+ fun <A, B, C, D> Flow<A>.sample(
+ b: Flow<B>,
+ c: Flow<C>,
+ d: Flow<D>
+ ): Flow<Quad<A, B, C, D>> {
+ return this.sample(combine(b, c, d, ::Triple), ::toQuad)
+ }
+
+ /**
+ * Samples the provided flows, emitting a tuple of the original flow's value as well as each
+ * of the combined flows' values.
+ *
+ * Flow<A>.sample(Flow<B>, Flow<C>, Flow<D>, Flow<E>) -> (A, B, C, D, E)
+ */
+ fun <A, B, C, D, E> Flow<A>.sample(
+ b: Flow<B>,
+ c: Flow<C>,
+ d: Flow<D>,
+ e: Flow<E>,
+ ): Flow<Quint<A, B, C, D, E>> {
+ return this.sample(combine(b, c, d, e, ::Quad), ::toQuint)
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index 1d4f2cb..d2f45ae 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -42,8 +42,8 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.AuthRippleController;
-import com.android.systemui.bouncer.domain.interactor.BouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
import com.android.systemui.doze.util.BurnInHelperKt;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
@@ -78,7 +78,7 @@
protected MockitoSession mStaticMockSession;
protected final SceneTestUtils mSceneTestUtils = new SceneTestUtils(this);
- protected @Mock BouncerInteractor mBouncerInteractor;
+ protected @Mock DeviceEntryInteractor mDeviceEntryInteractor;
protected @Mock LockIconView mLockIconView;
protected @Mock AnimatedStateListDrawable mIconDrawable;
protected @Mock Context mContext;
@@ -176,7 +176,7 @@
mFeatureFlags,
mPrimaryBouncerInteractor,
mContext,
- () -> mBouncerInteractor,
+ () -> mDeviceEntryInteractor,
mSceneTestUtils.getSceneContainerFlags()
);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index adcec10..93a5393 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -381,7 +381,7 @@
// THEN show primary bouncer via keyguard view controller, not scene container
verify(mKeyguardViewController).showPrimaryBouncer(anyBoolean());
- verify(mBouncerInteractor, never()).showOrUnlockDevice(any());
+ verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
}
@Test
@@ -395,7 +395,7 @@
// THEN show primary bouncer
verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean());
- verify(mBouncerInteractor).showOrUnlockDevice(any());
+ verify(mDeviceEntryInteractor).attemptDeviceEntry();
}
@Test
@@ -408,6 +408,7 @@
mUnderTest.onLongPress();
// THEN don't show primary bouncer
- verify(mBouncerInteractor, never()).showOrUnlockDevice(any());
+ verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
+ verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
index 6ac84bc..ae2ec2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
@@ -26,7 +26,7 @@
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityModel
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.scene.SceneTestUtils
@@ -100,12 +100,12 @@
}
@Test
- fun isAutoConfirmEnabled() =
+ fun isAutoConfirmFeatureEnabled() =
testScope.runTest {
whenever(lockPatternUtils.isAutoPinConfirmEnabled(USER_INFOS[0].id)).thenReturn(true)
whenever(lockPatternUtils.isAutoPinConfirmEnabled(USER_INFOS[1].id)).thenReturn(false)
- val values by collectValues(underTest.isAutoConfirmEnabled)
+ val values by collectValues(underTest.isAutoConfirmFeatureEnabled)
assertThat(values.first()).isFalse()
assertThat(values.last()).isTrue()
@@ -127,6 +127,22 @@
assertThat(values.last()).isTrue()
}
+ @Test
+ fun reportAuthenticationAttempt_emitsAuthenticationChallengeResult() =
+ testScope.runTest {
+ val authenticationChallengeResults by
+ collectValues(underTest.authenticationChallengeResult)
+
+ runCurrent()
+ underTest.reportAuthenticationAttempt(true)
+ runCurrent()
+ underTest.reportAuthenticationAttempt(false)
+ runCurrent()
+ underTest.reportAuthenticationAttempt(true)
+
+ assertThat(authenticationChallengeResults).isEqualTo(listOf(true, false, true))
+ }
+
private fun setSecurityModeAndDispatchBroadcast(
securityMode: KeyguardSecurityModel.SecurityMode,
) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index 2f5f460..7439db2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -20,9 +20,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.coroutines.collectLastValue
@@ -51,33 +50,16 @@
testScope.runTest {
val authMethod by collectLastValue(underTest.authenticationMethod)
runCurrent()
- assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.Pin)
- assertThat(underTest.getAuthenticationMethod())
- .isEqualTo(DomainLayerAuthenticationMethodModel.Pin)
+ assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Pin)
+ assertThat(underTest.getAuthenticationMethod()).isEqualTo(AuthenticationMethodModel.Pin)
utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Password
+ AuthenticationMethodModel.Password
)
- assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.Password)
+ assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Password)
assertThat(underTest.getAuthenticationMethod())
- .isEqualTo(DomainLayerAuthenticationMethodModel.Password)
- }
-
- @Test
- fun authenticationMethod_noneTreatedAsSwipe_whenLockscreenEnabled() =
- testScope.runTest {
- val authMethod by collectLastValue(underTest.authenticationMethod)
- runCurrent()
-
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.None
- )
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
-
- assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.Swipe)
- assertThat(underTest.getAuthenticationMethod())
- .isEqualTo(DomainLayerAuthenticationMethodModel.Swipe)
+ .isEqualTo(AuthenticationMethodModel.Password)
}
@Test
@@ -86,23 +68,18 @@
val authMethod by collectLastValue(underTest.authenticationMethod)
runCurrent()
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.None
- )
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(false)
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- assertThat(authMethod).isEqualTo(DomainLayerAuthenticationMethodModel.None)
+ assertThat(authMethod).isEqualTo(AuthenticationMethodModel.None)
assertThat(underTest.getAuthenticationMethod())
- .isEqualTo(DomainLayerAuthenticationMethodModel.None)
+ .isEqualTo(AuthenticationMethodModel.None)
}
@Test
fun authenticate_withCorrectPin_returnsTrue() =
testScope.runTest {
val isThrottled by collectLastValue(underTest.isThrottled)
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SUCCEEDED)
assertThat(isThrottled).isFalse()
@@ -111,9 +88,7 @@
@Test
fun authenticate_withIncorrectPin_returnsFalse() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
assertThat(underTest.authenticate(listOf(9, 8, 7, 6, 5, 4)))
.isEqualTo(AuthenticationResult.FAILED)
}
@@ -121,9 +96,7 @@
@Test(expected = IllegalArgumentException::class)
fun authenticate_withEmptyPin_throwsException() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
underTest.authenticate(listOf())
}
@@ -132,7 +105,7 @@
testScope.runTest {
val pin = List(16) { 9 }
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
overrideCredential(pin)
}
@@ -148,9 +121,7 @@
// If the policy changes, there is work to do in SysUI.
assertThat(DevicePolicyManager.MAX_PASSWORD_LENGTH).isLessThan(17)
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
assertThat(underTest.authenticate(List(17) { 9 }))
.isEqualTo(AuthenticationResult.FAILED)
}
@@ -160,7 +131,7 @@
testScope.runTest {
val isThrottled by collectLastValue(underTest.isThrottled)
utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Password
+ AuthenticationMethodModel.Password
)
assertThat(underTest.authenticate("password".toList()))
@@ -172,7 +143,7 @@
fun authenticate_withIncorrectPassword_returnsFalse() =
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Password
+ AuthenticationMethodModel.Password
)
assertThat(underTest.authenticate("alohomora".toList()))
@@ -183,7 +154,7 @@
fun authenticate_withCorrectPattern_returnsTrue() =
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pattern
+ AuthenticationMethodModel.Pattern
)
assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN))
@@ -194,7 +165,7 @@
fun authenticate_withIncorrectPattern_returnsFalse() =
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pattern
+ AuthenticationMethodModel.Pattern
)
assertThat(
@@ -211,13 +182,16 @@
}
@Test
- fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNullAndHasNoEffect() =
+ fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNull() =
testScope.runTest {
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
val isThrottled by collectLastValue(underTest.isThrottled)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
- setAutoConfirmEnabled(true)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(true)
}
+ assertThat(isAutoConfirmEnabled).isTrue()
+
assertThat(
underTest.authenticate(
FakeAuthenticationRepository.DEFAULT_PIN.toMutableList().apply {
@@ -231,12 +205,15 @@
}
@Test
- fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalseAndDoesNotUnlockDevice() =
+ fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalse() =
testScope.runTest {
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
- setAutoConfirmEnabled(true)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(true)
}
+ assertThat(isAutoConfirmEnabled).isTrue()
+
assertThat(
underTest.authenticate(
FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 },
@@ -244,17 +221,18 @@
)
)
.isEqualTo(AuthenticationResult.FAILED)
- val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
- assertThat(isUnlocked).isFalse()
}
@Test
- fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalseAndDoesNotUnlockDevice() =
+ fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalse() =
testScope.runTest {
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
- setAutoConfirmEnabled(true)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(true)
}
+ assertThat(isAutoConfirmEnabled).isTrue()
+
assertThat(
underTest.authenticate(
FakeAuthenticationRepository.DEFAULT_PIN + listOf(7),
@@ -262,17 +240,18 @@
)
)
.isEqualTo(AuthenticationResult.FAILED)
- val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
- assertThat(isUnlocked).isFalse()
}
@Test
- fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrueAndUnlocksDevice() =
+ fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrue() =
testScope.runTest {
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
- setAutoConfirmEnabled(true)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(true)
}
+ assertThat(isAutoConfirmEnabled).isTrue()
+
assertThat(
underTest.authenticate(
FakeAuthenticationRepository.DEFAULT_PIN,
@@ -280,16 +259,38 @@
)
)
.isEqualTo(AuthenticationResult.SUCCEEDED)
- val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
- assertThat(isUnlocked).isTrue()
}
@Test
- fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNullAndHasNoEffects() =
+ fun tryAutoConfirm_withAutoConfirmCorrectPinButDuringThrottling_returnsNull() =
+ testScope.runTest {
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
+ val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
+ val hintedPinLength by collectLastValue(underTest.hintedPinLength)
+ utils.authenticationRepository.apply {
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(true)
+ setThrottleDuration(42)
+ }
+
+ val authResult =
+ underTest.authenticate(
+ FakeAuthenticationRepository.DEFAULT_PIN,
+ tryAutoConfirm = true
+ )
+
+ assertThat(authResult).isEqualTo(AuthenticationResult.SKIPPED)
+ assertThat(isAutoConfirmEnabled).isFalse()
+ assertThat(isUnlocked).isFalse()
+ assertThat(hintedPinLength).isNull()
+ }
+
+ @Test
+ fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNull() =
testScope.runTest {
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
- setAutoConfirmEnabled(false)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(false)
}
assertThat(
underTest.authenticate(
@@ -298,53 +299,38 @@
)
)
.isEqualTo(AuthenticationResult.SKIPPED)
- val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
- assertThat(isUnlocked).isFalse()
}
@Test
- fun tryAutoConfirm_withoutCorrectPassword_returnsNullAndHasNoEffects() =
+ fun tryAutoConfirm_withoutCorrectPassword_returnsNull() =
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Password
+ AuthenticationMethodModel.Password
)
assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true))
.isEqualTo(AuthenticationResult.SKIPPED)
- val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
- assertThat(isUnlocked).isFalse()
}
@Test
fun throttling() =
testScope.runTest {
- val isUnlocked by collectLastValue(utils.deviceEntryRepository.isUnlocked)
val throttling by collectLastValue(underTest.throttling)
val isThrottled by collectLastValue(underTest.isThrottled)
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)
- assertThat(isUnlocked).isTrue()
- assertThat(isThrottled).isFalse()
- assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
-
- utils.deviceEntryRepository.setUnlocked(false)
- assertThat(isUnlocked).isFalse()
assertThat(isThrottled).isFalse()
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
// Make many wrong attempts, but just shy of what's needed to get throttled:
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1) {
underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN
- assertThat(isUnlocked).isFalse()
assertThat(isThrottled).isFalse()
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
}
// Make one more wrong attempt, leading to throttling:
underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN
- assertThat(isUnlocked).isFalse()
assertThat(isThrottled).isTrue()
assertThat(throttling)
.isEqualTo(
@@ -358,7 +344,6 @@
// Correct PIN, but throttled, so doesn't attempt it:
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SKIPPED)
- assertThat(isUnlocked).isFalse()
assertThat(isThrottled).isTrue()
assertThat(throttling)
.isEqualTo(
@@ -391,7 +376,6 @@
// Move the clock forward one more second, to completely finish the throttling period:
advanceTimeBy(1000)
- assertThat(isUnlocked).isFalse()
assertThat(isThrottled).isFalse()
assertThat(throttling)
.isEqualTo(
@@ -405,7 +389,6 @@
// Correct PIN and no longer throttled so unlocks successfully:
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(isUnlocked).isTrue()
assertThat(isThrottled).isFalse()
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
}
@@ -415,8 +398,8 @@
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
- setAutoConfirmEnabled(false)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(false)
}
assertThat(hintedPinLength).isNull()
@@ -427,13 +410,13 @@
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
overrideCredential(
buildList {
repeat(utils.authenticationRepository.hintedPinLength - 1) { add(it + 1) }
}
)
- setAutoConfirmEnabled(true)
+ setAutoConfirmFeatureEnabled(true)
}
assertThat(hintedPinLength).isNull()
@@ -444,8 +427,8 @@
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
- setAutoConfirmEnabled(true)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ setAutoConfirmFeatureEnabled(true)
overrideCredential(
buildList {
repeat(utils.authenticationRepository.hintedPinLength) { add(it + 1) }
@@ -461,13 +444,13 @@
testScope.runTest {
val hintedPinLength by collectLastValue(underTest.hintedPinLength)
utils.authenticationRepository.apply {
- setAuthenticationMethod(DataLayerAuthenticationMethodModel.Pin)
+ setAuthenticationMethod(AuthenticationMethodModel.Pin)
overrideCredential(
buildList {
repeat(utils.authenticationRepository.hintedPinLength + 1) { add(it + 1) }
}
)
- setAutoConfirmEnabled(true)
+ setAutoConfirmFeatureEnabled(true)
}
assertThat(hintedPinLength).isNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 915661b..6ead0e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -19,16 +19,14 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.domain.interactor.AuthenticationResult
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
import kotlin.math.ceil
import kotlin.time.Duration.Companion.milliseconds
@@ -48,17 +46,9 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val authenticationInteractor = utils.authenticationInteractor()
- private val sceneInteractor = utils.sceneInteractor()
- private val deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
- )
private val underTest =
utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
)
@Before
@@ -74,16 +64,10 @@
@Test
fun pinAuthMethod() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(underTest.message)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
runCurrent()
- utils.deviceEntryRepository.setUnlocked(false)
- underTest.showOrUnlockDevice()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
-
underTest.clearMessage()
assertThat(message).isEmpty()
@@ -94,7 +78,6 @@
assertThat(underTest.authenticate(listOf(9, 8, 7)))
.isEqualTo(AuthenticationResult.FAILED)
assertThat(message).isEqualTo(MESSAGE_WRONG_PIN)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.resetMessage()
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
@@ -102,35 +85,25 @@
// Correct input.
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@Test
fun pinAuthMethod_tryAutoConfirm_withAutoConfirmPin() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- val message by collectLastValue(underTest.message)
+ val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
runCurrent()
- utils.authenticationRepository.setAutoConfirmEnabled(true)
- utils.deviceEntryRepository.setUnlocked(false)
- underTest.showOrUnlockDevice()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
- underTest.clearMessage()
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+ assertThat(isAutoConfirmEnabled).isTrue()
// Incomplete input.
assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true))
.isEqualTo(AuthenticationResult.SKIPPED)
- assertThat(message).isEmpty()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
// Wrong 6-digit pin
assertThat(underTest.authenticate(listOf(1, 2, 3, 5, 5, 6), tryAutoConfirm = true))
.isEqualTo(AuthenticationResult.FAILED)
- assertThat(message).isEqualTo(MESSAGE_WRONG_PIN)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
// Correct input.
assertThat(
@@ -140,27 +113,20 @@
)
)
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@Test
fun pinAuthMethod_tryAutoConfirm_withoutAutoConfirmPin() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(underTest.message)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
runCurrent()
- utils.deviceEntryRepository.setUnlocked(false)
- underTest.showOrUnlockDevice()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.clearMessage()
// Incomplete input.
assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true))
.isEqualTo(AuthenticationResult.SKIPPED)
assertThat(message).isEmpty()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
// Correct input.
assertThat(
@@ -171,25 +137,16 @@
)
.isEqualTo(AuthenticationResult.SKIPPED)
assertThat(message).isEmpty()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun passwordAuthMethod() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(underTest.message)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Password
)
runCurrent()
- utils.deviceEntryRepository.setUnlocked(false)
- underTest.showOrUnlockDevice()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)
-
- underTest.clearMessage()
- assertThat(message).isEmpty()
underTest.resetMessage()
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)
@@ -198,7 +155,6 @@
assertThat(underTest.authenticate("alohamora".toList()))
.isEqualTo(AuthenticationResult.FAILED)
assertThat(message).isEqualTo(MESSAGE_WRONG_PASSWORD)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.resetMessage()
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)
@@ -206,26 +162,16 @@
// Correct input.
assertThat(underTest.authenticate("password".toList()))
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@Test
fun patternAuthMethod() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(underTest.message)
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern
)
runCurrent()
- utils.deviceEntryRepository.setUnlocked(false)
- underTest.showOrUnlockDevice()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
-
- underTest.clearMessage()
- assertThat(message).isEmpty()
-
underTest.resetMessage()
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
@@ -241,7 +187,6 @@
assertThat(wrongPattern.size).isAtLeast(utils.authenticationRepository.minPatternLength)
assertThat(underTest.authenticate(wrongPattern)).isEqualTo(AuthenticationResult.FAILED)
assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.resetMessage()
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
@@ -255,7 +200,6 @@
assertThat(underTest.authenticate(tooShortPattern))
.isEqualTo(AuthenticationResult.SKIPPED)
assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.resetMessage()
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
@@ -263,51 +207,6 @@
// Correct input.
assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN))
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
- }
-
- @Test
- fun showOrUnlockDevice_notLocked_switchesToGoneScene() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.deviceEntryRepository.setUnlocked(true)
- runCurrent()
-
- underTest.showOrUnlockDevice()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
- }
-
- @Test
- fun showOrUnlockDevice_authMethodNotSecure_switchesToGoneScene() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
- utils.deviceEntryRepository.setUnlocked(false)
-
- underTest.showOrUnlockDevice()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
- }
-
- @Test
- fun showOrUnlockDevice_customMessageShown() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- val message by collectLastValue(underTest.message)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
- runCurrent()
- utils.deviceEntryRepository.setUnlocked(false)
-
- val customMessage = "Hello there!"
- underTest.showOrUnlockDevice(customMessage)
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(message).isEqualTo(customMessage)
}
@Test
@@ -316,15 +215,9 @@
val isThrottled by collectLastValue(underTest.isThrottled)
val throttling by collectLastValue(underTest.throttling)
val message by collectLastValue(underTest.message)
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- runCurrent()
- underTest.showOrUnlockDevice()
- runCurrent()
- assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer)
assertThat(isThrottled).isFalse()
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
- assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { times ->
// Wrong PIN.
assertThat(underTest.authenticate(listOf(6, 7, 8, 9)))
@@ -353,7 +246,6 @@
// Correct PIN, but throttled, so doesn't change away from the bouncer scene:
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SKIPPED)
- assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer)
assertTryAgainMessage(
message,
FakeAuthenticationRepository.THROTTLE_DURATION_MS.milliseconds.inWholeSeconds
@@ -379,42 +271,24 @@
FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING,
)
)
- assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer)
// Correct PIN and no longer throttled so changes to the Gone scene:
assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
.isEqualTo(AuthenticationResult.SUCCEEDED)
- assertThat(currentScene?.key).isEqualTo(SceneKey.Gone)
assertThat(isThrottled).isFalse()
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
}
@Test
- fun hide_whenOnBouncerScene_hidesBouncerAndGoesToLockscreenScene() =
+ fun imeHiddenEvent_isTriggered() =
testScope.runTest {
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "")
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- val bouncerSceneKey = currentScene?.key
- assertThat(bouncerSceneKey).isEqualTo(SceneKey.Bouncer)
+ val imeHiddenEvent by collectLastValue(underTest.onImeHidden)
+ runCurrent()
underTest.onImeHidden()
+ runCurrent()
- assertThat(currentScene?.key).isEqualTo(SceneKey.Lockscreen)
- }
-
- @Test
- fun hide_whenNotOnBouncerScene_doesNothing() =
- testScope.runTest {
- sceneInteractor.changeScene(SceneModel(SceneKey.Shade), "")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "")
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- val notBouncerSceneKey = currentScene?.key
- assertThat(notBouncerSceneKey).isNotEqualTo(SceneKey.Bouncer)
-
- underTest.onImeHidden()
-
- assertThat(currentScene?.key).isEqualTo(notBouncerSceneKey)
+ assertThat(imeHiddenEvent).isNotNull()
}
private fun assertTryAgainMessage(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index 8e1f5ac..cfcb545 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -19,8 +19,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
@@ -37,23 +37,16 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
- private val authenticationInteractor = utils.authenticationInteractor()
private val sceneInteractor = utils.sceneInteractor()
- private val deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
+ private val bouncerInteractor =
+ utils.bouncerInteractor(
+ authenticationInteractor = utils.authenticationInteractor(),
)
private val underTest =
PinBouncerViewModel(
applicationContext = context,
viewModelScope = testScope.backgroundScope,
- interactor =
- utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
- ),
+ interactor = bouncerInteractor,
isInputEnabled = MutableStateFlow(true),
)
@@ -85,18 +78,14 @@
@Test
fun onImeVisibilityChanged() =
testScope.runTest {
- val desiredScene by collectLastValue(sceneInteractor.desiredScene)
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "")
- assertThat(desiredScene?.key).isEqualTo(SceneKey.Bouncer)
-
- underTest.onImeVisibilityChanged(false)
- assertThat(desiredScene?.key).isEqualTo(SceneKey.Bouncer)
+ val onImeHidden by collectLastValue(bouncerInteractor.onImeHidden)
underTest.onImeVisibilityChanged(true)
- assertThat(desiredScene?.key).isEqualTo(SceneKey.Bouncer)
+ assertThat(onImeHidden).isNull()
underTest.onImeVisibilityChanged(false)
- assertThat(desiredScene?.key).isEqualTo(SceneKey.Lockscreen)
+ assertThat(onImeHidden).isNotNull()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index 6357a1a..f4346b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -19,10 +19,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.scene.SceneTestUtils
@@ -48,16 +46,9 @@
private val testScope = utils.testScope
private val authenticationInteractor = utils.authenticationInteractor()
private val actionButtonInteractor = utils.bouncerActionButtonInteractor()
- private val deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = utils.sceneInteractor(),
- )
private val bouncerInteractor =
utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
- sceneInteractor = utils.sceneInteractor(),
)
private val underTest =
utils.bouncerViewModel(
@@ -96,8 +87,7 @@
@Test
fun authMethodChanged_doesNotReuseInstances() =
testScope.runTest {
- val seen =
- mutableMapOf<DomainLayerAuthenticationMethodModel, AuthMethodBouncerViewModel>()
+ val seen = mutableMapOf<AuthenticationMethodModel, AuthMethodBouncerViewModel>()
val authMethodViewModel: AuthMethodBouncerViewModel? by
collectLastValue(underTest.authMethodViewModel)
@@ -137,7 +127,7 @@
@Test
fun authMethodsToTest_returnsCompleteSampleOfAllAuthMethodTypes() {
assertThat(authMethodsToTest().map { it::class }.toSet())
- .isEqualTo(DomainLayerAuthenticationMethodModel::class.sealedSubclasses.toSet())
+ .isEqualTo(AuthenticationMethodModel::class.sealedSubclasses.toSet())
}
@Test
@@ -145,9 +135,7 @@
testScope.runTest {
val message by collectLastValue(underTest.message)
val throttling by collectLastValue(bouncerInteractor.throttling)
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
assertThat(message?.isUpdateAnimated).isTrue()
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) {
@@ -170,9 +158,7 @@
}
)
val throttling by collectLastValue(bouncerInteractor.throttling)
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
assertThat(isInputEnabled).isTrue()
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) {
@@ -189,9 +175,7 @@
fun throttlingDialogMessage() =
testScope.runTest {
val throttlingDialogMessage by collectLastValue(underTest.throttlingDialogMessage)
- utils.authenticationRepository.setAuthenticationMethod(
- DataLayerAuthenticationMethodModel.Pin
- )
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) {
// Wrong PIN.
@@ -243,34 +227,12 @@
assertThat(isFoldSplitRequired).isTrue()
}
- private fun authMethodsToTest(): List<DomainLayerAuthenticationMethodModel> {
+ private fun authMethodsToTest(): List<AuthenticationMethodModel> {
return listOf(
- DomainLayerAuthenticationMethodModel.None,
- DomainLayerAuthenticationMethodModel.Swipe,
- DomainLayerAuthenticationMethodModel.Pin,
- DomainLayerAuthenticationMethodModel.Password,
- DomainLayerAuthenticationMethodModel.Pattern,
- )
- }
-
- private fun FakeAuthenticationRepository.setAuthenticationMethod(
- model: DomainLayerAuthenticationMethodModel,
- ) {
- setAuthenticationMethod(
- when (model) {
- is DomainLayerAuthenticationMethodModel.None,
- is DomainLayerAuthenticationMethodModel.Swipe ->
- DataLayerAuthenticationMethodModel.None
- is DomainLayerAuthenticationMethodModel.Pin ->
- DataLayerAuthenticationMethodModel.Pin
- is DomainLayerAuthenticationMethodModel.Password ->
- DataLayerAuthenticationMethodModel.Password
- is DomainLayerAuthenticationMethodModel.Pattern ->
- DataLayerAuthenticationMethodModel.Pattern
- }
- )
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(
- model !is DomainLayerAuthenticationMethodModel.None
+ AuthenticationMethodModel.None,
+ AuthenticationMethodModel.Pin,
+ AuthenticationMethodModel.Password,
+ AuthenticationMethodModel.Pattern,
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 390742031..c498edf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -19,8 +19,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainAuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
@@ -46,16 +45,9 @@
private val testScope = utils.testScope
private val authenticationInteractor = utils.authenticationInteractor()
private val sceneInteractor = utils.sceneInteractor()
- private val deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = utils.sceneInteractor(),
- )
private val bouncerInteractor =
utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
)
private val bouncerViewModel =
utils.bouncerViewModel(
@@ -88,8 +80,7 @@
assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD)
assertThat(password).isEqualTo("")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(underTest.authenticationMethod)
- .isEqualTo(DomainAuthenticationMethodModel.Password)
+ assertThat(underTest.authenticationMethod).isEqualTo(AuthenticationMethodModel.Password)
}
@Test
@@ -110,19 +101,19 @@
@Test
fun onAuthenticateKeyPressed_whenCorrect() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val authResult by
+ collectLastValue(authenticationInteractor.authenticationChallengeResult)
lockDeviceAndOpenPasswordBouncer()
underTest.onPasswordInputChanged("password")
underTest.onAuthenticateKeyPressed()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ assertThat(authResult).isTrue()
}
@Test
fun onAuthenticateKeyPressed_whenWrong() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
lockDeviceAndOpenPasswordBouncer()
@@ -132,13 +123,11 @@
assertThat(password).isEqualTo("")
assertThat(message?.text).isEqualTo(WRONG_PASSWORD)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onAuthenticateKeyPressed_whenEmpty() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
utils.authenticationRepository.setAuthenticationMethod(
@@ -147,7 +136,6 @@
utils.deviceEntryRepository.setUnlocked(false)
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
underTest.onShown()
// Enter nothing.
@@ -155,13 +143,13 @@
assertThat(password).isEqualTo("")
assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@Test
fun onAuthenticateKeyPressed_correctAfterWrong() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val authResult by
+ collectLastValue(authenticationInteractor.authenticationChallengeResult)
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
lockDeviceAndOpenPasswordBouncer()
@@ -171,7 +159,7 @@
underTest.onAuthenticateKeyPressed()
assertThat(password).isEqualTo("")
assertThat(message?.text).isEqualTo(WRONG_PASSWORD)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+ assertThat(authResult).isFalse()
// Enter the correct password:
underTest.onPasswordInputChanged("password")
@@ -179,7 +167,7 @@
underTest.onAuthenticateKeyPressed()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ assertThat(authResult).isTrue()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 47db4f8..3f5ddba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -19,9 +19,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainAuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate as Point
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.res.R
@@ -49,16 +48,9 @@
private val testScope = utils.testScope
private val authenticationInteractor = utils.authenticationInteractor()
private val sceneInteractor = utils.sceneInteractor()
- private val deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = utils.sceneInteractor(),
- )
private val bouncerInteractor =
utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
)
private val bouncerViewModel =
utils.bouncerViewModel(
@@ -96,8 +88,7 @@
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(underTest.authenticationMethod)
- .isEqualTo(DomainAuthenticationMethodModel.Pattern)
+ assertThat(underTest.authenticationMethod).isEqualTo(AuthenticationMethodModel.Pattern)
}
@Test
@@ -120,7 +111,8 @@
@Test
fun onDragEnd_whenCorrect() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val authResult by
+ collectLastValue(authenticationInteractor.authenticationChallengeResult)
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
lockDeviceAndOpenPatternBouncer()
@@ -150,7 +142,7 @@
underTest.onDragEnd()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ assertThat(authResult).isTrue()
}
@Test
@@ -344,7 +336,8 @@
@Test
fun onDragEnd_correctAfterWrong() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val authResult by
+ collectLastValue(authenticationInteractor.authenticationChallengeResult)
val message by collectLastValue(bouncerViewModel.message)
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
@@ -356,14 +349,14 @@
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
assertThat(message?.text).isEqualTo(WRONG_PATTERN)
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+ assertThat(authResult).isFalse()
// Enter the correct pattern:
CORRECT_PATTERN.forEach(::dragToCoordinate)
underTest.onDragEnd()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ assertThat(authResult).isTrue()
}
private fun dragOverCoordinates(vararg coordinatesDragged: Point) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 3ddac7e..6da6951 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -19,9 +19,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainAuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
@@ -48,16 +47,9 @@
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
private val authenticationInteractor = utils.authenticationInteractor()
- private val deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = utils.sceneInteractor(),
- )
private val bouncerInteractor =
utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
)
private val bouncerViewModel =
utils.bouncerViewModel(
@@ -96,8 +88,7 @@
assertThat(message?.text).ignoringCase().isEqualTo(ENTER_YOUR_PIN)
assertThat(pin).isEmpty()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- assertThat(underTest.authenticationMethod)
- .isEqualTo(DomainAuthenticationMethodModel.Pin)
+ assertThat(underTest.authenticationMethod).isEqualTo(AuthenticationMethodModel.Pin)
}
@Test
@@ -181,7 +172,8 @@
@Test
fun onAuthenticateButtonClicked_whenCorrect() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val authResult by
+ collectLastValue(authenticationInteractor.authenticationChallengeResult)
lockDeviceAndOpenPinBouncer()
FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
@@ -190,7 +182,7 @@
underTest.onAuthenticateButtonClicked()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ assertThat(authResult).isTrue()
}
@Test
@@ -217,7 +209,8 @@
@Test
fun onAuthenticateButtonClicked_correctAfterWrong() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ val authResult by
+ collectLastValue(authenticationInteractor.authenticationChallengeResult)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
lockDeviceAndOpenPinBouncer()
@@ -230,7 +223,7 @@
underTest.onAuthenticateButtonClicked()
assertThat(message?.text).ignoringCase().isEqualTo(WRONG_PIN)
assertThat(pin).isEmpty()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+ assertThat(authResult).isFalse()
// Enter the correct PIN:
FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
@@ -240,21 +233,22 @@
underTest.onAuthenticateButtonClicked()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ assertThat(authResult).isTrue()
}
@Test
fun onAutoConfirm_whenCorrect() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAutoConfirmEnabled(true)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+ val authResult by
+ collectLastValue(authenticationInteractor.authenticationChallengeResult)
lockDeviceAndOpenPinBouncer()
FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
underTest.onPinButtonClicked(digit)
}
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ assertThat(authResult).isTrue()
}
@Test
@@ -263,7 +257,7 @@
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
- utils.authenticationRepository.setAutoConfirmEnabled(true)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
lockDeviceAndOpenPinBouncer()
FakeAuthenticationRepository.DEFAULT_PIN.dropLast(1).forEach { digit ->
@@ -323,7 +317,7 @@
testScope.runTest {
val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setAutoConfirmEnabled(true)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
assertThat(backspaceButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden)
}
@@ -333,7 +327,7 @@
testScope.runTest {
val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setAutoConfirmEnabled(true)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
underTest.onPinButtonClicked(1)
@@ -355,7 +349,7 @@
testScope.runTest {
val confirmButtonAppearance by collectLastValue(underTest.confirmButtonAppearance)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setAutoConfirmEnabled(true)
+ utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
assertThat(confirmButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index ca8316d..28fae81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -21,7 +21,6 @@
import android.appwidget.AppWidgetProviderInfo
import android.content.BroadcastReceiver
import android.content.ComponentName
-import android.content.pm.PackageManager
import android.os.UserHandle
import android.os.UserManager
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -34,8 +33,6 @@
import com.android.systemui.communal.shared.CommunalWidgetHost
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.res.R
@@ -72,16 +69,12 @@
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
- @Mock private lateinit var packageManager: PackageManager
-
@Mock private lateinit var userManager: UserManager
@Mock private lateinit var userHandle: UserHandle
@Mock private lateinit var userTracker: UserTracker
- @Mock private lateinit var featureFlags: FeatureFlagsClassic
-
@Mock private lateinit var stopwatchProviderInfo: AppWidgetProviderInfo
@Mock private lateinit var providerInfoA: AppWidgetProviderInfo
@@ -113,13 +106,13 @@
communalRepository = FakeCommunalRepository()
communalEnabled(true)
- widgetOnKeyguardEnabled(true)
setAppWidgetIds(emptyList())
overrideResource(R.array.config_communalWidgetAllowlist, fakeAllowlist.toTypedArray())
whenever(stopwatchProviderInfo.loadLabel(any())).thenReturn("Stopwatch")
whenever(userTracker.userHandle).thenReturn(userHandle)
+ whenever(communalWidgetDao.getWidgets()).thenReturn(flowOf(emptyMap()))
}
@Test
@@ -213,7 +206,7 @@
testScope.runTest {
communalEnabled(false)
val repository = initCommunalWidgetRepository()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
verifyBroadcastReceiverNeverRegistered()
}
@@ -222,7 +215,7 @@
testScope.runTest {
userUnlocked(true)
val repository = initCommunalWidgetRepository()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
verifyBroadcastReceiverNeverRegistered()
}
@@ -231,7 +224,7 @@
testScope.runTest {
userUnlocked(false)
val repository = initCommunalWidgetRepository()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
verifyBroadcastReceiverRegistered()
}
@@ -241,7 +234,7 @@
userUnlocked(false)
val repository = initCommunalWidgetRepository()
- val job = launch { repository.stopwatchAppWidgetInfo.collect() }
+ val job = launch { repository.communalWidgets.collect() }
runCurrent()
val receiver = broadcastReceiverUpdate()
@@ -252,53 +245,16 @@
}
@Test
- fun stopwatch_whenUserUnlocks_receiveProviderInfo() =
- testScope.runTest {
- userUnlocked(false)
- val repository = initCommunalWidgetRepository()
- val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo)
- assertThat(lastStopwatchProviderInfo()).isNull()
-
- userUnlocked(true)
- installedProviders(listOf(stopwatchProviderInfo))
- broadcastReceiverUpdate()
-
- assertThat(lastStopwatchProviderInfo()?.providerInfo).isEqualTo(stopwatchProviderInfo)
- }
-
- @Test
- fun stopwatch_userUnlockedButWidgetNotInstalled_noProviderInfo() =
- testScope.runTest {
- userUnlocked(true)
- installedProviders(listOf())
-
- val repository = initCommunalWidgetRepository()
-
- val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo)
- assertThat(lastStopwatchProviderInfo()).isNull()
- }
-
- @Test
- fun appWidgetId_providerInfoAvailable_allocateAppWidgetId() =
- testScope.runTest {
- userUnlocked(true)
- installedProviders(listOf(stopwatchProviderInfo))
- val repository = initCommunalWidgetRepository()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
- verify(appWidgetHost).allocateAppWidgetId()
- }
-
- @Test
fun appWidgetHost_userUnlocked_startListening() =
testScope.runTest {
userUnlocked(false)
val repository = initCommunalWidgetRepository()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
verify(appWidgetHost, Mockito.never()).startListening()
userUnlocked(true)
broadcastReceiverUpdate()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
verify(appWidgetHost).startListening()
}
@@ -308,18 +264,18 @@
testScope.runTest {
userUnlocked(false)
val repository = initCommunalWidgetRepository()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
userUnlocked(true)
broadcastReceiverUpdate()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
verify(appWidgetHost).startListening()
verify(appWidgetHost, Mockito.never()).stopListening()
userUnlocked(false)
broadcastReceiverUpdate()
- collectLastValue(repository.stopwatchAppWidgetInfo)()
+ collectLastValue(repository.communalWidgets)()
verify(appWidgetHost).stopListening()
}
@@ -334,11 +290,9 @@
communalRepository,
communalWidgetHost,
communalWidgetDao,
- packageManager,
userManager,
userTracker,
logBuffer,
- featureFlags,
)
}
@@ -385,10 +339,6 @@
communalRepository.setIsCommunalEnabled(enabled)
}
- private fun widgetOnKeyguardEnabled(enabled: Boolean) {
- whenever(featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)).thenReturn(enabled)
- }
-
private fun userUnlocked(userUnlocked: Boolean) {
whenever(userManager.isUserUnlockingOrUnlocked(userHandle)).thenReturn(userUnlocked)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 08d54c0..af4bf36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -29,7 +29,6 @@
import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
import com.android.systemui.communal.domain.model.CommunalContentModel
-import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.coroutines.collectLastValue
@@ -45,7 +44,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
@@ -53,8 +51,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class CommunalInteractorTest : SysuiTestCase() {
- @Mock private lateinit var stopwatchAppWidgetInfo: CommunalAppWidgetInfo
-
private lateinit var testScope: TestScope
private lateinit var tutorialRepository: FakeCommunalTutorialRepository
@@ -85,18 +81,6 @@
}
@Test
- fun appWidgetInfoFlow() =
- testScope.runTest {
- val lastAppWidgetInfo = collectLastValue(underTest.appWidgetInfo)
- runCurrent()
- assertThat(lastAppWidgetInfo()).isNull()
-
- widgetRepository.setStopwatchAppWidgetInfo(stopwatchAppWidgetInfo)
- runCurrent()
- assertThat(lastAppWidgetInfo()).isEqualTo(stopwatchAppWidgetInfo)
- }
-
- @Test
fun communalEnabled() =
testScope.runTest {
communalRepository.setIsCommunalEnabled(true)
@@ -256,7 +240,7 @@
}
@Test
- fun contentOrdering() =
+ fun ordering_smartspaceBeforeUmoBeforeWidgets() =
testScope.runTest {
tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
index 33a6667..a496292 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
@@ -7,7 +7,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalHubSection
-import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -20,14 +19,13 @@
@SmallTest
class DefaultCommunalBlueprintTest : SysuiTestCase() {
@Mock private lateinit var hubSection: DefaultCommunalHubSection
- @Mock private lateinit var widgetSection: DefaultCommunalWidgetSection
private lateinit var blueprint: DefaultCommunalBlueprint
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- blueprint = DefaultCommunalBlueprint(hubSection, widgetSection)
+ blueprint = DefaultCommunalBlueprint(hubSection)
}
@Test
@@ -35,7 +33,6 @@
val constraintLayout = ConstraintLayout(context, null)
blueprint.replaceViews(null, constraintLayout)
verify(hubSection).addViews(constraintLayout)
- verify(widgetSection).addViews(constraintLayout)
}
@Test
@@ -43,6 +40,5 @@
val cs = ConstraintSet()
blueprint.applyConstraints(cs)
verify(hubSection).applyConstraints(cs)
- verify(widgetSection).applyConstraints(cs)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
index 2c80035..97ac8c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
@@ -83,16 +83,27 @@
}
@Test
- fun isInsecureLockscreenEnabled() =
+ fun isLockscreenEnabled() =
testScope.runTest {
whenever(lockPatternUtils.isLockScreenDisabled(USER_INFOS[0].id)).thenReturn(false)
whenever(lockPatternUtils.isLockScreenDisabled(USER_INFOS[1].id)).thenReturn(true)
userRepository.setSelectedUserInfo(USER_INFOS[0])
- assertThat(underTest.isInsecureLockscreenEnabled()).isTrue()
+ assertThat(underTest.isLockscreenEnabled()).isTrue()
userRepository.setSelectedUserInfo(USER_INFOS[1])
- assertThat(underTest.isInsecureLockscreenEnabled()).isFalse()
+ assertThat(underTest.isLockscreenEnabled()).isFalse()
+ }
+
+ @Test
+ fun reportSuccessfulAuthentication_shouldUpdateIsUnlocked() =
+ testScope.runTest {
+ val isUnlocked by collectLastValue(underTest.isUnlocked)
+ assertThat(isUnlocked).isFalse()
+
+ underTest.reportSuccessfulAuthentication()
+
+ assertThat(isUnlocked).isTrue()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
index aebadc5..abd9f28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
@@ -19,7 +19,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
@@ -60,7 +60,7 @@
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
utils.deviceEntryRepository.apply {
- setInsecureLockscreenEnabled(false)
+ setLockscreenEnabled(false)
// Toggle isUnlocked, twice.
//
@@ -83,8 +83,7 @@
@Test
fun isUnlocked_whenAuthMethodIsNoneAndLockscreenEnabled_isTrue() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ setupSwipeDeviceEntryMethod()
val isUnlocked by collectLastValue(underTest.isUnlocked)
assertThat(isUnlocked).isTrue()
@@ -94,8 +93,7 @@
fun isDeviceEntered_onLockscreenWithSwipe_isFalse() =
testScope.runTest {
val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ setupSwipeDeviceEntryMethod()
switchToScene(SceneKey.Lockscreen)
assertThat(isDeviceEntered).isFalse()
@@ -105,8 +103,7 @@
fun isDeviceEntered_onShadeBeforeDismissingLockscreenWithSwipe_isFalse() =
testScope.runTest {
val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ setupSwipeDeviceEntryMethod()
switchToScene(SceneKey.Lockscreen)
runCurrent()
switchToScene(SceneKey.Shade)
@@ -118,8 +115,7 @@
fun isDeviceEntered_afterDismissingLockscreenWithSwipe_isTrue() =
testScope.runTest {
val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ setupSwipeDeviceEntryMethod()
switchToScene(SceneKey.Lockscreen)
runCurrent()
switchToScene(SceneKey.Gone)
@@ -131,8 +127,7 @@
fun isDeviceEntered_onShadeAfterDismissingLockscreenWithSwipe_isTrue() =
testScope.runTest {
val isDeviceEntered by collectLastValue(underTest.isDeviceEntered)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ setupSwipeDeviceEntryMethod()
switchToScene(SceneKey.Lockscreen)
runCurrent()
switchToScene(SceneKey.Gone)
@@ -148,7 +143,7 @@
utils.authenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pattern
)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ utils.deviceEntryRepository.setLockscreenEnabled(true)
switchToScene(SceneKey.Lockscreen)
runCurrent()
switchToScene(SceneKey.Bouncer)
@@ -160,8 +155,7 @@
@Test
fun canSwipeToEnter_onLockscreenWithSwipe_isTrue() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ setupSwipeDeviceEntryMethod()
switchToScene(SceneKey.Lockscreen)
val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter)
@@ -172,7 +166,7 @@
fun canSwipeToEnter_onLockscreenWithPin_isFalse() =
testScope.runTest {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ utils.deviceEntryRepository.setLockscreenEnabled(true)
switchToScene(SceneKey.Lockscreen)
val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter)
@@ -182,8 +176,7 @@
@Test
fun canSwipeToEnter_afterLockscreenDismissedInSwipeMode_isFalse() =
testScope.runTest {
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ setupSwipeDeviceEntryMethod()
switchToScene(SceneKey.Lockscreen)
runCurrent()
switchToScene(SceneKey.Gone)
@@ -192,6 +185,11 @@
assertThat(canSwipeToEnter).isFalse()
}
+ private fun setupSwipeDeviceEntryMethod() {
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+ utils.deviceEntryRepository.setLockscreenEnabled(true)
+ }
+
@Test
fun canSwipeToEnter_whenTrustedByTrustManager_isTrue() =
testScope.runTest {
@@ -278,12 +276,68 @@
}
@Test
+ fun showOrUnlockDevice_notLocked_switchesToGoneScene() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ switchToScene(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ utils.deviceEntryRepository.setUnlocked(true)
+ runCurrent()
+
+ underTest.attemptDeviceEntry()
+
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ }
+
+ @Test
+ fun showOrUnlockDevice_authMethodNotSecure_switchesToGoneScene() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ switchToScene(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+
+ underTest.attemptDeviceEntry()
+
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ }
+
+ @Test
+ fun showOrUnlockDevice_authMethodSwipe_switchesToGoneScene() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ switchToScene(SceneKey.Lockscreen)
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+
+ utils.deviceEntryRepository.setLockscreenEnabled(true)
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+
+ underTest.attemptDeviceEntry()
+
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
+ }
+
+ @Test
fun isBypassEnabled_disabledInRepository_false() =
testScope.runTest {
utils.deviceEntryRepository.setBypassEnabled(false)
assertThat(underTest.isBypassEnabled.value).isFalse()
}
+ @Test
+ fun successfulAuthenticationChallengeAttempt_updatedIsUnlockedState() =
+ testScope.runTest {
+ val isUnlocked by collectLastValue(underTest.isUnlocked)
+ assertThat(isUnlocked).isFalse()
+
+ utils.authenticationRepository.reportAuthenticationAttempt(true)
+
+ assertThat(isUnlocked).isTrue()
+ }
+
private fun switchToScene(sceneKey: SceneKey) {
sceneInteractor.changeScene(SceneModel(sceneKey), "reason")
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 7de28de..0b3bc9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -19,7 +19,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
@@ -45,7 +45,7 @@
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ utils.deviceEntryRepository.setLockscreenEnabled(true)
utils.deviceEntryRepository.setUnlocked(true)
sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
index b101acf..437a35f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
@@ -38,8 +38,6 @@
import com.android.settingslib.media.PhoneMediaDevice
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.media.controls.MediaTestUtils
import com.android.systemui.media.controls.models.player.MediaData
import com.android.systemui.media.controls.models.player.MediaDeviceData
@@ -112,7 +110,6 @@
private lateinit var session: MediaSession
private lateinit var mediaData: MediaData
@JvmField @Rule val mockito = MockitoJUnit.rule()
- private val featureFlags = FakeFeatureFlagsClassic()
@Before
fun setUp() {
@@ -131,7 +128,6 @@
fakeFgExecutor,
fakeBgExecutor,
dumpster,
- featureFlags,
)
manager.addListener(listener)
@@ -150,7 +146,6 @@
MediaTestUtils.emptyMediaData.copy(packageName = PACKAGE, token = session.sessionToken)
whenever(controllerFactory.create(session.sessionToken)).thenReturn(controller)
setupLeAudioConfiguration(false)
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, false)
}
@After
@@ -463,7 +458,6 @@
@Test
fun mr2ReturnsSystemRouteWithNullName_isPhone_usePhoneName() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
// When the routing session name is null, and is a system session for a PhoneMediaDevice
val phoneDevice = mock(PhoneMediaDevice::class.java)
whenever(phoneDevice.iconWithoutBackground).thenReturn(icon)
@@ -489,7 +483,6 @@
@Test
fun mr2ReturnsSystemRouteWithNullName_useSelectedRouteName() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
// When the routing session does not have a name, and is a system session
whenever(route.name).thenReturn(null)
whenever(mr2.getSelectedRoutes(any())).thenReturn(listOf(selectedRoute))
@@ -725,101 +718,6 @@
assertThat(data.showBroadcastButton).isFalse()
}
- // Duplicates of above tests with MEDIA_DEVICE_NAME_FIX enabled
-
- @Test
- fun loadMediaDataWithNullToken_withNameFix() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
- manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null))
- fakeBgExecutor.runAllReady()
- fakeFgExecutor.runAllReady()
- val data = captureDeviceData(KEY)
- assertThat(data.enabled).isTrue()
- assertThat(data.name).isEqualTo(DEVICE_NAME)
- }
-
- @Test
- fun onAboutToConnectDeviceAdded_findsDeviceInfoFromAddress_withNameFix() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
- manager.onMediaDataLoaded(KEY, null, mediaData)
- // Run and reset the executors and listeners so we only focus on new events.
- fakeBgExecutor.runAllReady()
- fakeFgExecutor.runAllReady()
- reset(listener)
-
- // Ensure we'll get device info when using the address
- val fullMediaDevice = mock(MediaDevice::class.java)
- val address = "fakeAddress"
- val nameFromDevice = "nameFromDevice"
- val iconFromDevice = mock(Drawable::class.java)
- whenever(lmm.getMediaDeviceById(eq(address))).thenReturn(fullMediaDevice)
- whenever(fullMediaDevice.name).thenReturn(nameFromDevice)
- whenever(fullMediaDevice.iconWithoutBackground).thenReturn(iconFromDevice)
-
- // WHEN the about-to-connect device changes to non-null
- val deviceCallback = captureCallback()
- val nameFromParam = "nameFromParam"
- val iconFromParam = mock(Drawable::class.java)
- deviceCallback.onAboutToConnectDeviceAdded(address, nameFromParam, iconFromParam)
- assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
-
- // THEN the about-to-connect device based on the address is returned
- val data = captureDeviceData(KEY)
- assertThat(data.enabled).isTrue()
- assertThat(data.name).isEqualTo(nameFromDevice)
- assertThat(data.name).isNotEqualTo(nameFromParam)
- assertThat(data.icon).isEqualTo(iconFromDevice)
- assertThat(data.icon).isNotEqualTo(iconFromParam)
- }
-
- @Test
- fun deviceNameFromMR2RouteInfo_withNameFix() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
- // GIVEN that MR2Manager returns a valid routing session
- whenever(route.name).thenReturn(REMOTE_DEVICE_NAME)
- // WHEN a notification is added
- manager.onMediaDataLoaded(KEY, null, mediaData)
- fakeBgExecutor.runAllReady()
- fakeFgExecutor.runAllReady()
- // THEN it uses the route name (instead of device name)
- val data = captureDeviceData(KEY)
- assertThat(data.enabled).isTrue()
- assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
- }
-
- @Test
- fun deviceDisabledWhenMR2ReturnsNullRouteInfo_withNameFix() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
- // GIVEN that MR2Manager returns null for routing session
- whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
- // WHEN a notification is added
- manager.onMediaDataLoaded(KEY, null, mediaData)
- fakeBgExecutor.runAllReady()
- fakeFgExecutor.runAllReady()
- // THEN the device is disabled and name is set to null
- val data = captureDeviceData(KEY)
- assertThat(data.enabled).isFalse()
- assertThat(data.name).isNull()
- }
-
- @Test
- fun mr2ReturnsNonSystemRouteWithNullName_useLocalDeviceName_withNameFix() {
- featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
- // GIVEN that MR2Manager returns a routing session that does not have a name
- whenever(route.name).thenReturn(null)
- whenever(route.isSystemSession).thenReturn(false)
- // WHEN a notification is added
- manager.onMediaDataLoaded(KEY, null, mediaData)
- fakeBgExecutor.runAllReady()
- fakeFgExecutor.runAllReady()
- // THEN the device is enabled and uses the current connected device name
- val data = captureDeviceData(KEY)
- assertThat(data.name).isEqualTo(DEVICE_NAME)
- assertThat(data.enabled).isTrue()
- }
-
- // End duplicate tests
-
private fun captureCallback(): LocalMediaManager.DeviceCallback {
val captor = ArgumentCaptor.forClass(LocalMediaManager.DeviceCallback::class.java)
verify(lmm).registerCallback(captor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index 8f27e4e..92c2d74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
@@ -55,12 +55,9 @@
fun setup() {
MockitoAnnotations.initMocks(this)
whenever(logBufferFactory.create(any(), any(), any())).thenReturn(logBuffer)
+ val tileSpec: TileSpec = TileSpec.create("chatty_tile")
underTest =
- QSTileLogger(
- mapOf("chatty_tile" to chattyLogBuffer),
- logBufferFactory,
- statusBarController
- )
+ QSTileLogger(mapOf(tileSpec to chattyLogBuffer), logBufferFactory, statusBarController)
}
@Test
@@ -133,7 +130,7 @@
"sd=null, " +
"svi=None, " +
"enabled=ENABLED, " +
- "a11y=null" +
+ "a11y=android.widget.Switch" +
"], " +
"data=test_data"
)
@@ -157,7 +154,7 @@
"sd=null, " +
"svi=None, " +
"enabled=ENABLED, " +
- "a11y=null], " +
+ "a11y=android.widget.Switch], " +
"data=test_data"
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
index 682b2d0..5eca8ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
@@ -19,7 +19,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -29,8 +31,8 @@
class QSTileConfigProviderTest : SysuiTestCase() {
private val underTest =
- QSTileConfigProviderImpl(
- mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = VALID_SPEC })
+ createQSTileConfigProviderImpl(
+ mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = VALID_SPEC }),
)
@Test
@@ -43,13 +45,31 @@
underTest.getConfig(INVALID_SPEC.spec)
}
+ @Test
+ fun hasConfigReturnsTrueForValidSpec() {
+ assertThat(underTest.hasConfig(VALID_SPEC.spec)).isTrue()
+ }
+
+ @Test
+ fun hasConfigReturnsFalseForInvalidSpec() {
+ assertThat(underTest.hasConfig(INVALID_SPEC.spec)).isFalse()
+ }
+
@Test(expected = IllegalArgumentException::class)
fun validatesSpecUponCreation() {
- QSTileConfigProviderImpl(
+ createQSTileConfigProviderImpl(
mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = INVALID_SPEC })
)
}
+ private fun createQSTileConfigProviderImpl(
+ configs: Map<String, QSTileConfig>
+ ): QSTileConfigProviderImpl =
+ QSTileConfigProviderImpl(
+ configs,
+ mock<QsEventLogger>(),
+ )
+
private companion object {
val VALID_SPEC = TileSpec.create("valid_tile_spec")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index a9f8239..d582b9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -19,7 +19,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
@@ -90,13 +90,8 @@
underTest =
QuickSettingsSceneViewModel(
- bouncerInteractor =
- utils.bouncerInteractor(
- deviceEntryInteractor =
- utils.deviceEntryInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
- ),
+ deviceEntryInteractor =
+ utils.deviceEntryInteractor(
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index e84d274..d1db9c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -26,7 +26,7 @@
import com.android.internal.util.EmergencyAffordanceManager
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
@@ -39,7 +39,6 @@
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
-import com.android.systemui.scene.SceneTestUtils.Companion.toDataLayer
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
@@ -60,6 +59,7 @@
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
+import junit.framework.Assert.assertTrue
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
@@ -72,7 +72,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -135,9 +134,7 @@
private val bouncerInteractor =
utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
)
private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
@@ -234,7 +231,6 @@
ShadeSceneViewModel(
applicationScope = testScope.backgroundScope,
deviceEntryInteractor = deviceEntryInteractor,
- bouncerInteractor = bouncerInteractor,
shadeHeaderViewModel = shadeHeaderViewModel,
)
@@ -247,7 +243,6 @@
applicationScope = testScope.backgroundScope,
sceneInteractor = sceneInteractor,
deviceEntryInteractor = deviceEntryInteractor,
- authenticationInteractor = authenticationInteractor,
keyguardInteractor = keyguardInteractor,
flags = utils.sceneContainerFlags,
sysUiState = sysUiState,
@@ -255,6 +250,7 @@
sceneLogger = mock(),
falsingCollector = utils.falsingCollector(),
powerInteractor = powerInteractor,
+ bouncerInteractor = bouncerInteractor,
)
startable.start()
@@ -298,7 +294,7 @@
@Test
fun swipeUpOnLockscreen_withAuthMethodSwipe_dismissesLockscreen() =
testScope.runTest {
- setAuthMethod(DomainLayerAuthenticationMethodModel.Swipe)
+ setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
val upDestinationSceneKey by
collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
@@ -312,7 +308,7 @@
fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
testScope.runTest {
val upDestinationSceneKey by collectLastValue(shadeSceneViewModel.upDestinationSceneKey)
- setAuthMethod(DomainLayerAuthenticationMethodModel.Swipe)
+ setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
assertCurrentScene(SceneKey.Lockscreen)
// Emulate a user swipe to the shade scene.
@@ -329,7 +325,8 @@
fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenDismissed_goesToGone() =
testScope.runTest {
val upDestinationSceneKey by collectLastValue(shadeSceneViewModel.upDestinationSceneKey)
- setAuthMethod(DomainLayerAuthenticationMethodModel.Swipe)
+ setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+ assertTrue(deviceEntryInteractor.canSwipeToEnter.value)
assertCurrentScene(SceneKey.Lockscreen)
// Emulate a user swipe to dismiss the lockscreen.
@@ -349,7 +346,7 @@
@Test
fun withAuthMethodNone_deviceWakeUp_skipsLockscreen() =
testScope.runTest {
- setAuthMethod(DomainLayerAuthenticationMethodModel.None)
+ setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = false)
putDeviceToSleep(instantlyLockDevice = false)
assertCurrentScene(SceneKey.Lockscreen)
@@ -360,7 +357,7 @@
@Test
fun withAuthMethodSwipe_deviceWakeUp_doesNotSkipLockscreen() =
testScope.runTest {
- setAuthMethod(DomainLayerAuthenticationMethodModel.Swipe)
+ setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
putDeviceToSleep(instantlyLockDevice = false)
assertCurrentScene(SceneKey.Lockscreen)
@@ -428,7 +425,7 @@
@Test
fun dismissingIme_whileOnPasswordBouncer_navigatesToLockscreen() =
testScope.runTest {
- setAuthMethod(DomainLayerAuthenticationMethodModel.Password)
+ setAuthMethod(AuthenticationMethodModel.Password)
val upDestinationSceneKey by
collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer)
@@ -445,7 +442,7 @@
@Test
fun bouncerActionButtonClick_opensEmergencyServicesDialer() =
testScope.runTest {
- setAuthMethod(DomainLayerAuthenticationMethodModel.Password)
+ setAuthMethod(AuthenticationMethodModel.Password)
val upDestinationSceneKey by
collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer)
@@ -464,7 +461,7 @@
@Test
fun bouncerActionButtonClick_duringCall_returnsToCall() =
testScope.runTest {
- setAuthMethod(DomainLayerAuthenticationMethodModel.Password)
+ setAuthMethod(AuthenticationMethodModel.Password)
startPhoneCall()
val upDestinationSceneKey by
collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
@@ -508,19 +505,19 @@
/** Updates the current authentication method and related states in the data layer. */
private fun TestScope.setAuthMethod(
- authMethod: DomainLayerAuthenticationMethodModel,
+ authMethod: AuthenticationMethodModel,
+ enableLockscreen: Boolean = true
) {
+ if (authMethod.isSecure) {
+ assert(enableLockscreen) {
+ "Lockscreen cannot be disabled with a secure authentication method."
+ }
+ }
// Set the lockscreen enabled bit _before_ set the auth method as the code picks up on the
// lockscreen enabled bit _after_ the auth method is changed and the lockscreen enabled bit
// is not an observable that can trigger a new evaluation.
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(
- authMethod !is DomainLayerAuthenticationMethodModel.None
- )
- utils.authenticationRepository.setAuthenticationMethod(authMethod.toDataLayer())
- if (!authMethod.isSecure) {
- // When the auth method is not secure, the device is never considered locked.
- utils.deviceEntryRepository.setUnlocked(true)
- }
+ utils.deviceEntryRepository.setLockscreenEnabled(enableLockscreen)
+ utils.authenticationRepository.setAuthenticationMethod(authMethod)
runCurrent()
}
@@ -648,6 +645,9 @@
emulateUserDrivenTransition(SceneKey.Bouncer)
enterPin()
+ // This repository state is not changed by the AuthInteractor, it relies on
+ // KeyguardStateController.
+ utils.deviceEntryRepository.setUnlocked(true)
emulateUiSceneTransition(
expectedVisible = false,
)
@@ -707,7 +707,7 @@
}
/** Emulates the dismissal of the IME (soft keyboard). */
- private fun TestScope.dismissIme(
+ private suspend fun TestScope.dismissIme(
showImeBeforeDismissing: Boolean = true,
) {
bouncerViewModel.authMethodViewModel.value?.apply {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index f6362fe..2f654e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -24,16 +24,15 @@
import androidx.test.filters.SmallTest
import com.android.systemui.Flags as AconfigFlags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.model.SysUiState
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.SceneTestUtils.Companion.toDataLayer
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -63,8 +62,12 @@
private val sceneInteractor = utils.sceneInteractor()
private val sceneContainerFlags = utils.sceneContainerFlags
private val authenticationInteractor = utils.authenticationInteractor()
+ private val bouncerInteractor =
+ utils.bouncerInteractor(authenticationInteractor = authenticationInteractor)
+ private val faceAuthRepository = FakeDeviceEntryFaceAuthRepository()
private val deviceEntryInteractor =
utils.deviceEntryInteractor(
+ faceAuthRepository = faceAuthRepository,
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
)
@@ -78,7 +81,6 @@
applicationScope = testScope.backgroundScope,
sceneInteractor = sceneInteractor,
deviceEntryInteractor = deviceEntryInteractor,
- authenticationInteractor = authenticationInteractor,
keyguardInteractor = keyguardInteractor,
flags = sceneContainerFlags,
sysUiState = sysUiState,
@@ -86,6 +88,7 @@
sceneLogger = mock(),
falsingCollector = falsingCollector,
powerInteractor = powerInteractor,
+ bouncerInteractor = bouncerInteractor,
)
@Before
@@ -198,12 +201,55 @@
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
+ // Authenticate using a passive auth method like face auth while bypass is disabled.
+ faceAuthRepository.isAuthenticated.value = true
utils.deviceEntryRepository.setUnlocked(true)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
}
@Test
+ fun stayOnCurrentSceneWhenDeviceIsUnlockedAndUserIsNotOnLockscreen() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
+ val transitionStateFlowValue =
+ prepareState(
+ isBypassEnabled = true,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ initialSceneKey = SceneKey.Lockscreen,
+ )
+ underTest.start()
+ runCurrent()
+
+ sceneInteractor.changeScene(SceneModel(SceneKey.Shade), "switch to shade")
+ transitionStateFlowValue.value = ObservableTransitionState.Idle(SceneKey.Shade)
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Shade)
+
+ utils.deviceEntryRepository.setUnlocked(true)
+ runCurrent()
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Shade)
+ }
+
+ @Test
+ fun switchToGoneWhenDeviceIsUnlockedAndUserIsOnBouncerWithBypassDisabled() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
+ prepareState(
+ isBypassEnabled = false,
+ initialSceneKey = SceneKey.Bouncer,
+ )
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
+ underTest.start()
+
+ // Authenticate using a passive auth method like face auth while bypass is disabled.
+ faceAuthRepository.isAuthenticated.value = true
+ utils.deviceEntryRepository.setUnlocked(true)
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
+ }
+
+ @Test
fun switchToLockscreenWhenDeviceSleepsLocked() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
@@ -255,6 +301,7 @@
prepareState(
initialSceneKey = SceneKey.Lockscreen,
authenticationMethod = AuthenticationMethodModel.None,
+ isLockscreenEnabled = false,
)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
@@ -269,7 +316,8 @@
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
prepareState(
initialSceneKey = SceneKey.Lockscreen,
- authenticationMethod = AuthenticationMethodModel.Swipe,
+ authenticationMethod = AuthenticationMethodModel.None,
+ isLockscreenEnabled = true,
)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
@@ -406,6 +454,24 @@
}
@Test
+ fun bouncerImeHidden_shouldTransitionBackToLockscreen() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
+ prepareState(
+ initialSceneKey = SceneKey.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Password,
+ isDeviceUnlocked = false,
+ )
+ underTest.start()
+ runCurrent()
+
+ bouncerInteractor.onImeHidden()
+ runCurrent()
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ }
+
+ @Test
fun collectFalsingSignals_screenOnAndOff_aodUnavailable() =
testScope.runTest {
utils.keyguardRepository.setAodAvailable(false)
@@ -526,8 +592,14 @@
isBypassEnabled: Boolean = false,
initialSceneKey: SceneKey? = null,
authenticationMethod: AuthenticationMethodModel? = null,
+ isLockscreenEnabled: Boolean = true,
startsAwake: Boolean = true,
): MutableStateFlow<ObservableTransitionState> {
+ if (authenticationMethod?.isSecure == true) {
+ assert(isLockscreenEnabled) {
+ "Lockscreen cannot be disabled while having a secure authentication method"
+ }
+ }
sceneContainerFlags.enabled = true
utils.deviceEntryRepository.setUnlocked(isDeviceUnlocked)
utils.deviceEntryRepository.setBypassEnabled(isBypassEnabled)
@@ -542,11 +614,9 @@
sceneInteractor.onSceneChanged(SceneModel(it), "reason")
}
authenticationMethod?.let {
- utils.authenticationRepository.setAuthenticationMethod(
- authenticationMethod.toDataLayer()
- )
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(
- authenticationMethod != AuthenticationMethodModel.None
+ utils.authenticationRepository.setAuthenticationMethod(authenticationMethod)
+ utils.deviceEntryRepository.setLockscreenEnabled(
+ isLockscreenEnabled = isLockscreenEnabled
)
}
if (startsAwake) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index bc28ccb..f4c05e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -376,6 +376,7 @@
mFeatureFlags.set(Flags.MIGRATE_NSSL, false);
mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
mFeatureFlags.set(Flags.ONE_WAY_HAPTICS_API_MIGRATION, false);
+ mFeatureFlags.set(Flags.MIGRATE_CLOCKS_TO_BLUEPRINT, false);
mMainDispatcher = getMainDispatcher();
KeyguardInteractorFactory.WithDependencies keyguardInteractorDeps =
KeyguardInteractorFactory.create();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index e920687..20b19fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -158,6 +158,15 @@
}
@Test
+ fun updateLegacyLockscreenShadeTracking() =
+ testScope.runTest {
+ assertThat(underTest.legacyLockscreenShadeTracking.value).isEqualTo(false)
+
+ underTest.setLegacyLockscreenShadeTracking(true)
+ assertThat(underTest.legacyLockscreenShadeTracking.value).isEqualTo(true)
+ }
+
+ @Test
fun updateLegacyQsTracking() =
testScope.runTest {
assertThat(underTest.legacyQsTracking.value).isEqualTo(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index 589f9ae..fa849fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -19,7 +19,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
@@ -96,12 +96,6 @@
ShadeSceneViewModel(
applicationScope = testScope.backgroundScope,
deviceEntryInteractor = deviceEntryInteractor,
- bouncerInteractor =
- utils.bouncerInteractor(
- deviceEntryInteractor = deviceEntryInteractor,
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
- ),
shadeHeaderViewModel = shadeHeaderViewModel,
)
}
@@ -130,7 +124,7 @@
fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ utils.deviceEntryRepository.setLockscreenEnabled(true)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
@@ -142,7 +136,7 @@
fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
testScope.runTest {
val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
- utils.deviceEntryRepository.setInsecureLockscreenEnabled(true)
+ utils.deviceEntryRepository.setLockscreenEnabled(true)
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
index b8fe2f9..cb83e7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
@@ -20,10 +20,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.smartspace.config.BcSmartspaceConfigProvider
-import com.android.systemui.util.mockito.whenever
-import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
@@ -45,16 +42,7 @@
}
@Test
- fun isDefaultDateWeatherDisabled_flagIsTrue_returnsTrue() {
- whenever(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
-
+ fun isDefaultDateWeatherDisabled_returnsTrue() {
assertTrue(configProvider.isDefaultDateWeatherDisabled)
}
-
- @Test
- fun isDefaultDateWeatherDisabled_flagIsFalse_returnsFalse() {
- whenever(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
-
- assertFalse(configProvider.isDefaultDateWeatherDisabled)
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index a1425f8..ae32142 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -323,6 +323,20 @@
}
@Test
+ public void testCurrentUserPrivateNotificationsNullChannel() {
+ // GIVEN current user allows private notifications to show
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+
+ mCurrentUserNotif.setRanking(new RankingBuilder(mCurrentUserNotif.getRanking())
+ .setChannel(null)
+ .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
+ // THEN the notification is not redacted
+ assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ }
+
+ @Test
public void testWorkPrivateNotificationsRedacted() {
// GIVEN work profile doesn't private notifications to show
mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 9036f22..8440e00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -38,7 +38,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.BcSmartspaceConfigPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin
@@ -205,10 +204,6 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- // Todo(b/261760571): flip the flag value here when feature is launched, and update relevant
- // tests.
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
-
`when`(secureSettings.getUriFor(PRIVATE_LOCKSCREEN_SETTING))
.thenReturn(fakePrivateLockscreenSettingUri)
`when`(secureSettings.getUriFor(NOTIF_ON_LOCKSCREEN_SETTING))
@@ -260,17 +255,6 @@
deviceProvisionedListener = deviceProvisionedCaptor.value
}
- @Test(expected = RuntimeException::class)
- fun testBuildAndConnectWeatherView_throwsIfDecouplingDisabled() {
- // GIVEN the feature flag is disabled
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
-
- // WHEN we try to build the view
- controller.buildAndConnectWeatherView(fakeParent)
-
- // THEN an exception is thrown
- }
-
@Test
fun testBuildAndConnectView_connectsOnlyAfterDeviceIsProvisioned() {
// GIVEN an unprovisioned device and an attempt to connect
@@ -332,6 +316,8 @@
clearInvocations(plugin)
// WHEN the session is closed
+ controller.stateChangeListener.onViewDetachedFromWindow(dateSmartspaceView as View)
+ controller.stateChangeListener.onViewDetachedFromWindow(weatherSmartspaceView as View)
controller.stateChangeListener.onViewDetachedFromWindow(smartspaceView as View)
controller.disconnect()
@@ -376,20 +362,6 @@
configChangeListener.onThemeChanged()
// We update the new text color to match the wallpaper color
- verify(smartspaceView).setPrimaryTextColor(anyInt())
- }
-
- @Test
- fun testThemeChange_ifDecouplingEnabled_updatesTextColor() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
-
- // GIVEN a connected smartspace session
- connectSession()
-
- // WHEN the theme changes
- configChangeListener.onThemeChanged()
-
- // We update the new text color to match the wallpaper color
verify(dateSmartspaceView).setPrimaryTextColor(anyInt())
verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
verify(smartspaceView).setPrimaryTextColor(anyInt())
@@ -404,20 +376,6 @@
statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f)
// We pass that along to the view
- verify(smartspaceView).setDozeAmount(0.7f)
- }
-
- @Test
- fun testDozeAmountChange_ifDecouplingEnabled_updatesViews() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
-
- // GIVEN a connected smartspace session
- connectSession()
-
- // WHEN the doze amount changes
- statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f)
-
- // We pass that along to the view
verify(dateSmartspaceView).setDozeAmount(0.7f)
verify(weatherSmartspaceView).setDozeAmount(0.7f)
verify(smartspaceView).setDozeAmount(0.7f)
@@ -472,7 +430,7 @@
}
@Test
- fun testAllTargetsAreFilteredExceptWeatherWhenNotificationsAreDisabled() {
+ fun testAllTargetsAreFilteredInclWeatherWhenNotificationsAreDisabled() {
// GIVEN the active user doesn't allow any notifications on lockscreen
setShowNotifications(userHandlePrimary, false)
connectSession()
@@ -488,7 +446,7 @@
sessionListener.onTargetsAvailable(targets)
// THEN all non-sensitive content is still shown
- verify(plugin).onTargetsAvailable(eq(listOf(targets[3])))
+ verify(plugin).onTargetsAvailable(emptyList())
}
@Test
@@ -519,8 +477,7 @@
}
@Test
- fun testSessionListener_ifDecouplingEnabled_weatherTargetIsFilteredOut() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
+ fun testSessionListener_weatherTargetIsFilteredOut() {
connectSession()
// WHEN we receive a list of targets
@@ -670,8 +627,7 @@
}
@Test
- fun testSessionListener_ifDecouplingEnabled_weatherDataUpdates() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
+ fun testSessionListener_weatherDataUpdates() {
connectSession()
clock.setCurrentTimeMillis(SMARTSPACE_TIME_JUST_RIGHT)
@@ -699,33 +655,6 @@
}
@Test
- fun testSessionListener_ifDecouplingDisabled_weatherDataUpdates() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(false)
- connectSession()
-
- clock.setCurrentTimeMillis(SMARTSPACE_TIME_JUST_RIGHT)
- // WHEN we receive a list of targets
- val targets = listOf(
- makeWeatherTargetWithExtras(
- id = 1,
- userHandle = userHandlePrimary,
- description = "Sunny",
- state = WeatherData.WeatherStateIcon.SUNNY.id,
- temperature = "32",
- useCelsius = false),
- makeTarget(2, userHandlePrimary, isSensitive = true)
- )
-
- sessionListener.onTargetsAvailable(targets)
-
- verify(keyguardUpdateMonitor).sendWeatherData(argThat { w ->
- w.description == "Sunny" &&
- w.state == WeatherData.WeatherStateIcon.SUNNY &&
- w.temperature == 32 && !w.useCelsius
- })
- }
-
- @Test
fun testSettingsAreReloaded() {
// GIVEN a connected session where the privacy settings later flip to false
connectSession()
@@ -781,6 +710,8 @@
connectSession()
// WHEN we are told to cleanup
+ controller.stateChangeListener.onViewDetachedFromWindow(dateSmartspaceView as View)
+ controller.stateChangeListener.onViewDetachedFromWindow(weatherSmartspaceView as View)
controller.stateChangeListener.onViewDetachedFromWindow(smartspaceView as View)
controller.disconnect()
@@ -816,16 +747,6 @@
}
@Test
- fun testWeatherViewUsesSameSession() {
- `when`(featureFlags.isEnabled(Flags.SMARTSPACE_DATE_WEATHER_DECOUPLED)).thenReturn(true)
- // GIVEN a connected session
- connectSession()
-
- // No checks is needed here, since connectSession() already checks internally that
- // createSmartspaceSession is invoked only once.
- }
-
- @Test
fun testViewGetInitializedWithBypassEnabledState() {
// GIVEN keyguard bypass is enabled.
`when`(keyguardBypassController.bypassEnabled).thenReturn(true)
@@ -853,31 +774,29 @@
}
private fun connectSession() {
- if (controller.isDateWeatherDecoupled()) {
- val dateView = controller.buildAndConnectDateView(fakeParent)
- dateSmartspaceView = dateView as SmartspaceView
- fakeParent.addView(dateView)
- controller.stateChangeListener.onViewAttachedToWindow(dateView)
+ val dateView = controller.buildAndConnectDateView(fakeParent)
+ dateSmartspaceView = dateView as SmartspaceView
+ fakeParent.addView(dateView)
+ controller.stateChangeListener.onViewAttachedToWindow(dateView)
- verify(dateSmartspaceView).setUiSurface(
- BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
- verify(dateSmartspaceView).registerDataProvider(datePlugin)
+ verify(dateSmartspaceView).setUiSurface(
+ BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+ verify(dateSmartspaceView).registerDataProvider(datePlugin)
- verify(dateSmartspaceView).setPrimaryTextColor(anyInt())
- verify(dateSmartspaceView).setDozeAmount(0.5f)
+ verify(dateSmartspaceView).setPrimaryTextColor(anyInt())
+ verify(dateSmartspaceView).setDozeAmount(0.5f)
- val weatherView = controller.buildAndConnectWeatherView(fakeParent)
- weatherSmartspaceView = weatherView as SmartspaceView
- fakeParent.addView(weatherView)
- controller.stateChangeListener.onViewAttachedToWindow(weatherView)
+ val weatherView = controller.buildAndConnectWeatherView(fakeParent)
+ weatherSmartspaceView = weatherView as SmartspaceView
+ fakeParent.addView(weatherView)
+ controller.stateChangeListener.onViewAttachedToWindow(weatherView)
- verify(weatherSmartspaceView).setUiSurface(
- BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
- verify(weatherSmartspaceView).registerDataProvider(weatherPlugin)
+ verify(weatherSmartspaceView).setUiSurface(
+ BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+ verify(weatherSmartspaceView).registerDataProvider(weatherPlugin)
- verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
- verify(weatherSmartspaceView).setDozeAmount(0.5f)
- }
+ verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
+ verify(weatherSmartspaceView).setDozeAmount(0.5f)
val view = controller.buildAndConnectView(fakeParent)
smartspaceView = view as SmartspaceView
@@ -918,10 +837,8 @@
verify(smartspaceView).setPrimaryTextColor(anyInt())
verify(smartspaceView).setDozeAmount(0.5f)
- if (controller.isDateWeatherDecoupled()) {
- clearInvocations(dateSmartspaceView)
- clearInvocations(weatherSmartspaceView)
- }
+ clearInvocations(dateSmartspaceView)
+ clearInvocations(weatherSmartspaceView)
clearInvocations(smartspaceView)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
index 80d941a..722b170 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
@@ -32,9 +32,11 @@
VisualInterruptionDecisionProviderImpl(
ambientDisplayConfiguration,
batteryController,
+ deviceProvisionedController,
globalSettings,
headsUpManager,
keyguardNotificationVisibilityProvider,
+ keyguardStateController,
logger,
mainHandler,
powerManager,
@@ -50,6 +52,7 @@
assertPeekNotSuppressed()
assertPulseNotSuppressed()
assertBubbleNotSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -59,6 +62,7 @@
assertPeekNotSuppressed()
assertPulseNotSuppressed()
assertBubbleNotSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -68,6 +72,7 @@
assertPeekSuppressed()
assertPulseNotSuppressed()
assertBubbleNotSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -77,6 +82,7 @@
assertPeekSuppressed()
assertPulseNotSuppressed()
assertBubbleNotSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -86,6 +92,7 @@
assertPeekNotSuppressed()
assertPulseSuppressed()
assertBubbleNotSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -95,6 +102,7 @@
assertPeekNotSuppressed()
assertPulseSuppressed()
assertBubbleNotSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -104,6 +112,7 @@
assertPeekNotSuppressed()
assertPulseNotSuppressed()
assertBubbleSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -113,6 +122,7 @@
assertPeekNotSuppressed()
assertPulseNotSuppressed()
assertBubbleSuppressed()
+ assertFsiNotSuppressed()
}
}
@@ -193,6 +203,10 @@
assertShouldBubble(buildBubbleEntry())
}
+ private fun assertFsiNotSuppressed() {
+ forEachFsiState { assertShouldFsi(buildFsiEntry()) }
+ }
+
private fun withCondition(condition: VisualInterruptionCondition, block: () -> Unit) {
provider.addCondition(condition)
block()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
index 7f12b22..0f29836 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -20,6 +20,7 @@
import android.app.Notification
import android.app.Notification.BubbleMetadata
import android.app.Notification.FLAG_BUBBLE
+import android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED
import android.app.Notification.GROUP_ALERT_ALL
import android.app.Notification.GROUP_ALERT_CHILDREN
import android.app.Notification.GROUP_ALERT_SUMMARY
@@ -29,6 +30,7 @@
import android.app.NotificationManager.IMPORTANCE_HIGH
import android.app.NotificationManager.IMPORTANCE_LOW
import android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT
+import android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
import android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK
import android.app.NotificationManager.VISIBILITY_NO_OVERRIDE
import android.app.PendingIntent
@@ -56,18 +58,19 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.MAX_HUN_WHEN_AGE_MS
-import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.FakeDeviceProvisionedController
import com.android.systemui.statusbar.policy.HeadsUpManager
-import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.settings.FakeGlobalSettings
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.utils.leaks.FakeBatteryController
+import com.android.systemui.utils.leaks.FakeKeyguardStateController
import com.android.systemui.utils.leaks.LeakCheckedTest
import com.android.systemui.utils.os.FakeHandler
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.`when` as whenever
@@ -77,13 +80,13 @@
protected val ambientDisplayConfiguration = FakeAmbientDisplayConfiguration(context)
protected val batteryController = FakeBatteryController(leakCheck)
- protected val deviceProvisionedController: DeviceProvisionedController = mock()
+ protected val deviceProvisionedController = FakeDeviceProvisionedController()
protected val flags: NotifPipelineFlags = mock()
protected val globalSettings = FakeGlobalSettings()
protected val headsUpManager: HeadsUpManager = mock()
protected val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider =
mock()
- protected val keyguardStateController: KeyguardStateController = mock()
+ protected val keyguardStateController = FakeKeyguardStateController(leakCheck)
protected val logger: NotificationInterruptLogger = mock()
protected val mainHandler = FakeHandler(Looper.getMainLooper())
protected val powerManager: PowerManager = mock()
@@ -137,15 +140,18 @@
}
@Test
- fun testShouldNotPeek_packageSnoozed() {
+ fun testShouldNotPeek_packageSnoozed_withoutFsi() {
ensurePeekState { hunSnoozed = true }
assertShouldNotHeadsUp(buildPeekEntry())
}
@Test
- fun testShouldPeek_packageSnoozedButFsi() {
- ensurePeekState { hunSnoozed = true }
- assertShouldHeadsUp(buildFsiEntry())
+ fun testShouldPeek_packageSnoozed_withFsi() {
+ val entry = buildFsiEntry()
+ forEachPeekableFsiState {
+ ensurePeekState { hunSnoozed = true }
+ assertShouldHeadsUp(entry)
+ }
}
@Test
@@ -217,29 +223,31 @@
@Test
fun testShouldPeek_defaultLegacySuppressor() {
ensurePeekState()
- provider.addLegacySuppressor(neverSuppresses)
- assertShouldHeadsUp(buildPeekEntry())
+ withLegacySuppressor(neverSuppresses) { assertShouldHeadsUp(buildPeekEntry()) }
}
@Test
fun testShouldNotPeek_legacySuppressInterruptions() {
ensurePeekState()
- provider.addLegacySuppressor(alwaysSuppressesInterruptions)
- assertShouldNotHeadsUp(buildPeekEntry())
+ withLegacySuppressor(alwaysSuppressesInterruptions) {
+ assertShouldNotHeadsUp(buildPeekEntry())
+ }
}
@Test
fun testShouldNotPeek_legacySuppressAwakeInterruptions() {
ensurePeekState()
- provider.addLegacySuppressor(alwaysSuppressesAwakeInterruptions)
- assertShouldNotHeadsUp(buildPeekEntry())
+ withLegacySuppressor(alwaysSuppressesAwakeInterruptions) {
+ assertShouldNotHeadsUp(buildPeekEntry())
+ }
}
@Test
fun testShouldNotPeek_legacySuppressAwakeHeadsUp() {
ensurePeekState()
- provider.addLegacySuppressor(alwaysSuppressesAwakeHeadsUp)
- assertShouldNotHeadsUp(buildPeekEntry())
+ withLegacySuppressor(alwaysSuppressesAwakeHeadsUp) {
+ assertShouldNotHeadsUp(buildPeekEntry())
+ }
}
@Test
@@ -251,29 +259,31 @@
@Test
fun testShouldPulse_defaultLegacySuppressor() {
ensurePulseState()
- provider.addLegacySuppressor(neverSuppresses)
- assertShouldHeadsUp(buildPulseEntry())
+ withLegacySuppressor(neverSuppresses) { assertShouldHeadsUp(buildPulseEntry()) }
}
@Test
fun testShouldNotPulse_legacySuppressInterruptions() {
ensurePulseState()
- provider.addLegacySuppressor(alwaysSuppressesInterruptions)
- assertShouldNotHeadsUp(buildPulseEntry())
+ withLegacySuppressor(alwaysSuppressesInterruptions) {
+ assertShouldNotHeadsUp(buildPulseEntry())
+ }
}
@Test
fun testShouldPulse_legacySuppressAwakeInterruptions() {
ensurePulseState()
- provider.addLegacySuppressor(alwaysSuppressesAwakeInterruptions)
- assertShouldHeadsUp(buildPulseEntry())
+ withLegacySuppressor(alwaysSuppressesAwakeInterruptions) {
+ assertShouldHeadsUp(buildPulseEntry())
+ }
}
@Test
fun testShouldPulse_legacySuppressAwakeHeadsUp() {
ensurePulseState()
- provider.addLegacySuppressor(alwaysSuppressesAwakeHeadsUp)
- assertShouldHeadsUp(buildPulseEntry())
+ withLegacySuppressor(alwaysSuppressesAwakeHeadsUp) {
+ assertShouldHeadsUp(buildPulseEntry())
+ }
}
@Test
@@ -439,29 +449,31 @@
@Test
fun testShouldBubble_defaultLegacySuppressor() {
ensureBubbleState()
- provider.addLegacySuppressor(neverSuppresses)
- assertShouldBubble(buildBubbleEntry())
+ withLegacySuppressor(neverSuppresses) { assertShouldBubble(buildBubbleEntry()) }
}
@Test
fun testShouldNotBubble_legacySuppressInterruptions() {
ensureBubbleState()
- provider.addLegacySuppressor(alwaysSuppressesInterruptions)
- assertShouldNotBubble(buildBubbleEntry())
+ withLegacySuppressor(alwaysSuppressesInterruptions) {
+ assertShouldNotBubble(buildBubbleEntry())
+ }
}
@Test
fun testShouldNotBubble_legacySuppressAwakeInterruptions() {
ensureBubbleState()
- provider.addLegacySuppressor(alwaysSuppressesAwakeInterruptions)
- assertShouldNotBubble(buildBubbleEntry())
+ withLegacySuppressor(alwaysSuppressesAwakeInterruptions) {
+ assertShouldNotBubble(buildBubbleEntry())
+ }
}
@Test
fun testShouldBubble_legacySuppressAwakeHeadsUp() {
ensureBubbleState()
- provider.addLegacySuppressor(alwaysSuppressesAwakeHeadsUp)
- assertShouldBubble(buildBubbleEntry())
+ withLegacySuppressor(alwaysSuppressesAwakeHeadsUp) {
+ assertShouldBubble(buildBubbleEntry())
+ }
}
@Test
@@ -477,6 +489,107 @@
}
@Test
+ fun testShouldNotFsi_noFullScreenIntent() {
+ forEachFsiState { assertShouldNotFsi(buildFsiEntry { hasFsi = false }) }
+ }
+
+ @Test
+ fun testShouldNotFsi_showStickyHun() {
+ forEachFsiState {
+ assertShouldNotFsi(
+ buildFsiEntry {
+ hasFsi = false
+ isStickyAndNotDemoted = true
+ }
+ )
+ }
+ }
+
+ @Test
+ fun testShouldNotFsi_onlyDnd() {
+ forEachFsiState {
+ assertShouldNotFsi(
+ buildFsiEntry { suppressedVisualEffects = SUPPRESSED_EFFECT_FULL_SCREEN_INTENT },
+ expectWouldInterruptWithoutDnd = true
+ )
+ }
+ }
+
+ @Test
+ fun testShouldNotFsi_notImportantEnough() {
+ forEachFsiState { assertShouldNotFsi(buildFsiEntry { importance = IMPORTANCE_DEFAULT }) }
+ }
+
+ @Test
+ fun testShouldNotFsi_notOnlyDnd() {
+ forEachFsiState {
+ assertShouldNotFsi(
+ buildFsiEntry {
+ suppressedVisualEffects = SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
+ importance = IMPORTANCE_DEFAULT
+ },
+ expectWouldInterruptWithoutDnd = false
+ )
+ }
+ }
+
+ @Test
+ fun testShouldNotFsi_suppressiveGroupAlertBehavior() {
+ forEachFsiState {
+ assertShouldNotFsi(
+ buildFsiEntry {
+ isGrouped = true
+ isGroupSummary = true
+ groupAlertBehavior = GROUP_ALERT_CHILDREN
+ }
+ )
+ }
+ }
+
+ @Test
+ fun testShouldFsi_suppressiveGroupAlertBehavior_notGrouped() {
+ forEachFsiState {
+ assertShouldFsi(
+ buildFsiEntry {
+ isGrouped = false
+ isGroupSummary = true
+ groupAlertBehavior = GROUP_ALERT_CHILDREN
+ }
+ )
+ }
+ }
+
+ @Test
+ fun testShouldFsi_suppressiveGroupAlertBehavior_notSuppressive() {
+ forEachFsiState {
+ assertShouldFsi(
+ buildFsiEntry {
+ isGrouped = true
+ isGroupSummary = true
+ groupAlertBehavior = GROUP_ALERT_ALL
+ }
+ )
+ }
+ }
+
+ @Test
+ fun testShouldNotFsi_suppressiveBubbleMetadata() {
+ forEachFsiState {
+ assertShouldNotFsi(
+ buildFsiEntry {
+ hasBubbleMetadata = true
+ bubbleSuppressesNotification = true
+ }
+ )
+ }
+ }
+
+ @Test
+ fun testShouldNotFsi_packageSuspended() {
+ forEachFsiState { assertShouldNotFsi(buildFsiEntry { packageSuspended = true }) }
+ }
+
+ @Test
fun testShouldFsi_notInteractive() {
ensureNotInteractiveFsiState()
assertShouldFsi(buildFsiEntry())
@@ -494,6 +607,76 @@
assertShouldFsi(buildFsiEntry())
}
+ @Test
+ fun testShouldNotFsi_expectedToHun() {
+ forEachPeekableFsiState {
+ ensurePeekState()
+ assertShouldNotFsi(buildFsiEntry())
+ }
+ }
+
+ @Test
+ fun testShouldNotFsi_expectedToHun_hunSnoozed() {
+ forEachPeekableFsiState {
+ ensurePeekState { hunSnoozed = true }
+ assertShouldNotFsi(buildFsiEntry())
+ }
+ }
+
+ @Test
+ fun testShouldFsi_lockedShade() {
+ ensureLockedShadeFsiState()
+ assertShouldFsi(buildFsiEntry())
+ }
+
+ @Test
+ fun testShouldFsi_keyguardOccluded() {
+ ensureKeyguardOccludedFsiState()
+ assertShouldFsi(buildFsiEntry())
+ }
+
+ @Test
+ fun testShouldFsi_deviceNotProvisioned() {
+ ensureDeviceNotProvisionedFsiState()
+ assertShouldFsi(buildFsiEntry())
+ }
+
+ @Test
+ fun testShouldNotFsi_noHunOrKeyguard() {
+ ensureNoHunOrKeyguardFsiState()
+ assertShouldNotFsi(buildFsiEntry())
+ }
+
+ @Test
+ fun testShouldFsi_defaultLegacySuppressor() {
+ forEachFsiState {
+ withLegacySuppressor(neverSuppresses) { assertShouldFsi(buildFsiEntry()) }
+ }
+ }
+
+ @Test
+ fun testShouldFsi_suppressInterruptions() {
+ forEachFsiState {
+ withLegacySuppressor(alwaysSuppressesInterruptions) { assertShouldFsi(buildFsiEntry()) }
+ }
+ }
+
+ @Test
+ fun testShouldFsi_suppressAwakeInterruptions() {
+ forEachFsiState {
+ withLegacySuppressor(alwaysSuppressesAwakeInterruptions) {
+ assertShouldFsi(buildFsiEntry())
+ }
+ }
+ }
+
+ @Test
+ fun testShouldFsi_suppressAwakeHeadsUp() {
+ forEachFsiState {
+ withLegacySuppressor(alwaysSuppressesAwakeHeadsUp) { assertShouldFsi(buildFsiEntry()) }
+ }
+ }
+
protected data class State(
var hunSettingEnabled: Boolean? = null,
var hunSnoozed: Boolean? = null,
@@ -505,6 +688,9 @@
var keyguardShouldHideNotification: Boolean? = null,
var pulseOnNotificationsEnabled: Boolean? = null,
var statusBarState: Int? = null,
+ var keyguardIsShowing: Boolean = false,
+ var keyguardIsOccluded: Boolean = false,
+ var deviceProvisioned: Boolean = true
)
protected fun setState(state: State): Unit =
@@ -536,6 +722,11 @@
}
statusBarState?.let { statusBarStateController.state = it }
+
+ keyguardStateController.isOccluded = keyguardIsOccluded
+ keyguardStateController.isShowing = keyguardIsShowing
+
+ deviceProvisionedController.deviceProvisioned = deviceProvisioned
}
protected fun ensureState(block: State.() -> Unit) =
@@ -565,26 +756,104 @@
protected fun ensureBubbleState(block: State.() -> Unit = {}) = ensureState(block)
protected fun ensureNotInteractiveFsiState(block: State.() -> Unit = {}) = ensureState {
- isDreaming = false
isInteractive = false
- statusBarState = SHADE
run(block)
}
protected fun ensureDreamingFsiState(block: State.() -> Unit = {}) = ensureState {
- isDreaming = true
isInteractive = true
- statusBarState = SHADE
+ isDreaming = true
run(block)
}
protected fun ensureKeyguardFsiState(block: State.() -> Unit = {}) = ensureState {
- isDreaming = false
isInteractive = true
+ isDreaming = false
statusBarState = KEYGUARD
run(block)
}
+ protected fun ensureLockedShadeFsiState(block: State.() -> Unit = {}) = ensureState {
+ // It is assumed *but not checked in the code* that statusBarState is SHADE_LOCKED.
+ isInteractive = true
+ isDreaming = false
+ statusBarState = SHADE
+ hunSettingEnabled = false
+ keyguardIsShowing = true
+ keyguardIsOccluded = false
+ run(block)
+ }
+
+ protected fun ensureKeyguardOccludedFsiState(block: State.() -> Unit = {}) = ensureState {
+ isInteractive = true
+ isDreaming = false
+ statusBarState = SHADE
+ hunSettingEnabled = false
+ keyguardIsShowing = true
+ keyguardIsOccluded = true
+ run(block)
+ }
+
+ protected fun ensureDeviceNotProvisionedFsiState(block: State.() -> Unit = {}) = ensureState {
+ isInteractive = true
+ isDreaming = false
+ statusBarState = SHADE
+ hunSettingEnabled = false
+ keyguardIsShowing = false
+ deviceProvisioned = false
+ run(block)
+ }
+
+ protected fun ensureNoHunOrKeyguardFsiState(block: State.() -> Unit = {}) = ensureState {
+ isInteractive = true
+ isDreaming = false
+ statusBarState = SHADE
+ hunSettingEnabled = false
+ keyguardIsShowing = false
+ deviceProvisioned = true
+ run(block)
+ }
+
+ protected fun forEachFsiState(block: () -> Unit) {
+ ensureNotInteractiveFsiState()
+ block()
+
+ ensureDreamingFsiState()
+ block()
+
+ ensureKeyguardFsiState()
+ block()
+
+ ensureLockedShadeFsiState()
+ block()
+
+ ensureKeyguardOccludedFsiState()
+ block()
+
+ ensureDeviceNotProvisionedFsiState()
+ block()
+ }
+
+ private fun forEachPeekableFsiState(extendState: State.() -> Unit = {}, block: () -> Unit) {
+ ensureLockedShadeFsiState(extendState)
+ block()
+
+ ensureKeyguardOccludedFsiState(extendState)
+ block()
+
+ ensureDeviceNotProvisionedFsiState(extendState)
+ block()
+ }
+
+ protected fun withLegacySuppressor(
+ suppressor: NotificationInterruptSuppressor,
+ block: () -> Unit
+ ) {
+ provider.addLegacySuppressor(suppressor)
+ block()
+ provider.removeLegacySuppressor(suppressor)
+ }
+
protected fun assertShouldHeadsUp(entry: NotificationEntry) =
provider.makeUnloggedHeadsUpDecision(entry).let {
assertTrue("unexpected suppressed HUN: ${it.logReason}", it.shouldInterrupt)
@@ -610,9 +879,19 @@
assertTrue("unexpected suppressed FSI: ${it.logReason}", it.shouldInterrupt)
}
- protected fun assertShouldNotFsi(entry: NotificationEntry) =
+ protected fun assertShouldNotFsi(
+ entry: NotificationEntry,
+ expectWouldInterruptWithoutDnd: Boolean? = null
+ ) =
provider.makeUnloggedFullScreenIntentDecision(entry).let {
assertFalse("unexpected unsuppressed FSI: ${it.logReason}", it.shouldInterrupt)
+ if (expectWouldInterruptWithoutDnd != null) {
+ assertEquals(
+ "unexpected unsuppressed-without-DND FSI: ${it.logReason}",
+ expectWouldInterruptWithoutDnd,
+ it.wouldInterruptWithoutDnd
+ )
+ }
}
protected class EntryBuilder(val context: Context) {
@@ -630,6 +909,8 @@
var isGroupSummary: Boolean? = null
var groupAlertBehavior: Int? = null
var hasJustLaunchedFsi = false
+ var isStickyAndNotDemoted = false
+ var packageSuspended: Boolean? = null
private fun buildBubbleMetadata(): BubbleMetadata {
val builder =
@@ -681,6 +962,10 @@
if (isBubble) {
flags = flags or FLAG_BUBBLE
}
+
+ if (isStickyAndNotDemoted) {
+ flags = flags or FLAG_FSI_REQUESTED_BUT_DENIED
+ }
}
.let { NotificationEntryBuilder().setNotification(it) }
.apply {
@@ -699,10 +984,15 @@
it.notifyFullScreenIntentLaunched()
}
+ if (isStickyAndNotDemoted) {
+ assertFalse(it.isDemoted)
+ }
+
modifyRanking(it)
.apply {
suppressedVisualEffects?.let { setSuppressedVisualEffects(it) }
visibilityOverride?.let { setVisibilityOverride(it) }
+ packageSuspended?.let { setSuspended(it) }
}
.build()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 6203531..e91d6d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -83,6 +83,7 @@
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -906,6 +907,20 @@
assertEquals(bottomImeInset, mStackScrollerInternal.mBottomInset);
}
+ @Test
+ public void testSetMaxDisplayedNotifications_notifiesListeners() {
+ ExpandableView.OnHeightChangedListener listener =
+ mock(ExpandableView.OnHeightChangedListener.class);
+ Runnable runnable = mock(Runnable.class);
+ mStackScroller.setOnHeightChangedListener(listener);
+ mStackScroller.setOnHeightChangedRunnable(runnable);
+
+ mStackScroller.setMaxDisplayedNotifications(50);
+
+ verify(listener).onHeightChanged(mNotificationShelf, false);
+ verify(runnable).run();
+ }
+
private void setBarStateForTest(int state) {
// Can't inject this through the listener or we end up on the actual implementation
// rather than the mock because the spy just coppied the anonymous inner /shruggie.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 22553df..db8f217 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -40,13 +40,8 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.res.R
import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.user.domain.UserDomainLayerModule
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
@@ -84,25 +79,13 @@
}
}
- private val notificationStackSizeCalculator: NotificationStackSizeCalculator = mock()
- private val notificationStackScrollLayoutController: NotificationStackScrollLayoutController =
- mock {
- whenever(view).thenReturn(mock())
- whenever(shelfHeight).thenReturn(0)
- }
-
private val testComponent: TestComponent =
DaggerSharedNotificationContainerViewModelTest_TestComponent.factory()
.create(
test = this,
featureFlags =
FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) },
- mocks =
- TestMocksModule(
- notificationStackSizeCalculator = notificationStackSizeCalculator,
- notificationStackScrollLayoutController =
- notificationStackScrollLayoutController,
- )
+ mocks = TestMocksModule(),
)
@Test
@@ -336,17 +319,9 @@
@Test
fun maxNotificationsOnLockscreen() =
testComponent.runTest {
- whenever(
- notificationStackSizeCalculator.computeMaxKeyguardNotifications(
- any(),
- any(),
- any(),
- any()
- )
- )
- .thenReturn(10)
-
- val maxNotifications by collectLastValue(underTest.maxNotifications)
+ var notificationCount = 10
+ val maxNotifications by
+ collectLastValue(underTest.getMaxNotifications { notificationCount })
showLockscreen()
@@ -356,21 +331,52 @@
SharedNotificationContainerPosition(top = 1f, bottom = 2f)
assertThat(maxNotifications).isEqualTo(10)
+
+ // Also updates when directly requested (as it would from NotificationStackScrollLayout)
+ notificationCount = 25
+ sharedNotificationContainerInteractor.notificationStackChanged()
+ assertThat(maxNotifications).isEqualTo(25)
+ }
+
+ @Test
+ fun maxNotificationsOnLockscreen_DoesNotUpdateWhenUserInteracting() =
+ testComponent.runTest {
+ var notificationCount = 10
+ val maxNotifications by
+ collectLastValue(underTest.getMaxNotifications { notificationCount })
+
+ showLockscreen()
+
+ overrideResource(R.bool.config_use_split_notification_shade, false)
+ configurationRepository.onAnyConfigurationChange()
+ keyguardInteractor.sharedNotificationContainerPosition.value =
+ SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+
+ assertThat(maxNotifications).isEqualTo(10)
+
+ // Shade expanding... still 10
+ shadeRepository.setLockscreenShadeExpansion(0.5f)
+ assertThat(maxNotifications).isEqualTo(10)
+
+ notificationCount = 25
+
+ // When shade is expanding by user interaction
+ shadeRepository.setLegacyLockscreenShadeTracking(true)
+
+ // Should still be 10, since the user is interacting
+ assertThat(maxNotifications).isEqualTo(10)
+
+ shadeRepository.setLegacyLockscreenShadeTracking(false)
+ shadeRepository.setLockscreenShadeExpansion(0f)
+
+ // Stopped tracking, show 25
+ assertThat(maxNotifications).isEqualTo(25)
}
@Test
fun maxNotificationsOnShade() =
testComponent.runTest {
- whenever(
- notificationStackSizeCalculator.computeMaxKeyguardNotifications(
- any(),
- any(),
- any(),
- any()
- )
- )
- .thenReturn(10)
- val maxNotifications by collectLastValue(underTest.maxNotifications)
+ val maxNotifications by collectLastValue(underTest.getMaxNotifications { 10 })
// Show lockscreen with shade expanded
showLockscreenWithShadeExpanded()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
index ddfe79a..6e9363b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
@@ -20,16 +20,16 @@
import com.android.internal.widget.LockPatternView
import com.android.internal.widget.LockscreenCredential
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.authentication.shared.model.AuthenticationResultModel
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import dagger.Binds
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -37,12 +37,13 @@
import kotlinx.coroutines.test.currentTime
class FakeAuthenticationRepository(
- private val deviceEntryRepository: FakeDeviceEntryRepository,
private val currentTime: () -> Long,
) : AuthenticationRepository {
- private val _isAutoConfirmEnabled = MutableStateFlow(false)
- override val isAutoConfirmEnabled: StateFlow<Boolean> = _isAutoConfirmEnabled.asStateFlow()
+ private val _isAutoConfirmFeatureEnabled = MutableStateFlow(false)
+ override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
+ _isAutoConfirmFeatureEnabled.asStateFlow()
+ override val authenticationChallengeResult = MutableSharedFlow<Boolean>()
override val hintedPinLength: Int = HINTING_PIN_LENGTH
@@ -79,7 +80,7 @@
override suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) {
failedAttemptCount = if (isSuccessful) 0 else failedAttemptCount + 1
- deviceEntryRepository.setUnlocked(isSuccessful)
+ authenticationChallengeResult.emit(isSuccessful)
}
override suspend fun getPinLength(): Int {
@@ -98,8 +99,8 @@
_throttling.value = throttlingModel
}
- fun setAutoConfirmEnabled(isEnabled: Boolean) {
- _isAutoConfirmEnabled.value = isEnabled
+ fun setAutoConfirmFeatureEnabled(isEnabled: Boolean) {
+ _isAutoConfirmFeatureEnabled.value = isEnabled
}
override suspend fun setThrottleDuration(durationMs: Int) {
@@ -215,9 +216,8 @@
@Provides
@SysUISingleton
fun provideFake(
- deviceEntryRepository: FakeDeviceEntryRepository,
scope: TestScope,
- ) = FakeAuthenticationRepository(deviceEntryRepository, currentTime = { scope.currentTime })
+ ) = FakeAuthenticationRepository(currentTime = { scope.currentTime })
@Module
interface Bindings {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index 8a2ff50..c6f12e2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -1,22 +1,14 @@
package com.android.systemui.communal.data.repository
-import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
/** Fake implementation of [CommunalWidgetRepository] */
class FakeCommunalWidgetRepository : CommunalWidgetRepository {
- private val _stopwatchAppWidgetInfo = MutableStateFlow<CommunalAppWidgetInfo?>(null)
- override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> = _stopwatchAppWidgetInfo
-
private val _communalWidgets = MutableStateFlow<List<CommunalWidgetContentModel>>(emptyList())
override val communalWidgets: Flow<List<CommunalWidgetContentModel>> = _communalWidgets
- fun setStopwatchAppWidgetInfo(appWidgetInfo: CommunalAppWidgetInfo) {
- _stopwatchAppWidgetInfo.value = appWidgetInfo
- }
-
fun setCommunalWidgets(inventory: List<CommunalWidgetContentModel>) {
_communalWidgets.value = inventory
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
index f029348..ba70d46 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
@@ -27,7 +27,7 @@
@SysUISingleton
class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository {
- private var isInsecureLockscreenEnabled = true
+ private var isLockscreenEnabled = true
private val _isBypassEnabled = MutableStateFlow(false)
override val isBypassEnabled: StateFlow<Boolean> = _isBypassEnabled
@@ -35,16 +35,20 @@
private val _isUnlocked = MutableStateFlow(false)
override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow()
- override suspend fun isInsecureLockscreenEnabled(): Boolean {
- return isInsecureLockscreenEnabled
+ override suspend fun isLockscreenEnabled(): Boolean {
+ return isLockscreenEnabled
+ }
+
+ override fun reportSuccessfulAuthentication() {
+ _isUnlocked.value = true
}
fun setUnlocked(isUnlocked: Boolean) {
_isUnlocked.value = isUnlocked
}
- fun setInsecureLockscreenEnabled(isLockscreenEnabled: Boolean) {
- this.isInsecureLockscreenEnabled = isLockscreenEnabled
+ fun setLockscreenEnabled(isLockscreenEnabled: Boolean) {
+ this.isLockscreenEnabled = isLockscreenEnabled
}
fun setBypassEnabled(isBypassEnabled: Boolean) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
index de72a7d..d231d63 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
@@ -24,6 +24,8 @@
override fun getConfig(tileSpec: String): QSTileConfig = configs.getValue(tileSpec)
+ override fun hasConfig(tileSpec: String): Boolean = configs.containsKey(tileSpec)
+
fun putConfig(tileSpec: TileSpec, config: QSTileConfig) {
configs[tileSpec.spec] = config
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 36ec18f..72cc08f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -26,11 +26,9 @@
import com.android.internal.logging.MetricsLogger
import com.android.internal.util.EmergencyAffordanceManager
import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
import com.android.systemui.authentication.data.repository.AuthenticationRepository
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
import com.android.systemui.bouncer.data.repository.BouncerRepository
import com.android.systemui.bouncer.data.repository.EmergencyServicesRepository
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
@@ -110,7 +108,6 @@
val deviceEntryRepository: FakeDeviceEntryRepository by lazy { FakeDeviceEntryRepository() }
val authenticationRepository: FakeAuthenticationRepository by lazy {
FakeAuthenticationRepository(
- deviceEntryRepository = deviceEntryRepository,
currentTime = { testScope.currentTime },
)
}
@@ -181,6 +178,7 @@
sceneInteractor = sceneInteractor,
deviceEntryFaceAuthRepository = faceAuthRepository,
trustRepository = trustRepository,
+ flags = FakeSceneContainerFlags(enabled = true)
)
}
@@ -192,7 +190,6 @@
repository = repository,
backgroundDispatcher = testDispatcher,
userRepository = userRepository,
- deviceEntryRepository = deviceEntryRepository,
clock = mock { whenever(elapsedRealtime()).thenAnswer { testScope.currentTime } }
)
}
@@ -221,17 +218,13 @@
}
fun bouncerInteractor(
- deviceEntryInteractor: DeviceEntryInteractor,
authenticationInteractor: AuthenticationInteractor,
- sceneInteractor: SceneInteractor,
): BouncerInteractor {
return BouncerInteractor(
applicationScope = applicationScope(),
applicationContext = context,
repository = BouncerRepository(featureFlags),
- deviceEntryInteractor = deviceEntryInteractor,
authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
flags = sceneContainerFlags,
falsingInteractor = falsingInteractor(),
)
@@ -345,19 +338,4 @@
dozeLogger = dozeLogger,
)
}
-
- companion object {
- fun DomainLayerAuthenticationMethodModel.toDataLayer(): DataLayerAuthenticationMethodModel {
- return when (this) {
- DomainLayerAuthenticationMethodModel.None -> DataLayerAuthenticationMethodModel.None
- DomainLayerAuthenticationMethodModel.Swipe ->
- DataLayerAuthenticationMethodModel.None
- DomainLayerAuthenticationMethodModel.Pin -> DataLayerAuthenticationMethodModel.Pin
- DomainLayerAuthenticationMethodModel.Password ->
- DataLayerAuthenticationMethodModel.Password
- DomainLayerAuthenticationMethodModel.Pattern ->
- DataLayerAuthenticationMethodModel.Pattern
- }
- }
- }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 800593f..02318ab 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -59,6 +59,8 @@
private val _legacyIsQsExpanded = MutableStateFlow(false)
@Deprecated("Use ShadeInteractor instead") override val legacyIsQsExpanded = _legacyIsQsExpanded
+ override val legacyLockscreenShadeTracking = MutableStateFlow(false)
+
@Deprecated("Use ShadeInteractor instead")
override fun setLegacyIsQsExpanded(legacyIsQsExpanded: Boolean) {
_legacyIsQsExpanded.value = legacyIsQsExpanded
@@ -81,6 +83,11 @@
_legacyShadeTracking.value = tracking
}
+ @Deprecated("Should only be called by NPVC and tests")
+ override fun setLegacyLockscreenShadeTracking(tracking: Boolean) {
+ legacyLockscreenShadeTracking.value = tracking
+ }
+
fun setShadeModel(model: ShadeModel) {
_shadeModel.value = model
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt
new file mode 100644
index 0000000..0c2b115
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt
@@ -0,0 +1,31 @@
+package com.android.systemui.statusbar.policy
+
+class FakeDeviceProvisionedController : DeviceProvisionedController {
+ @JvmField var deviceProvisioned = true
+
+ override fun addCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
+ TODO("Not yet implemented")
+ }
+
+ override fun removeCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
+ TODO("Not yet implemented")
+ }
+
+ override fun isDeviceProvisioned() = deviceProvisioned
+
+ override fun getCurrentUser(): Int {
+ TODO("Not yet implemented")
+ }
+
+ override fun isUserSetup(user: Int): Boolean {
+ TODO("Not yet implemented")
+ }
+
+ override fun isCurrentUserSetup(): Boolean {
+ TODO("Not yet implemented")
+ }
+
+ override fun isFrpActive(): Boolean {
+ TODO("Not yet implemented")
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
index bdf1aff..d452810 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
@@ -24,6 +24,9 @@
private final BaseLeakChecker<Callback> mCallbackController;
+ private boolean mIsShowing = false;
+ private boolean mIsOccluded = false;
+
public FakeKeyguardStateController(LeakCheck test) {
mCallbackController = new BaseLeakChecker<Callback>(test, "keyguard");
}
@@ -45,7 +48,11 @@
@Override
public boolean isShowing() {
- return false;
+ return mIsShowing;
+ }
+
+ public void setShowing(boolean showing) {
+ mIsShowing = showing;
}
@Override
@@ -60,7 +67,11 @@
@Override
public boolean isOccluded() {
- return false;
+ return mIsOccluded;
+ }
+
+ public void setOccluded(boolean occluded) {
+ mIsOccluded = occluded;
}
@Override
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index ec12d21..fc4ed1d 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -28,9 +28,8 @@
name: "ravenwood-junit",
srcs: ["junit-src/**/*.java"],
libs: [
+ "framework-minus-apex.ravenwood",
"junit",
],
- sdk_version: "core_current",
- host_supported: true,
visibility: ["//visibility:public"],
}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java
similarity index 96%
rename from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java
rename to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java
index 76964a7..7dc197e 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodClassLoadHook.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.TYPE;
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java
similarity index 96%
rename from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java
rename to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java
index ddf65dc..1d31579 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeep.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepWholeClass.java
similarity index 93%
rename from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java
rename to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepWholeClass.java
index d7ef7f5..d2c77c1 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodKeepWholeClass.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
@@ -35,5 +35,5 @@
*/
@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
@Retention(RetentionPolicy.CLASS)
-public @interface RavenwoodWholeClassKeep {
+public @interface RavenwoodKeepWholeClass {
}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java
similarity index 96%
rename from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
rename to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java
index 8cdc1ff..4b9cf85 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodNativeSubstitutionClass.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.TYPE;
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodRemove.java
similarity index 96%
rename from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java
rename to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodRemove.java
index 759c918..6727327 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodRemove.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodReplace.java
similarity index 83%
copy from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
copy to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodReplace.java
index 8cdc1ff..a920f63 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodReplace.java
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
-import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.ElementType.METHOD;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -29,8 +29,7 @@
*
* @hide
*/
-@Target({TYPE})
+@Target({METHOD})
@Retention(RetentionPolicy.CLASS)
-public @interface RavenwoodNativeSubstitutionClass {
- String value();
+public @interface RavenwoodReplace {
}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java
similarity index 96%
rename from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java
rename to ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java
index de3dd04..a234a9b 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package android.ravenwood.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java
deleted file mode 100644
index 5a0a8f4..0000000
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.ravenwood.annotations;
-
-import static java.lang.annotation.ElementType.METHOD;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
- * QUESTIONS ABOUT IT.
- *
- * TODO: Javadoc
- *
- * @hide
- */
-@Target({METHOD})
-@Retention(RetentionPolicy.CLASS)
-public @interface RavenwoodSubstitute {
- // TODO We should add "_host" as default. We're not doing it yet, because extractign the default
- // value with ASM doesn't seem trivial. (? not sure.)
- String suffix();
-}
diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt
index 48c0a2d..692d598 100644
--- a/ravenwood/framework-minus-apex-ravenwood-policies.txt
+++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt
@@ -76,21 +76,13 @@
class android.util.UtilConfig stubclass
# Internals
-class com.android.internal.util.ArrayUtils stubclass
- method newUnpaddedByteArray (I)[B @newUnpaddedByteArray$ravenwood
- method newUnpaddedCharArray (I)[C @newUnpaddedCharArray$ravenwood
- method newUnpaddedIntArray (I)[I @newUnpaddedIntArray$ravenwood
- method newUnpaddedBooleanArray (I)[Z @newUnpaddedBooleanArray$ravenwood
- method newUnpaddedLongArray (I)[J @newUnpaddedLongArray$ravenwood
- method newUnpaddedFloatArray (I)[F @newUnpaddedFloatArray$ravenwood
- method newUnpaddedObjectArray (I)[Ljava/lang/Object; @newUnpaddedObjectArray$ravenwood
- method newUnpaddedArray (Ljava/lang/Class;I)[Ljava/lang/Object; @newUnpaddedArray$ravenwood
-
class com.android.internal.util.GrowingArrayUtils stubclass
class com.android.internal.util.LineBreakBufferedWriter stubclass
class com.android.internal.util.Preconditions stubclass
class com.android.internal.util.StringPool stubclass
+class com.android.internal.os.SomeArgs stubclass
+
# Parcel
class android.os.Parcel stubclass
method writeException (Ljava/lang/Exception;)V @writeException$ravenwood
@@ -102,14 +94,16 @@
class android.os.BadParcelableException stubclass
class android.os.BadTypeParcelableException stubclass
-# Binder: just enough to construct, no further functionality
-class android.os.Binder stub
- method <init> ()V stub
- method <init> (Ljava/lang/String;)V stub
- method isDirectlyHandlingTransaction ()Z stub
- method isDirectlyHandlingTransactionNative ()Z @isDirectlyHandlingTransactionNative$ravenwood
- method getNativeBBinderHolder ()J @getNativeBBinderHolder$ravenwood
+# Binder
+class android.os.DeadObjectException stubclass
+class android.os.DeadSystemException stubclass
+class android.os.RemoteException stubclass
+class android.os.TransactionTooLargeException stubclass
# Containers
class android.os.BaseBundle stubclass
class android.os.Bundle stubclass
+
+# Misc
+class android.os.PatternMatcher stubclass
+class android.os.ParcelUuid stubclass
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index a6b3f66..bffd0cd 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -16,6 +16,7 @@
package android.platform.test.ravenwood;
+import android.os.Process;
import android.platform.test.annotations.IgnoreUnderRavenwood;
import org.junit.Assume;
@@ -23,6 +24,8 @@
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
* THIS RULE IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
* QUESTIONS ABOUT IT.
@@ -30,20 +33,84 @@
* @hide
*/
public class RavenwoodRule implements TestRule {
+ private static AtomicInteger sNextPid = new AtomicInteger(100);
+
+ /**
+ * Unless the test author requests differently, run as "nobody", and give each collection of
+ * tests its own unique PID.
+ */
+ private int mUid = android.os.Process.NOBODY_UID;
+ private int mPid = sNextPid.getAndIncrement();
+
+ public RavenwoodRule() {
+ }
+
+ public static class Builder {
+ private RavenwoodRule mRule = new RavenwoodRule();
+
+ public Builder() {
+ }
+
+ /**
+ * Configure the identity of this process to be the system UID for the duration of the
+ * test. Has no effect under non-Ravenwood environments.
+ */
+ public Builder setProcessSystem() {
+ mRule.mUid = android.os.Process.SYSTEM_UID;
+ return this;
+ }
+
+ /**
+ * Configure the identity of this process to be an app UID for the duration of the
+ * test. Has no effect under non-Ravenwood environments.
+ */
+ public Builder setProcessApp() {
+ mRule.mUid = android.os.Process.FIRST_APPLICATION_UID;
+ return this;
+ }
+
+ public RavenwoodRule build() {
+ return mRule;
+ }
+ }
+
+ /**
+ * Return if the current process is running under a Ravenwood test environment.
+ */
public boolean isUnderRavenwood() {
// TODO: give ourselves a better environment signal
return System.getProperty("java.class.path").contains("ravenwood");
}
+ private void init() {
+ android.os.Process.init$ravenwood(mUid, mPid);
+ android.os.Binder.init$ravenwood();
+ }
+
+ private void reset() {
+ android.os.Process.reset$ravenwood();
+ android.os.Binder.reset$ravenwood();
+ }
+
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
+ final boolean isUnderRavenwood = isUnderRavenwood();
if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) {
- Assume.assumeFalse(isUnderRavenwood());
+ Assume.assumeFalse(isUnderRavenwood);
}
- base.evaluate();
+ if (isUnderRavenwood) {
+ init();
+ }
+ try {
+ base.evaluate();
+ } finally {
+ if (isUnderRavenwood) {
+ reset();
+ }
+ }
}
};
}
diff --git a/ravenwood/mockito/Android.bp b/ravenwood/mockito/Android.bp
new file mode 100644
index 0000000..6dbff4c
--- /dev/null
+++ b/ravenwood/mockito/Android.bp
@@ -0,0 +1,38 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// Ravenwood tests run on the hostside, so we need mockit of the host variant.
+// But we need to use it in modules of the android variant, so we "wash" the variant with it.
+java_host_for_device {
+ name: "mockito_ravenwood",
+ libs: [
+ "mockito",
+ "objenesis",
+ ],
+}
+
+android_ravenwood_test {
+ name: "RavenwoodMockitoTest",
+
+ srcs: [
+ "test/**/*.java",
+ ],
+ static_libs: [
+ "junit",
+ "truth",
+
+ "mockito_ravenwood",
+ ],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+ auto_gen_config: true,
+}
diff --git a/ravenwood/mockito/test/com/android/ravenwood/mockito/MockitoTest.java b/ravenwood/mockito/test/com/android/ravenwood/mockito/MockitoTest.java
new file mode 100644
index 0000000..b175ae7
--- /dev/null
+++ b/ravenwood/mockito/test/com/android/ravenwood/mockito/MockitoTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.ravenwood.mockito;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+
+import org.junit.Test;
+
+public class MockitoTest {
+ @Test
+ public void testMockJdkClass() {
+ Process object = mock(Process.class);
+
+ when(object.exitValue()).thenReturn(42);
+
+ assertThat(object.exitValue()).isEqualTo(42);
+ }
+
+ /* It still doesn't work...
+STACKTRACE:
+org.mockito.exceptions.base.MockitoException:
+Mockito cannot mock this class: class android.content.Intent.
+
+Mockito can only mock non-private & non-final classes.
+If you're not sure why you're getting this error, please report to the mailing list.
+
+
+... But Intent public, non-final.
+
+ */
+ // @Test
+ private void testMockAndroidClass1() {
+ Intent object = mock(Intent.class);
+
+ when(object.getAction()).thenReturn("ACTION_RAVENWOOD");
+
+ assertThat(object.getAction()).isEqualTo("ACTION_RAVENWOOD");
+ }
+
+ @Test
+ public void testMockAndroidClass2() {
+ Context object = mock(Context.class);
+
+ when(object.getPackageName()).thenReturn("android");
+
+ assertThat(object.getPackageName()).isEqualTo("android");
+ }
+}
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index 0811f90..776a19a 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -1,2 +1,9 @@
# Only classes listed here can use the Ravenwood annotations.
+com.android.internal.util.ArrayUtils
+
+android.os.Binder
+android.os.Binder$IdentitySupplier
+android.os.IBinder
+android.os.Process
+android.os.SystemClock
diff --git a/ravenwood/ravenwood-standard-options.txt b/ravenwood/ravenwood-standard-options.txt
index 6e1384f..4b07ef6 100644
--- a/ravenwood/ravenwood-standard-options.txt
+++ b/ravenwood/ravenwood-standard-options.txt
@@ -16,22 +16,22 @@
# Standard annotations.
# Note, each line is a single argument, so we need newlines after each `--xxx-annotation`.
--keep-annotation
- android.ravenwood.annotations.RavenwoodKeep
+ android.ravenwood.annotation.RavenwoodKeep
--keep-class-annotation
- android.ravenwood.annotations.RavenwoodWholeClassKeep
+ android.ravenwood.annotation.RavenwoodKeepWholeClass
--throw-annotation
- android.ravenwood.annotations.RavenwoodThrow
+ android.ravenwood.annotation.RavenwoodThrow
--remove-annotation
- android.ravenwood.annotations.RavenwoodRemove
+ android.ravenwood.annotation.RavenwoodRemove
--substitute-annotation
- android.ravenwood.annotations.RavenwoodSubstitute
+ android.ravenwood.annotation.RavenwoodReplace
--native-substitute-annotation
- android.ravenwood.annotations.RavenwoodNativeSubstitutionClass
+ android.ravenwood.annotation.RavenwoodNativeSubstitutionClass
--class-load-hook-annotation
- android.ravenwood.annotations.RavenwoodClassLoadHook
+ android.ravenwood.annotation.RavenwoodClassLoadHook
diff --git a/services/companion/java/com/android/server/companion/virtual/OWNERS b/services/companion/java/com/android/server/companion/virtual/OWNERS
index 83143a4..5295ec8 100644
--- a/services/companion/java/com/android/server/companion/virtual/OWNERS
+++ b/services/companion/java/com/android/server/companion/virtual/OWNERS
@@ -1,3 +1,5 @@
+# Bug component: 1171888
+
set noparent
ogunwale@google.com
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 92af68b..85b3c9a 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -441,10 +441,8 @@
+ " is not the owner of the supplied VirtualDevice");
}
- int displayId = virtualDeviceImpl.createVirtualDisplay(virtualDisplayConfig, callback,
- packageName);
- mLocalService.onVirtualDisplayCreated(displayId);
- return displayId;
+ return virtualDeviceImpl.createVirtualDisplay(
+ virtualDisplayConfig, callback, packageName);
}
@Override // Binder call
@@ -625,9 +623,6 @@
private final class LocalService extends VirtualDeviceManagerInternal {
@GuardedBy("mVirtualDeviceManagerLock")
- private final ArrayList<VirtualDisplayListener>
- mVirtualDisplayListeners = new ArrayList<>();
- @GuardedBy("mVirtualDeviceManagerLock")
private final ArrayList<AppsOnVirtualDeviceListener>
mAppsOnVirtualDeviceListeners = new ArrayList<>();
@GuardedBy("mVirtualDeviceManagerLock")
@@ -665,35 +660,15 @@
}
@Override
- public void onVirtualDisplayCreated(int displayId) {
- final VirtualDisplayListener[] listeners;
- synchronized (mVirtualDeviceManagerLock) {
- listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
- }
- mHandler.post(() -> {
- for (VirtualDisplayListener listener : listeners) {
- listener.onVirtualDisplayCreated(displayId);
- }
- });
- }
-
- @Override
public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) {
- final VirtualDisplayListener[] listeners;
VirtualDeviceImpl virtualDeviceImpl;
synchronized (mVirtualDeviceManagerLock) {
- listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
virtualDeviceImpl = mVirtualDevices.get(
((VirtualDeviceImpl) virtualDevice).getDeviceId());
}
if (virtualDeviceImpl != null) {
virtualDeviceImpl.onVirtualDisplayRemoved(displayId);
}
- mHandler.post(() -> {
- for (VirtualDisplayListener listener : listeners) {
- listener.onVirtualDisplayRemoved(displayId);
- }
- });
}
@Override
@@ -799,22 +774,6 @@
}
@Override
- public void registerVirtualDisplayListener(
- @NonNull VirtualDisplayListener listener) {
- synchronized (mVirtualDeviceManagerLock) {
- mVirtualDisplayListeners.add(listener);
- }
- }
-
- @Override
- public void unregisterVirtualDisplayListener(
- @NonNull VirtualDisplayListener listener) {
- synchronized (mVirtualDeviceManagerLock) {
- mVirtualDisplayListeners.remove(listener);
- }
- }
-
- @Override
public void registerAppsOnVirtualDeviceListener(
@NonNull AppsOnVirtualDeviceListener listener) {
synchronized (mVirtualDeviceManagerLock) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 15fc2dc..f6835fe 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3651,7 +3651,26 @@
Watchdog.getInstance().pauseWatchingMonitorsFor(
SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#close might be slow");
if (mMounted) {
- mVold.unmountAppFuse(uid, mountId);
+ BackgroundThread.getHandler().post(() -> {
+ try {
+ // We need to run the unmount on a separate thread to
+ // prevent a possible deadlock, where:
+ // 1. AppFuseThread (this thread) tries to call into vold
+ // 2. the vold lock is held by another thread, which called:
+ // mVold.openAppFuseFile()
+ // as part of that call, vold calls open() on the
+ // underlying file, which is a call that needs to be
+ // handled by the AppFuseThread, which is stuck waiting
+ // for the vold lock (see 1.)
+ // It is safe to do the unmount asynchronously, because the mount
+ // path we use is never reused during the current boot cycle;
+ // see mNextAppFuseName. Also,we have anyway stopped serving
+ // requests at this point.
+ mVold.unmountAppFuse(uid, mountId);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ });
mMounted = false;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b99a98f..f92af67 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -599,6 +599,9 @@
private static final String INTENT_REMOTE_BUGREPORT_FINISHED =
"com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED";
+ public static final String DATA_FILE_PATH_HEADER = "Data File: ";
+ public static final String DATA_FILE_PATH_FOOTER = "End Data File\n";
+
// If set, we will push process association information in to procstats.
static final boolean TRACK_PROCSTATS_ASSOCIATIONS = true;
@@ -9595,17 +9598,33 @@
: Settings.Global.getInt(mContext.getContentResolver(), logcatSetting, 0);
int dropboxMaxSize = Settings.Global.getInt(
mContext.getContentResolver(), maxBytesSetting, DROPBOX_DEFAULT_MAX_SIZE);
- int maxDataFileSize = dropboxMaxSize - sb.length()
- - lines * RESERVED_BYTES_PER_LOGCAT_LINE;
- if (dataFile != null && maxDataFileSize > 0) {
- try {
- sb.append(FileUtils.readTextFile(dataFile, maxDataFileSize,
- "\n\n[[TRUNCATED]]"));
- } catch (IOException e) {
- Slog.e(TAG, "Error reading " + dataFile, e);
+ if (dataFile != null) {
+ // Attach the stack traces file to the report so collectors can load them
+ // by file if they have access.
+ sb.append(DATA_FILE_PATH_HEADER)
+ .append(dataFile.getAbsolutePath()).append('\n');
+
+ int maxDataFileSize = dropboxMaxSize
+ - sb.length()
+ - lines * RESERVED_BYTES_PER_LOGCAT_LINE
+ - DATA_FILE_PATH_FOOTER.length();
+
+ if (maxDataFileSize > 0) {
+ // Inline dataFile contents if there is room.
+ try {
+ sb.append(FileUtils.readTextFile(dataFile, maxDataFileSize,
+ "\n\n[[TRUNCATED]]\n"));
+ } catch (IOException e) {
+ Slog.e(TAG, "Error reading " + dataFile, e);
+ }
}
+
+ // Always append the footer, even there wasn't enough space to inline the
+ // dataFile contents.
+ sb.append(DATA_FILE_PATH_FOOTER);
}
+
if (crashInfo != null && crashInfo.stackTrace != null) {
sb.append(crashInfo.stackTrace);
}
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index e41b6ae..3ce92bc 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -265,13 +265,6 @@
@Nullable
public BroadcastRecord enqueueOrReplaceBroadcast(@NonNull BroadcastRecord record,
int recordIndex, @NonNull BroadcastConsumer deferredStatesApplyConsumer) {
- // When updateDeferredStates() has already applied a deferred state to
- // all pending items, apply to this new broadcast too
- if (mLastDeferredStates && record.deferUntilActive
- && (record.getDeliveryState(recordIndex) == BroadcastRecord.DELIVERY_PENDING)) {
- deferredStatesApplyConsumer.accept(record, recordIndex);
- }
-
// Ignore FLAG_RECEIVER_REPLACE_PENDING if the sender specified the policy using the
// BroadcastOptions delivery group APIs.
if (record.isReplacePending()
@@ -294,6 +287,13 @@
// with implicit responsiveness expectations.
getQueueForBroadcast(record).addLast(newBroadcastArgs);
onBroadcastEnqueued(record, recordIndex);
+
+ // When updateDeferredStates() has already applied a deferred state to
+ // all pending items, apply to this new broadcast too
+ if (mLastDeferredStates && shouldBeDeferred()
+ && (record.getDeliveryState(recordIndex) == BroadcastRecord.DELIVERY_PENDING)) {
+ deferredStatesApplyConsumer.accept(record, recordIndex);
+ }
return null;
}
@@ -1235,32 +1235,45 @@
}
/**
- * Update {@link BroadcastRecord.DELIVERY_DEFERRED} states of all our
+ * Update {@link BroadcastRecord#DELIVERY_DEFERRED} states of all our
* pending broadcasts, when needed.
*/
void updateDeferredStates(@NonNull BroadcastConsumer applyConsumer,
@NonNull BroadcastConsumer clearConsumer) {
// When all we have pending is deferred broadcasts, and we're cached,
// then we want everything to be marked deferred
- final boolean wantDeferredStates = (mCountDeferred > 0)
- && (mCountDeferred == mCountEnqueued) && mProcessFreezable;
+ final boolean wantDeferredStates = shouldBeDeferred();
if (mLastDeferredStates != wantDeferredStates) {
mLastDeferredStates = wantDeferredStates;
if (wantDeferredStates) {
forEachMatchingBroadcast((r, i) -> {
- return r.deferUntilActive
- && (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_PENDING);
+ return (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_PENDING);
}, applyConsumer, false);
} else {
forEachMatchingBroadcast((r, i) -> {
- return r.deferUntilActive
- && (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_DEFERRED);
+ return (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_DEFERRED);
}, clearConsumer, false);
}
}
}
+ void clearDeferredStates(@NonNull BroadcastConsumer clearConsumer) {
+ if (mLastDeferredStates) {
+ mLastDeferredStates = false;
+ forEachMatchingBroadcast((r, i) -> {
+ return (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_DEFERRED);
+ }, clearConsumer, false);
+ }
+ }
+
+ @VisibleForTesting
+ boolean shouldBeDeferred() {
+ if (mRunnableAtInvalidated) updateRunnableAt();
+ return mRunnableAtReason == REASON_CACHED
+ || mRunnableAtReason == REASON_CACHED_INFINITE_DEFER;
+ }
+
/**
* Check overall health, confirming things are in a reasonable state and
* that we're not wedged.
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index b481697..5b54561 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -479,6 +479,10 @@
break;
}
+ // Clear the deferred state of broadcasts in this queue as we are just about to
+ // deliver broadcasts to this process.
+ queue.clearDeferredStates(mBroadcastConsumerDeferClear);
+
// We might not have heard about a newly running process yet, so
// consider refreshing if we think we're cold
updateWarmProcess(queue);
@@ -1567,12 +1571,14 @@
r.resultExtras = null;
};
- private final BroadcastConsumer mBroadcastConsumerDeferApply = (r, i) -> {
+ @VisibleForTesting
+ final BroadcastConsumer mBroadcastConsumerDeferApply = (r, i) -> {
setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_DEFERRED,
"mBroadcastConsumerDeferApply");
};
- private final BroadcastConsumer mBroadcastConsumerDeferClear = (r, i) -> {
+ @VisibleForTesting
+ final BroadcastConsumer mBroadcastConsumerDeferClear = (r, i) -> {
setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_PENDING,
"mBroadcastConsumerDeferClear");
};
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index a0a7b2b..d0ab287 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1472,10 +1472,13 @@
}
return;
}
+ boolean processFreezableChangeReported = false;
if (opt.isPendingFreeze()) {
// Remove pending DO_FREEZE message
mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
opt.setPendingFreeze(false);
+ reportProcessFreezableChangedLocked(app);
+ processFreezableChangeReported = true;
if (DEBUG_FREEZER) {
Slog.d(TAG_AM, "Cancel freezing " + pid + " " + app.processName);
}
@@ -1524,7 +1527,9 @@
if (processKilled) {
return;
}
- reportProcessFreezableChangedLocked(app);
+ if (!processFreezableChangeReported) {
+ reportProcessFreezableChangedLocked(app);
+ }
long freezeTime = opt.getFreezeUnfreezeTime();
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 052b0c2..e5f7637 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2391,7 +2391,8 @@
long token = Binder.clearCallingIdentity();
try {
// Permissions are managed by UIDs, but unfortunately a package name is required in API.
- String packageName = ArrayUtils.firstOrNull(packageManager.getPackagesForUid(uid));
+ String packageName = ArrayUtils.firstOrNull(ArrayUtils.defeatNullable(
+ packageManager.getPackagesForUid(uid)));
if (packageName == null) {
return false;
}
diff --git a/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java
index 51cb950..5c8dd0d 100644
--- a/services/core/java/com/android/server/audio/AdiDeviceState.java
+++ b/services/core/java/com/android/server/audio/AdiDeviceState.java
@@ -25,6 +25,7 @@
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
+import android.media.Utils;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -167,8 +168,9 @@
public String toString() {
return "type: " + mDeviceType
+ " internal type: 0x" + Integer.toHexString(mInternalDeviceType)
- + " addr: " + mDeviceAddress + " bt audio type: "
- + AudioManager.audioDeviceCategoryToString(mAudioDeviceCategory)
+ + " addr: " + Utils.anonymizeBluetoothAddress(mInternalDeviceType, mDeviceAddress)
+ + " bt audio type: "
+ + AudioManager.audioDeviceCategoryToString(mAudioDeviceCategory)
+ " enabled: " + mSAEnabled + " HT: " + mHasHeadTracker
+ " HTenabled: " + mHeadTrackerEnabled;
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 7ba0827..e9b102b 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -48,6 +48,7 @@
import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.MediaMetrics;
import android.media.MediaRecorder.AudioSource;
+import android.media.Utils;
import android.media.audiopolicy.AudioProductStrategy;
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.SafeCloseable;
@@ -477,7 +478,7 @@
return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType)
+ " (" + AudioSystem.getDeviceName(mDeviceType)
+ ") name:" + mDeviceName
- + " addr:" + mDeviceAddress
+ + " addr:" + Utils.anonymizeBluetoothAddress(mDeviceType, mDeviceAddress)
+ " codec: " + Integer.toHexString(mDeviceCodecFormat)
+ " peer addr:" + mPeerDeviceAddress
+ " group:" + mGroupId
@@ -532,7 +533,7 @@
mApmConnectedDevices.forEach((keyType, valueAddress) -> {
pw.println(" " + prefix + " type:0x" + Integer.toHexString(keyType)
+ " (" + AudioSystem.getDeviceName(keyType)
- + ") addr:" + valueAddress); });
+ + ") addr:" + Utils.anonymizeBluetoothAddress(keyType, valueAddress)); });
pw.println("\n" + prefix + "Preferred devices for capture preset:");
mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> {
pw.println(" " + prefix + "capturePreset:" + capturePreset
@@ -1789,7 +1790,8 @@
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "A2DP device addr=" + address + " now available").printLog(TAG));
+ "A2DP device addr=" + Utils.anonymizeBluetoothAddress(address)
+ + " now available").printLog(TAG));
}
// Reset A2DP suspend state each time a new sink is connected
@@ -2027,7 +2029,8 @@
.equals(mApmConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP))) {
// removing A2DP device not currently used by AudioPolicy, log but don't act on it
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
- "A2DP device " + address + " made unavailable, was not used")).printLog(TAG));
+ "A2DP device " + Utils.anonymizeBluetoothAddress(address)
+ + " made unavailable, was not used")).printLog(TAG));
mmi.set(MediaMetrics.Property.EARLY_RETURN,
"A2DP device made unavailable, was not used")
.record();
@@ -2043,13 +2046,15 @@
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "APM failed to make unavailable A2DP device addr=" + address
+ "APM failed to make unavailable A2DP device addr="
+ + Utils.anonymizeBluetoothAddress(address)
+ " error=" + res).printLog(TAG));
// TODO: failed to disconnect, stop here
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
- "A2DP device addr=" + address + " made unavailable")).printLog(TAG));
+ "A2DP device addr=" + Utils.anonymizeBluetoothAddress(address)
+ + " made unavailable")).printLog(TAG));
}
mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
@@ -2238,7 +2243,8 @@
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "LE Audio device addr=" + address + " now available").printLog(TAG));
+ "LE Audio device addr=" + Utils.anonymizeBluetoothAddress(address)
+ + " now available").printLog(TAG));
}
// Reset LEA suspend state each time a new sink is connected
mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */);
@@ -2282,7 +2288,8 @@
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "LE Audio device addr=" + address + " made unavailable").printLog(TAG));
+ "LE Audio device addr=" + Utils.anonymizeBluetoothAddress(address)
+ + " made unavailable").printLog(TAG));
}
mConnectedDevices.remove(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 b2ee610..1e38c0f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -136,6 +136,7 @@
import android.media.MediaRecorder.AudioSource;
import android.media.PlayerBase;
import android.media.Spatializer;
+import android.media.Utils;
import android.media.VolumeInfo;
import android.media.VolumePolicy;
import android.media.audiofx.AudioEffect;
@@ -7470,7 +7471,7 @@
sVolumeLogger.enqueue(new EventLogger.StringEvent("setDeviceVolumeBehavior: dev:"
+ AudioSystem.getOutputDeviceName(device.getInternalType()) + " addr:"
- + device.getAddress() + " behavior:"
+ + Utils.anonymizeBluetoothAddress(device.getAddress()) + " behavior:"
+ AudioDeviceVolumeManager.volumeBehaviorName(deviceVolumeBehavior)
+ " pack:" + pkgName).printLog(TAG));
if (pkgName == null) {
@@ -9641,7 +9642,7 @@
private void avrcpSupportsAbsoluteVolume(String address, boolean support) {
// address is not used for now, but may be used when multiple a2dp devices are supported
sVolumeLogger.enqueue(new EventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr="
- + address + " support=" + support).printLog(TAG));
+ + Utils.anonymizeBluetoothAddress(address) + " support=" + support).printLog(TAG));
mDeviceBroker.setAvrcpAbsoluteVolumeSupported(support);
setAvrcpAbsoluteVolumeSupported(support);
}
@@ -10539,11 +10540,11 @@
AudioDeviceAttributes retrieveBluetoothAddressUncheked(@NonNull AudioDeviceAttributes ada) {
Objects.requireNonNull(ada);
if (AudioSystem.isBluetoothDevice(ada.getInternalType())) {
- String anonymizedAddress = anonymizeBluetoothAddress(ada.getAddress());
+ String anonymizedAddress = Utils.anonymizeBluetoothAddress(ada.getAddress());
for (AdiDeviceState ads : mDeviceBroker.getImmutableDeviceInventory()) {
if (!(AudioSystem.isBluetoothDevice(ads.getInternalDeviceType())
&& (ada.getInternalType() == ads.getInternalDeviceType())
- && anonymizedAddress.equals(anonymizeBluetoothAddress(
+ && anonymizedAddress.equals(Utils.anonymizeBluetoothAddress(
ads.getDeviceAddress())))) {
continue;
}
@@ -10554,19 +10555,6 @@
return ada;
}
- /**
- * Convert a Bluetooth MAC address to an anonymized one when exposed to a non privileged app
- * Must match the implementation of BluetoothUtils.toAnonymizedAddress()
- * @param address Mac address to be anonymized
- * @return anonymized mac address
- */
- static String anonymizeBluetoothAddress(String address) {
- if (address == null || address.length() != "AA:BB:CC:DD:EE:FF".length()) {
- return null;
- }
- return "XX:XX:XX:XX" + address.substring("XX:XX:XX:XX".length());
- }
-
private List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesList(
List<AudioDeviceAttributes> devices) {
if (isBluetoothPrividged()) {
@@ -10590,7 +10578,7 @@
return ada;
}
AudioDeviceAttributes res = new AudioDeviceAttributes(ada);
- res.setAddress(anonymizeBluetoothAddress(ada.getAddress()));
+ res.setAddress(Utils.anonymizeBluetoothAddress(ada.getAddress()));
return res;
}
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index 283353dd..0d7f778 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -32,29 +32,12 @@
*/
public abstract class VirtualDeviceManagerInternal {
- /** Interface to listen to the creation and destruction of virtual displays. */
- public interface VirtualDisplayListener {
- /** Notifies that a virtual display was created. */
- void onVirtualDisplayCreated(int displayId);
-
- /** Notifies that a virtual display was removed. */
- void onVirtualDisplayRemoved(int displayId);
- }
-
/** Interface to listen to the changes on the list of app UIDs running on any virtual device. */
public interface AppsOnVirtualDeviceListener {
/** Notifies that running apps on any virtual device has changed */
void onAppsOnAnyVirtualDeviceChanged(Set<Integer> allRunningUids);
}
- /** Register a listener for the creation and destruction of virtual displays. */
- public abstract void registerVirtualDisplayListener(
- @NonNull VirtualDisplayListener listener);
-
- /** Unregister a listener for the creation and destruction of virtual displays. */
- public abstract void unregisterVirtualDisplayListener(
- @NonNull VirtualDisplayListener listener);
-
/** Register a listener for changes of running app UIDs on any virtual device. */
public abstract void registerAppsOnVirtualDeviceListener(
@NonNull AppsOnVirtualDeviceListener listener);
@@ -104,13 +87,6 @@
public abstract @NonNull ArraySet<Integer> getDeviceIdsForUid(int uid);
/**
- * Notifies that a virtual display is created.
- *
- * @param displayId The display id of the created virtual display.
- */
- public abstract void onVirtualDisplayCreated(int displayId);
-
- /**
* Notifies that a virtual display is removed.
*
* @param virtualDevice The virtual device where the virtual display located.
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 53fbe8f..a12243b 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -22,7 +22,6 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_AUTO;
@@ -45,12 +44,10 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -113,7 +110,6 @@
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.CancellationSignal;
-import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
@@ -152,7 +148,6 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
-import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BinderUtils;
import com.android.net.module.util.LinkPropertiesUtils;
import com.android.net.module.util.NetdUtils;
@@ -202,7 +197,6 @@
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* @hide
@@ -1063,8 +1057,6 @@
// Store mPackage since it might be reset or might be replaced with the other VPN app.
final String oldPackage = mPackage;
final boolean isPackageChanged = !Objects.equals(packageName, oldPackage);
- // TODO: Remove "SdkLevel.isAtLeastT()" check once VpnManagerService is decoupled from
- // ConnectivityServiceTest.
// Only notify VPN apps that were already always-on, and only if the always-on provider
// changed, or the lockdown mode changed.
final boolean shouldNotifyOldPkg = isVpnApp(oldPackage) && mAlwaysOn
@@ -1078,12 +1070,6 @@
saveAlwaysOnPackage();
- // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
- // ConnectivityServiceTest.
- if (!SdkLevel.isAtLeastT()) {
- return true;
- }
-
if (shouldNotifyOldPkg) {
// If both of shouldNotifyOldPkg & isPackageChanged are true, that means the
// always-on of old package is disabled or the old package is replaced with the new
@@ -1984,9 +1970,7 @@
for (String app : packageNames) {
int uid = getAppUid(mContext, app, userId);
if (uid != -1) uids.add(uid);
- // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
- // ConnectivityServiceTest.
- if (Process.isApplicationUid(uid) && SdkLevel.isAtLeastT()) {
+ if (Process.isApplicationUid(uid)) {
uids.add(Process.toSdkSandboxUid(uid));
}
}
@@ -2297,15 +2281,6 @@
private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
@Override
- public void interfaceStatusChanged(String interfaze, boolean up) {
- synchronized (Vpn.this) {
- if (!up && mVpnRunner != null && mVpnRunner instanceof LegacyVpnRunner) {
- ((LegacyVpnRunner) mVpnRunner).exitIfOuterInterfaceIs(interfaze);
- }
- }
- }
-
- @Override
public void interfaceRemoved(String interfaze) {
synchronized (Vpn.this) {
if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
@@ -2556,17 +2531,6 @@
private native boolean jniAddAddress(String interfaze, String address, int prefixLen);
private native boolean jniDelAddress(String interfaze, String address, int prefixLen);
- private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
- for (RouteInfo route : prop.getAllRoutes()) {
- // Currently legacy VPN only works on IPv4.
- if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
- return route;
- }
- }
-
- throw new IllegalStateException("Unable to find IPv4 default gateway");
- }
-
private void enforceNotRestrictedUser() {
final long token = Binder.clearCallingIdentity();
try {
@@ -2665,10 +2629,6 @@
throw new SecurityException("Restricted users cannot establish VPNs");
}
- final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
- final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
- final String iface = ipv4DefaultRoute.getInterface();
-
// Load certificates.
String privateKey = "";
String userCert = "";
@@ -2700,8 +2660,6 @@
throw new IllegalStateException("Cannot load credentials");
}
- // Prepare arguments for racoon.
- String[] racoon = null;
switch (profile.type) {
case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
// Secret key is still just the alias (not the actual private key). The private key
@@ -2731,109 +2689,9 @@
// profile.
startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN);
return;
- case VpnProfile.TYPE_L2TP_IPSEC_PSK:
- racoon = new String[] {
- iface, profile.server, "udppsk", profile.ipsecIdentifier,
- profile.ipsecSecret, "1701",
- };
- break;
- case VpnProfile.TYPE_L2TP_IPSEC_RSA:
- racoon = new String[] {
- iface, profile.server, "udprsa", makeKeystoreEngineGrantString(privateKey),
- userCert, caCert, serverCert, "1701",
- };
- break;
- case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
- racoon = new String[] {
- iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
- profile.ipsecSecret, profile.username, profile.password, "", gateway,
- };
- break;
- case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
- racoon = new String[] {
- iface, profile.server, "xauthrsa", makeKeystoreEngineGrantString(privateKey),
- userCert, caCert, serverCert, profile.username, profile.password, "", gateway,
- };
- break;
- case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
- racoon = new String[] {
- iface, profile.server, "hybridrsa",
- caCert, serverCert, profile.username, profile.password, "", gateway,
- };
- break;
}
- // Prepare arguments for mtpd. MTU/MRU calculated conservatively. Only IPv4 supported
- // because LegacyVpn.
- // 1500 - 60 (Carrier-internal IPv6 + UDP + GTP) - 10 (PPP) - 16 (L2TP) - 8 (UDP)
- // - 77 (IPsec w/ SHA-2 512, 256b trunc-len, AES-CBC) - 8 (UDP encap) - 20 (IPv4)
- // - 28 (464xlat)
- String[] mtpd = null;
- switch (profile.type) {
- case VpnProfile.TYPE_PPTP:
- mtpd = new String[] {
- iface, "pptp", profile.server, "1723",
- "name", profile.username, "password", profile.password,
- "linkname", "vpn", "refuse-eap", "nodefaultroute",
- "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270",
- (profile.mppe ? "+mppe" : "nomppe"),
- };
- if (profile.mppe) {
- // Disallow PAP authentication when MPPE is requested, as MPPE cannot work
- // with PAP anyway, and users may not expect PAP (plain text) to be used when
- // MPPE was requested.
- mtpd = Arrays.copyOf(mtpd, mtpd.length + 1);
- mtpd[mtpd.length - 1] = "-pap";
- }
- break;
- case VpnProfile.TYPE_L2TP_IPSEC_PSK:
- case VpnProfile.TYPE_L2TP_IPSEC_RSA:
- mtpd = new String[] {
- iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
- "name", profile.username, "password", profile.password,
- "linkname", "vpn", "refuse-eap", "nodefaultroute",
- "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270",
- };
- break;
- }
-
- VpnConfig config = new VpnConfig();
- config.legacy = true;
- config.user = profile.key;
- config.interfaze = iface;
- config.session = profile.name;
- config.isMetered = false;
- config.proxyInfo = profile.proxy;
- if (underlying != null) {
- config.underlyingNetworks = new Network[] { underlying };
- }
-
- config.addLegacyRoutes(profile.routes);
- if (!profile.dnsServers.isEmpty()) {
- config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
- }
- if (!profile.searchDomains.isEmpty()) {
- config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
- }
- startLegacyVpn(config, racoon, mtpd, profile);
- }
-
- private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd,
- VpnProfile profile) {
- stopVpnRunnerPrivileged();
-
- // Prepare for the new request.
- prepareInternal(VpnConfig.LEGACY_VPN);
- updateState(DetailedState.CONNECTING, "startLegacyVpn");
-
- // Start a new LegacyVpnRunner and we are done!
- mVpnRunner = new LegacyVpnRunner(config, racoon, mtpd, profile);
- startLegacyVpnRunner();
- }
-
- @VisibleForTesting
- protected void startLegacyVpnRunner() {
- mVpnRunner.start();
+ throw new UnsupportedOperationException("Legacy VPN is deprecated");
}
/**
@@ -2851,17 +2709,7 @@
return;
}
- final boolean isLegacyVpn = mVpnRunner instanceof LegacyVpnRunner;
mVpnRunner.exit();
-
- // LegacyVpn uses daemons that must be shut down before new ones are brought up.
- // The same limitation does not apply to Platform VPNs.
- if (isLegacyVpn) {
- synchronized (LegacyVpnRunner.TAG) {
- // wait for old thread to completely finish before spinning up
- // new instance, otherwise state updates can be out of order.
- }
- }
}
/**
@@ -4143,9 +3991,7 @@
// Ignore stale runner.
if (mVpnRunner != this) return;
- // TODO(b/230548427): Remove SDK check once VPN related stuff are
- // decoupled from ConnectivityServiceTest.
- if (SdkLevel.isAtLeastT() && category != null && isVpnApp(mPackage)) {
+ if (category != null && isVpnApp(mPackage)) {
sendEventToVpnManagerApp(category, errorClass, errorCode,
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
mActiveNetwork,
@@ -4256,343 +4102,6 @@
}
}
- /**
- * Bringing up a VPN connection takes time, and that is all this thread
- * does. Here we have plenty of time. The only thing we need to take
- * care of is responding to interruptions as soon as possible. Otherwise
- * requests will pile up. This could be done in a Handler as a state
- * machine, but it is much easier to read in the current form.
- */
- private class LegacyVpnRunner extends VpnRunner {
- private static final String TAG = "LegacyVpnRunner";
-
- private final String[] mDaemons;
- private final String[][] mArguments;
- private final LocalSocket[] mSockets;
- private final String mOuterInterface;
- private final AtomicInteger mOuterConnection =
- new AtomicInteger(ConnectivityManager.TYPE_NONE);
- private final VpnProfile mProfile;
-
- private long mBringupStartTime = -1;
-
- /**
- * Watch for the outer connection (passing in the constructor) going away.
- */
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!mEnableTeardown) return;
-
- if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
- ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
- NetworkInfo info = (NetworkInfo)intent.getExtra(
- ConnectivityManager.EXTRA_NETWORK_INFO);
- if (info != null && !info.isConnectedOrConnecting()) {
- try {
- mObserver.interfaceStatusChanged(mOuterInterface, false);
- } catch (RemoteException e) {}
- }
- }
- }
- }
- };
-
- // GuardedBy("Vpn.this") (annotation can't be applied to constructor)
- LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd, VpnProfile profile) {
- super(TAG);
- if (racoon == null && mtpd == null) {
- throw new IllegalArgumentException(
- "Arguments to racoon and mtpd must not both be null");
- }
- mConfig = config;
- mDaemons = new String[] {"racoon", "mtpd"};
- // TODO: clear arguments from memory once launched
- mArguments = new String[][] {racoon, mtpd};
- mSockets = new LocalSocket[mDaemons.length];
-
- // This is the interface which VPN is running on,
- // mConfig.interfaze will change to point to OUR
- // internal interface soon. TODO - add inner/outer to mconfig
- // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
- // we will leave the VPN up. We should check that it's still there/connected after
- // registering
- mOuterInterface = mConfig.interfaze;
-
- mProfile = profile;
-
- if (!TextUtils.isEmpty(mOuterInterface)) {
- for (Network network : mConnectivityManager.getAllNetworks()) {
- final LinkProperties lp = mConnectivityManager.getLinkProperties(network);
- if (lp != null && lp.getAllInterfaceNames().contains(mOuterInterface)) {
- final NetworkInfo netInfo = mConnectivityManager.getNetworkInfo(network);
- if (netInfo != null) {
- mOuterConnection.set(netInfo.getType());
- break;
- }
- }
- }
- }
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- mContext.registerReceiver(mBroadcastReceiver, filter);
- }
-
- /**
- * Checks if the parameter matches the underlying interface
- *
- * <p>If the underlying interface is torn down, the LegacyVpnRunner also should be. It has
- * no ability to migrate between interfaces (or Networks).
- */
- public void exitIfOuterInterfaceIs(String interfaze) {
- if (interfaze.equals(mOuterInterface)) {
- Log.i(TAG, "Legacy VPN is going down with " + interfaze);
- exitVpnRunner();
- }
- }
-
- /** Tears down this LegacyVpn connection */
- @Override
- public void exitVpnRunner() {
- // We assume that everything is reset after stopping the daemons.
- interrupt();
-
- // Always disconnect. This may be called again in cleanupVpnStateLocked() if
- // exitVpnRunner() was called from exit(), but it will be a no-op.
- agentDisconnect();
- try {
- mContext.unregisterReceiver(mBroadcastReceiver);
- } catch (IllegalArgumentException e) {}
- }
-
- @Override
- public void run() {
- // Wait for the previous thread since it has been interrupted.
- Log.v(TAG, "Waiting");
- synchronized (TAG) {
- Log.v(TAG, "Executing");
- try {
- bringup();
- waitForDaemonsToStop();
- interrupted(); // Clear interrupt flag if execute called exit.
- } catch (InterruptedException e) {
- } finally {
- for (LocalSocket socket : mSockets) {
- IoUtils.closeQuietly(socket);
- }
- // This sleep is necessary for racoon to successfully complete sending delete
- // message to server.
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- }
- for (String daemon : mDaemons) {
- mDeps.stopService(daemon);
- }
- }
- agentDisconnect();
- }
- }
-
- private void checkInterruptAndDelay(boolean sleepLonger) throws InterruptedException {
- long now = SystemClock.elapsedRealtime();
- if (now - mBringupStartTime <= 60000) {
- Thread.sleep(sleepLonger ? 200 : 1);
- } else {
- updateState(DetailedState.FAILED, "checkpoint");
- throw new IllegalStateException("VPN bringup took too long");
- }
- }
-
- private void checkAndFixupArguments(@NonNull final InetAddress endpointAddress) {
- final String endpointAddressString = endpointAddress.getHostAddress();
- // Perform some safety checks before inserting the address in place.
- // Position 0 in mDaemons and mArguments must be racoon, and position 1 must be mtpd.
- if (!"racoon".equals(mDaemons[0]) || !"mtpd".equals(mDaemons[1])) {
- throw new IllegalStateException("Unexpected daemons order");
- }
-
- // Respectively, the positions at which racoon and mtpd take the server address
- // argument are 1 and 2. Not all types of VPN require both daemons however, and
- // in that case the corresponding argument array is null.
- if (mArguments[0] != null) {
- if (!mProfile.server.equals(mArguments[0][1])) {
- throw new IllegalStateException("Invalid server argument for racoon");
- }
- mArguments[0][1] = endpointAddressString;
- }
-
- if (mArguments[1] != null) {
- if (!mProfile.server.equals(mArguments[1][2])) {
- throw new IllegalStateException("Invalid server argument for mtpd");
- }
- mArguments[1][2] = endpointAddressString;
- }
- }
-
- private void bringup() {
- // Catch all exceptions so we can clean up a few things.
- try {
- // resolve never returns null. If it does because of some bug, it will be
- // caught by the catch() block below and cleanup gracefully.
- final InetAddress endpointAddress = mDeps.resolve(mProfile.server);
-
- // Big hack : dynamically replace the address of the server in the arguments
- // with the resolved address.
- checkAndFixupArguments(endpointAddress);
-
- // Initialize the timer.
- mBringupStartTime = SystemClock.elapsedRealtime();
-
- // Wait for the daemons to stop.
- for (String daemon : mDaemons) {
- while (!mDeps.isServiceStopped(daemon)) {
- checkInterruptAndDelay(true);
- }
- }
-
- // Clear the previous state.
- final File state = mDeps.getStateFile();
- state.delete();
- if (state.exists()) {
- throw new IllegalStateException("Cannot delete the state");
- }
- new File("/data/misc/vpn/abort").delete();
-
- updateState(DetailedState.CONNECTING, "execute");
-
- // Start the daemon with arguments.
- for (int i = 0; i < mDaemons.length; ++i) {
- String[] arguments = mArguments[i];
- if (arguments == null) {
- continue;
- }
-
- // Start the daemon.
- String daemon = mDaemons[i];
- mDeps.startService(daemon);
-
- // Wait for the daemon to start.
- while (!mDeps.isServiceRunning(daemon)) {
- checkInterruptAndDelay(true);
- }
-
- // Create the control socket.
- mSockets[i] = new LocalSocket();
-
- // Wait for the socket to connect and send over the arguments.
- mDeps.sendArgumentsToDaemon(daemon, mSockets[i], arguments,
- this::checkInterruptAndDelay);
- }
-
- // Wait for the daemons to create the new state.
- while (!state.exists()) {
- // Check if a running daemon is dead.
- for (int i = 0; i < mDaemons.length; ++i) {
- String daemon = mDaemons[i];
- if (mArguments[i] != null && !mDeps.isServiceRunning(daemon)) {
- throw new IllegalStateException(daemon + " is dead");
- }
- }
- checkInterruptAndDelay(true);
- }
-
- // Now we are connected. Read and parse the new state.
- String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
- if (parameters.length != 7) {
- throw new IllegalStateException("Cannot parse the state: '"
- + String.join("', '", parameters) + "'");
- }
-
- // Set the interface and the addresses in the config.
- synchronized (Vpn.this) {
- mConfig.interfaze = parameters[0].trim();
-
- mConfig.addLegacyAddresses(parameters[1]);
- // Set the routes if they are not set in the config.
- if (mConfig.routes == null || mConfig.routes.isEmpty()) {
- mConfig.addLegacyRoutes(parameters[2]);
- }
-
- // Set the DNS servers if they are not set in the config.
- if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
- String dnsServers = parameters[3].trim();
- if (!dnsServers.isEmpty()) {
- mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
- }
- }
-
- // Set the search domains if they are not set in the config.
- if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
- String searchDomains = parameters[4].trim();
- if (!searchDomains.isEmpty()) {
- mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
- }
- }
-
- // Add a throw route for the VPN server endpoint, if one was specified.
- if (endpointAddress instanceof Inet4Address) {
- mConfig.routes.add(new RouteInfo(
- new IpPrefix(endpointAddress, 32), null /*gateway*/,
- null /*iface*/, RTN_THROW));
- } else if (endpointAddress instanceof Inet6Address) {
- mConfig.routes.add(new RouteInfo(
- new IpPrefix(endpointAddress, 128), null /*gateway*/,
- null /*iface*/, RTN_THROW));
- } else {
- Log.e(TAG, "Unknown IP address family for VPN endpoint: "
- + endpointAddress);
- }
-
- // Here is the last step and it must be done synchronously.
- // Set the start time
- mConfig.startTime = SystemClock.elapsedRealtime();
-
- // Check if the thread was interrupted while we were waiting on the lock.
- checkInterruptAndDelay(false);
-
- // Check if the interface is gone while we are waiting.
- if (!mDeps.isInterfacePresent(Vpn.this, mConfig.interfaze)) {
- throw new IllegalStateException(mConfig.interfaze + " is gone");
- }
-
- // Now INetworkManagementEventObserver is watching our back.
- mInterface = mConfig.interfaze;
- prepareStatusIntent();
-
- agentConnect();
-
- Log.i(TAG, "Connected!");
- }
- } catch (Exception e) {
- Log.i(TAG, "Aborting", e);
- updateState(DetailedState.FAILED, e.getMessage());
- exitVpnRunner();
- }
- }
-
- /**
- * Check all daemons every two seconds. Return when one of them is stopped.
- * The caller will move to the disconnected state when this function returns,
- * which can happen if a daemon failed or if the VPN was torn down.
- */
- private void waitForDaemonsToStop() throws InterruptedException {
- if (!mNetworkInfo.isConnected()) {
- return;
- }
- while (true) {
- Thread.sleep(2000);
- for (int i = 0; i < mDaemons.length; i++) {
- if (mArguments[i] != null && mDeps.isServiceStopped(mDaemons[i])) {
- return;
- }
- }
- }
- }
- }
-
private void verifyCallingUidAndPackage(String packageName) {
mDeps.verifyCallingUidAndPackage(mContext, packageName, mUserId);
}
@@ -4839,11 +4348,9 @@
// Build intent first because the sessionKey will be reset after performing
// VpnRunner.exit(). Also, cache mOwnerUID even if ownerUID will not be changed in
// VpnRunner.exit() to prevent design being changed in the future.
- // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
- // ConnectivityServiceTest.
final int ownerUid = mOwnerUID;
Intent intent = null;
- if (SdkLevel.isAtLeastT() && isVpnApp(mPackage)) {
+ if (isVpnApp(mPackage)) {
intent = buildVpnManagerEventIntent(
VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER,
-1 /* errorClass */, -1 /* errorCode*/, mPackage,
@@ -4884,12 +4391,8 @@
// The underlying network, NetworkCapabilities and LinkProperties are not
// necessary to send to VPN app since the purpose of this event is to notify
// VPN app that VPN is deactivated by the user.
- // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
- // ConnectivityServiceTest.
- if (SdkLevel.isAtLeastT()) {
- mEventChanges.log("[VMEvent] " + packageName + " stopped");
- sendEventToVpnManagerApp(intent, packageName);
- }
+ mEventChanges.log("[VMEvent] " + packageName + " stopped");
+ sendEventToVpnManagerApp(intent, packageName);
}
private boolean storeAppExclusionList(@NonNull String packageName,
diff --git a/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
index 2d6b013..035a748 100644
--- a/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
+++ b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
@@ -39,7 +39,6 @@
private static final String TAG = AutofillSuggestionsController.class.getSimpleName();
@NonNull private final InputMethodManagerService mService;
- @NonNull private final InputMethodUtils.InputMethodSettings mSettings;
private static final class CreateInlineSuggestionsRequest {
@NonNull final InlineSuggestionsRequestInfo mRequestInfo;
@@ -76,7 +75,6 @@
AutofillSuggestionsController(@NonNull InputMethodManagerService service) {
mService = service;
- mSettings = mService.mSettings;
}
@GuardedBy("ImfLock.class")
@@ -88,7 +86,7 @@
final InputMethodInfo imi = mService.queryInputMethodForCurrentUserLocked(
mService.getSelectedMethodIdLocked());
try {
- if (userId == mSettings.getCurrentUserId()
+ if (userId == mService.getCurrentImeUserIdLocked()
&& imi != null && isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) {
mPendingInlineSuggestionsRequest = new CreateInlineSuggestionsRequest(
requestInfo, callback, imi.getPackageName());
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index e76aa1a..c8c0482 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -63,7 +63,6 @@
@NonNull private final InputMethodManagerService mService;
@NonNull private final Context mContext;
- @NonNull private final InputMethodUtils.InputMethodSettings mSettings;
@NonNull private final PackageManagerInternal mPackageManagerInternal;
@NonNull private final WindowManagerInternal mWindowManagerInternal;
@@ -113,7 +112,6 @@
int imeConnectionBindFlags, CountDownLatch latchForTesting) {
mService = service;
mContext = mService.mContext;
- mSettings = mService.mSettings;
mPackageManagerInternal = mService.mPackageManagerInternal;
mWindowManagerInternal = mService.mWindowManagerInternal;
mImeConnectionBindFlags = imeConnectionBindFlags;
@@ -322,7 +320,7 @@
private void updateCurrentMethodUid() {
final String curMethodPackage = mCurIntent.getComponent().getPackageName();
final int curMethodUid = mPackageManagerInternal.getPackageUid(
- curMethodPackage, 0 /* flags */, mSettings.getCurrentUserId());
+ curMethodPackage, 0 /* flags */, mService.getCurrentImeUserIdLocked());
if (curMethodUid < 0) {
Slog.e(TAG, "Failed to get UID for package=" + curMethodPackage);
mCurMethodUid = Process.INVALID_UID;
@@ -478,7 +476,7 @@
return false;
}
return mContext.bindServiceAsUser(mCurIntent, conn, flags,
- new UserHandle(mSettings.getCurrentUserId()));
+ new UserHandle(mService.getCurrentImeUserIdLocked()));
}
@GuardedBy("ImfLock.class")
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 1ae67dd..d656f20 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -20,6 +20,7 @@
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import static android.os.IServiceManager.DUMP_FLAG_PROTO;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
import static android.provider.Settings.Secure.STYLUS_HANDWRITING_DEFAULT_VALUE;
import static android.provider.Settings.Secure.STYLUS_HANDWRITING_ENABLED;
import static android.server.inputmethod.InputMethodManagerServiceProto.BACK_DISPOSITION;
@@ -1727,6 +1728,12 @@
registerDeviceListenerAndCheckStylusSupport();
}
+ @GuardedBy("ImfLock.class")
+ @UserIdInt
+ int getCurrentImeUserIdLocked() {
+ return mSettings.getCurrentUserId();
+ }
+
private final class InkWindowInitializer implements Runnable {
public void run() {
synchronized (ImfLock.class) {
@@ -6459,6 +6466,11 @@
if (!userHasDebugPriv(userId, shellCommand)) {
continue;
}
+ // Skip on headless user
+ if (USER_TYPE_SYSTEM_HEADLESS.equals(
+ mUserManagerInternal.getUserInfo(userId).userType)) {
+ continue;
+ }
final String nextIme;
final List<InputMethodInfo> nextEnabledImes;
if (userId == mSettings.getCurrentUserId()) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 86e417b..efa1e0d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -77,13 +77,15 @@
void showInputMethodMenu(boolean showAuxSubtypes, int displayId) {
if (DEBUG) Slog.v(TAG, "Show switching menu. showAuxSubtypes=" + showAuxSubtypes);
- final boolean isScreenLocked = isScreenLocked();
-
- final String lastInputMethodId = mSettings.getSelectedInputMethod();
- int lastInputMethodSubtypeId = mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
- if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
-
synchronized (ImfLock.class) {
+ final boolean isScreenLocked = mWindowManagerInternal.isKeyguardLocked()
+ && mWindowManagerInternal.isKeyguardSecure(
+ mService.getCurrentImeUserIdLocked());
+ final String lastInputMethodId = mSettings.getSelectedInputMethod();
+ int lastInputMethodSubtypeId =
+ mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
+ if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
+
final List<ImeSubtypeListItem> imList = mSwitchingController
.getSortedInputMethodAndSubtypeListForImeMenuLocked(
showAuxSubtypes, isScreenLocked);
@@ -200,12 +202,8 @@
mService.updateSystemUiLocked();
mService.sendOnNavButtonFlagsChangedLocked();
mSwitchingDialog.show();
- }
- }
- private boolean isScreenLocked() {
- return mWindowManagerInternal.isKeyguardLocked()
- && mWindowManagerInternal.isKeyguardSecure(mSettings.getCurrentUserId());
+ }
}
void updateKeyboardFromSettingsLocked() {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1bdd402..2ec3700 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -179,6 +179,8 @@
import android.app.role.RoleManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
+import android.companion.AssociationInfo;
+import android.companion.AssociationRequest;
import android.companion.ICompanionDeviceManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
@@ -546,6 +548,15 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
static final long ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION = 264179692L;
+ /**
+ * App calls to {@link android.app.NotificationManager#setInterruptionFilter} and
+ * {@link android.app.NotificationManager#setNotificationPolicy} manage DND through the
+ * creation and activation of an implicit {@link android.app.AutomaticZenRule}.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ static final long MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES = 308670109L;
+
private static final Duration POST_WAKE_LOCK_TIMEOUT = Duration.ofSeconds(30);
private IActivityManager mAm;
@@ -5343,6 +5354,12 @@
if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
final int callingUid = Binder.getCallingUid();
final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+
+ if (android.app.Flags.modesApi() && !canManageGlobalZenPolicy(pkg, callingUid)) {
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, callingUid, zen);
+ return;
+ }
+
final long identity = Binder.clearCallingIdentity();
try {
mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter",
@@ -5426,6 +5443,16 @@
}
}
+ private boolean canManageGlobalZenPolicy(String callingPkg, int callingUid) {
+ boolean isCompatChangeEnabled = Binder.withCleanCallingIdentity(
+ () -> CompatChanges.isChangeEnabled(MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES,
+ callingUid));
+ return !isCompatChangeEnabled
+ || isCallerIsSystemOrSystemUi()
+ || hasCompanionDevice(callingPkg, UserHandle.getUserId(callingUid),
+ AssociationRequest.DEVICE_PROFILE_WATCH);
+ }
+
private void enforcePolicyAccess(String pkg, String method) {
if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
@@ -5619,6 +5646,10 @@
@Override
public Policy getNotificationPolicy(String pkg) {
+ final int callingUid = Binder.getCallingUid();
+ if (android.app.Flags.modesApi() && !canManageGlobalZenPolicy(pkg, callingUid)) {
+ return mZenModeHelper.getNotificationPolicyFromImplicitZenRule(pkg);
+ }
final long identity = Binder.clearCallingIdentity();
try {
return mZenModeHelper.getNotificationPolicy();
@@ -5649,6 +5680,10 @@
enforcePolicyAccess(pkg, "setNotificationPolicy");
int callingUid = Binder.getCallingUid();
boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+
+ boolean shouldApplyAsImplicitRule = android.app.Flags.modesApi()
+ && !canManageGlobalZenPolicy(pkg, callingUid);
+
final long identity = Binder.clearCallingIdentity();
try {
final ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(pkg,
@@ -5687,16 +5722,21 @@
policy = new Policy(policy.priorityCategories,
policy.priorityCallSenders, policy.priorityMessageSenders,
newVisualEffects, policy.priorityConversationSenders);
- ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion, policy);
- mZenModeHelper.setNotificationPolicy(policy, callingUid, isSystemOrSystemUi);
+
+ if (shouldApplyAsImplicitRule) {
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, callingUid, policy);
+ } else {
+ ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion,
+ policy);
+ mZenModeHelper.setNotificationPolicy(policy, callingUid, isSystemOrSystemUi);
+ }
} catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set notification policy", e);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
-
-
@Override
public List<String> getEnabledNotificationListenerPackages() {
checkCallerIsSystem();
@@ -10556,6 +10596,12 @@
}
boolean hasCompanionDevice(ManagedServiceInfo info) {
+ return hasCompanionDevice(info.component.getPackageName(),
+ info.userid, /* withDeviceProfile= */ null);
+ }
+
+ private boolean hasCompanionDevice(String pkg, @UserIdInt int userId,
+ @Nullable @AssociationRequest.DeviceProfile String withDeviceProfile) {
if (mCompanionManager == null) {
mCompanionManager = getCompanionManager();
}
@@ -10565,17 +10611,19 @@
}
final long identity = Binder.clearCallingIdentity();
try {
- List<?> associations = mCompanionManager.getAssociations(
- info.component.getPackageName(), info.userid);
- if (!ArrayUtils.isEmpty(associations)) {
- return true;
+ List<AssociationInfo> associations = mCompanionManager.getAssociations(pkg, userId);
+ for (AssociationInfo association : associations) {
+ if (withDeviceProfile == null || withDeviceProfile.equals(
+ association.getDeviceProfile())) {
+ return true;
+ }
}
} catch (SecurityException se) {
// Not a privileged listener
} catch (RemoteException re) {
Slog.e(TAG, "Cannot reach companion device service", re);
} catch (Exception e) {
- Slog.e(TAG, "Cannot verify listener " + info, e);
+ Slog.e(TAG, "Cannot verify caller pkg=" + pkg + ", userId=" + userId, e);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -11401,17 +11449,16 @@
}
}
}
- }
- // clean up anything in the disallowed pkgs list
- for (int i = 0; i < pkgList.length; i++) {
- String pkg = pkgList[i];
- for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
- NotificationListenerFilter nlf =
- mRequestedNotificationListeners.valueAt(j);
-
- VersionedPackage ai = new VersionedPackage(pkg, uidList[i]);
- nlf.removePackage(ai);
+ // Clean up removed package from the disallowed packages list
+ for (int i = 0; i < pkgList.length; i++) {
+ String pkg = pkgList[i];
+ for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
+ NotificationListenerFilter nlf =
+ mRequestedNotificationListeners.valueAt(j);
+ VersionedPackage ai = new VersionedPackage(pkg, uidList[i]);
+ nlf.removePackage(ai);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 8f5676b3..9106c33 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -118,7 +118,10 @@
protected boolean canSnooze(int numberToSnooze) {
synchronized (mLock) {
- if ((mSnoozedNotifications.size() + numberToSnooze) > CONCURRENT_SNOOZE_LIMIT) {
+ if ((mSnoozedNotifications.size() + numberToSnooze) > CONCURRENT_SNOOZE_LIMIT
+ || (mPersistedSnoozedNotifications.size()
+ + mPersistedSnoozedNotificationsWithContext.size() + numberToSnooze)
+ > CONCURRENT_SNOOZE_LIMIT) {
return false;
}
}
@@ -357,6 +360,9 @@
if (groupSummaryKey != null) {
NotificationRecord record = mSnoozedNotifications.remove(groupSummaryKey);
+ String trimmedKey = getTrimmedString(groupSummaryKey);
+ mPersistedSnoozedNotificationsWithContext.remove(trimmedKey);
+ mPersistedSnoozedNotifications.remove(trimmedKey);
if (record != null && !record.isCanceled) {
Runnable runnable = () -> {
diff --git a/services/core/java/com/android/server/notification/ZenAdapters.java b/services/core/java/com/android/server/notification/ZenAdapters.java
new file mode 100644
index 0000000..2a65aff
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ZenAdapters.java
@@ -0,0 +1,78 @@
+/*
+ * 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.notification;
+
+import android.app.NotificationManager.Policy;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenPolicy;
+
+/**
+ * Converters between different Zen representations.
+ */
+class ZenAdapters {
+
+ static ZenPolicy notificationPolicyToZenPolicy(Policy policy) {
+ ZenPolicy.Builder zenPolicyBuilder = new ZenPolicy.Builder()
+ .allowAlarms(policy.allowAlarms())
+ .allowCalls(
+ policy.allowCalls()
+ ? ZenModeConfig.getZenPolicySenders(policy.allowCallsFrom())
+ : ZenPolicy.PEOPLE_TYPE_NONE)
+ .allowConversations(
+ policy.allowConversations()
+ ? notificationPolicyConversationSendersToZenPolicy(
+ policy.allowConversationsFrom())
+ : ZenPolicy.CONVERSATION_SENDERS_NONE)
+ .allowEvents(policy.allowEvents())
+ .allowMedia(policy.allowMedia())
+ .allowMessages(
+ policy.allowMessages()
+ ? ZenModeConfig.getZenPolicySenders(policy.allowMessagesFrom())
+ : ZenPolicy.PEOPLE_TYPE_NONE)
+ .allowReminders(policy.allowReminders())
+ .allowRepeatCallers(policy.allowRepeatCallers())
+ .allowSystem(policy.allowSystem());
+
+ if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
+ zenPolicyBuilder.showBadges(policy.showBadges())
+ .showFullScreenIntent(policy.showFullScreenIntents())
+ .showInAmbientDisplay(policy.showAmbient())
+ .showInNotificationList(policy.showInNotificationList())
+ .showLights(policy.showLights())
+ .showPeeking(policy.showPeeking())
+ .showStatusBarIcons(policy.showStatusBarIcons());
+ }
+
+ return zenPolicyBuilder.build();
+ }
+
+ @ZenPolicy.ConversationSenders
+ private static int notificationPolicyConversationSendersToZenPolicy(
+ int npPriorityConversationSenders) {
+ switch (npPriorityConversationSenders) {
+ case Policy.CONVERSATION_SENDERS_ANYONE:
+ return ZenPolicy.CONVERSATION_SENDERS_ANYONE;
+ case Policy.CONVERSATION_SENDERS_IMPORTANT:
+ return ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
+ case Policy.CONVERSATION_SENDERS_NONE:
+ return ZenPolicy.CONVERSATION_SENDERS_NONE;
+ case Policy.CONVERSATION_SENDERS_UNSET:
+ default:
+ return ZenPolicy.CONVERSATION_SENDERS_UNSET;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 762c1a1..c637df2 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -27,6 +27,7 @@
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
@@ -44,6 +45,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -349,11 +351,11 @@
ZenRule rule;
synchronized (mConfigLock) {
if (mConfig == null) return null;
- rule = mConfig.automaticRules.get(id);
+ rule = mConfig.automaticRules.get(id);
}
if (rule == null) return null;
if (canManageAutomaticZenRule(rule)) {
- return createAutomaticZenRule(rule);
+ return zenRuleToAutomaticZenRule(rule);
}
return null;
}
@@ -439,6 +441,167 @@
}
}
+ /**
+ * Create (or activate, or deactivate) an "implicit" {@link ZenRule} when an app that has
+ * Notification Policy Access but is not allowed to manage the global zen state
+ * calls {@link NotificationManager#setInterruptionFilter}.
+ *
+ * <p>When the {@code zenMode} is {@link Global#ZEN_MODE_OFF}, an existing implicit rule will be
+ * deactivated (if there is no implicit rule, the call will be ignored). For other modes, the
+ * rule's interruption filter will match the supplied {@code zenMode}. The policy of the last
+ * call to {@link NotificationManager#setNotificationPolicy} will be used (or, if never called,
+ * the global policy).
+ *
+ * <p>The created rule is owned by the calling package, but it has neither a
+ * {@link ConditionProviderService} nor an associated
+ * {@link AutomaticZenRule#configurationActivity}.
+ *
+ * @param zenMode one of the {@code Global#ZEN_MODE_x} values
+ */
+ void applyGlobalZenModeAsImplicitZenRule(String callingPkg, int callingUid, int zenMode) {
+ if (!android.app.Flags.modesApi()) {
+ Log.wtf(TAG, "applyGlobalZenModeAsImplicitZenRule called with flag off!");
+ return;
+ }
+ synchronized (mConfigLock) {
+ if (mConfig == null) {
+ return;
+ }
+ if (zenMode == Global.ZEN_MODE_OFF) {
+ // Deactivate implicit rule if it exists and is active; otherwise ignore.
+ ZenRule rule = mConfig.automaticRules.get(implicitRuleId(callingPkg));
+ if (rule != null) {
+ Condition deactivated = new Condition(rule.conditionId,
+ mContext.getString(R.string.zen_mode_implicit_deactivated),
+ Condition.STATE_FALSE);
+ setAutomaticZenRuleState(rule.id, deactivated,
+ callingUid, /* fromSystemOrSystemUi= */ false);
+ }
+ } else {
+ // Either create a new rule with a default ZenPolicy, or update an existing rule's
+ // filter value. In both cases, also activate (and unsnooze) it.
+ ZenModeConfig newConfig = mConfig.copy();
+ ZenRule rule = newConfig.automaticRules.get(implicitRuleId(callingPkg));
+ if (rule == null) {
+ rule = newImplicitZenRule(callingPkg);
+ newConfig.automaticRules.put(rule.id, rule);
+ }
+ rule.zenMode = zenMode;
+ rule.snoozing = false;
+ rule.condition = new Condition(rule.conditionId,
+ mContext.getString(R.string.zen_mode_implicit_activated),
+ Condition.STATE_TRUE);
+ setConfigLocked(newConfig, /* triggeringComponent= */ null,
+ "applyGlobalZenModeAsImplicitZenRule",
+ callingUid, /* fromSystemOrSystemUi= */ false);
+ }
+ }
+ }
+
+ /**
+ * Create (or update) an "implicit" {@link ZenRule} when an app that has Notification Policy
+ * Access but is not allowed to manage the global zen state calls
+ * {@link NotificationManager#setNotificationPolicy}.
+ *
+ * <p>The created rule is owned by the calling package and has the {@link ZenPolicy}
+ * corresponding to the supplied {@code policy}, but it has neither a
+ * {@link ConditionProviderService} nor an associated
+ * {@link AutomaticZenRule#configurationActivity}. Its zen mode will be set to
+ * {@link Global#ZEN_MODE_IMPORTANT_INTERRUPTIONS}.
+ */
+ void applyGlobalPolicyAsImplicitZenRule(String callingPkg, int callingUid,
+ NotificationManager.Policy policy) {
+ if (!android.app.Flags.modesApi()) {
+ Log.wtf(TAG, "applyGlobalPolicyAsImplicitZenRule called with flag off!");
+ return;
+ }
+ synchronized (mConfigLock) {
+ if (mConfig == null) {
+ return;
+ }
+ ZenModeConfig newConfig = mConfig.copy();
+ ZenRule rule = newConfig.automaticRules.get(implicitRuleId(callingPkg));
+ if (rule == null) {
+ rule = newImplicitZenRule(callingPkg);
+ rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ newConfig.automaticRules.put(rule.id, rule);
+ }
+ // TODO: b/308673679 - Keep user customization of this rule!
+ rule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy);
+ setConfigLocked(newConfig, /* triggeringComponent= */ null,
+ "applyGlobalPolicyAsImplicitZenRule",
+ callingUid, /* fromSystemOrSystemUi= */ false);
+ }
+ }
+
+ /**
+ * Returns the {@link Policy} associated to the "implicit" {@link ZenRule} of a package that has
+ * Notification Policy Access but is not allowed to manage the global zen state.
+ *
+ * <p>If the implicit rule doesn't exist, or it doesn't specify a {@link ZenPolicy} (because the
+ * app never called {@link NotificationManager#setNotificationPolicy}) then the default policy
+ * is returned (i.e. same as {@link #getNotificationPolicy}.
+ *
+ * <p>Any unset values in the {@link ZenPolicy} will be mapped to their current defaults.
+ */
+ @Nullable
+ Policy getNotificationPolicyFromImplicitZenRule(String callingPkg) {
+ if (!android.app.Flags.modesApi()) {
+ Log.wtf(TAG, "getNotificationPolicyFromImplicitZenRule called with flag off!");
+ return getNotificationPolicy();
+ }
+ synchronized (mConfigLock) {
+ if (mConfig == null) {
+ return null;
+ }
+ ZenRule implicitRule = mConfig.automaticRules.get(implicitRuleId(callingPkg));
+ if (implicitRule != null && implicitRule.zenPolicy != null) {
+ return mConfig.toNotificationPolicy(implicitRule.zenPolicy);
+ } else {
+ return getNotificationPolicy();
+ }
+ }
+ }
+
+ /**
+ * Creates an empty {@link ZenRule} to be used as the implicit rule for {@code pkg}.
+ * Both {@link ZenRule#zenMode} and {@link ZenRule#zenPolicy} are unset.
+ */
+ private ZenRule newImplicitZenRule(String pkg) {
+ ZenRule rule = new ZenRule();
+ rule.id = implicitRuleId(pkg);
+ rule.pkg = pkg;
+ rule.creationTime = System.currentTimeMillis();
+
+ Binder.withCleanCallingIdentity(() -> {
+ try {
+ ApplicationInfo applicationInfo = mPm.getApplicationInfo(pkg, 0);
+ rule.name = applicationInfo.loadLabel(mPm).toString();
+ } catch (PackageManager.NameNotFoundException e) {
+ // Should not happen, since it's the app calling us (?)
+ Log.w(TAG, "Package not found for creating implicit zen rule");
+ rule.name = "Unknown";
+ }
+ });
+
+ rule.condition = null;
+ rule.conditionId = new Uri.Builder()
+ .scheme(Condition.SCHEME)
+ .authority("android")
+ .appendPath("implicit")
+ .appendPath(pkg)
+ .build();
+ rule.enabled = true;
+ rule.modified = false;
+ rule.component = null;
+ rule.configurationActivity = null;
+ return rule;
+ }
+
+ private static String implicitRuleId(String forPackage) {
+ return "implicit_" + forPackage;
+ }
+
public boolean removeAutomaticZenRule(String id, String reason, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
@@ -626,7 +789,7 @@
}
// update default rule (if locale changed, name of rule will change)
currRule.name = defaultRule.name;
- updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
+ updateAutomaticZenRule(defaultRule.id, zenRuleToAutomaticZenRule(currRule),
"locale changed", callingUid, fromSystemOrSystemUi);
}
}
@@ -669,7 +832,7 @@
return null;
}
- private void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
+ private static void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
boolean isNew) {
if (rule.enabled != automaticZenRule.isEnabled()) {
rule.snoozing = false;
@@ -699,7 +862,7 @@
}
}
- protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
+ private static AutomaticZenRule zenRuleToAutomaticZenRule(ZenRule rule) {
AutomaticZenRule azr;
if (Flags.modesApi()) {
azr = new AutomaticZenRule.Builder(rule.name, rule.conditionId)
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 25b7ca1..dcac8c9 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -7,8 +7,6 @@
bug: "290381858"
}
-
-
flag {
name: "polite_notifications"
namespace: "systemui"
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index cba605e..2617703 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -60,6 +60,7 @@
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatureArrays;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.isSystemOrRootOrShell;
+import static com.android.server.pm.parsing.PackageInfoUtils.getDeprecatedSignatures;
import static com.android.server.pm.resolution.ComponentResolver.RESOLVE_PRIORITY_SORTER;
import android.Manifest;
@@ -1531,6 +1532,7 @@
pi.applicationInfo = PackageInfoUtils.generateDelegateApplicationInfo(
ai, flags, state, userId);
pi.signingInfo = ps.getSigningInfo();
+ pi.signatures = getDeprecatedSignatures(pi.signingInfo.getSigningDetails(), flags);
if (DEBUG_PACKAGE_INFO) {
Log.v(TAG, "ps.pkg is n/a for ["
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index c26cf1c..38cde3e 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -238,21 +238,7 @@
}
final SigningDetails signingDetails = pkg.getSigningDetails();
- // deprecated method of getting signing certificates
- if ((flags & PackageManager.GET_SIGNATURES) != 0) {
- if (signingDetails.hasPastSigningCertificates()) {
- // Package has included signing certificate rotation information. Return the oldest
- // cert so that programmatic checks keep working even if unaware of key rotation.
- info.signatures = new Signature[1];
- info.signatures[0] = signingDetails.getPastSigningCertificates()[0];
- } else if (signingDetails.hasSignatures()) {
- // otherwise keep old behavior
- int numberOfSigs = signingDetails.getSignatures().length;
- info.signatures = new Signature[numberOfSigs];
- System.arraycopy(signingDetails.getSignatures(), 0, info.signatures, 0,
- numberOfSigs);
- }
- }
+ info.signatures = getDeprecatedSignatures(signingDetails, flags);
// replacement for GET_SIGNATURES
if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
@@ -358,6 +344,30 @@
return info;
}
+ /**
+ * Retrieve the deprecated {@link PackageInfo.signatures} field of signing certificates
+ */
+ public static Signature[] getDeprecatedSignatures(SigningDetails signingDetails, long flags) {
+ if ((flags & PackageManager.GET_SIGNATURES) == 0) {
+ return null;
+ }
+ if (signingDetails.hasPastSigningCertificates()) {
+ // Package has included signing certificate rotation information. Return the oldest
+ // cert so that programmatic checks keep working even if unaware of key rotation.
+ Signature[] signatures = new Signature[1];
+ signatures[0] = signingDetails.getPastSigningCertificates()[0];
+ return signatures;
+ } else if (signingDetails.hasSignatures()) {
+ // otherwise keep old behavior
+ int numberOfSigs = signingDetails.getSignatures().length;
+ Signature[] signatures = new Signature[numberOfSigs];
+ System.arraycopy(signingDetails.getSignatures(), 0, signatures, 0,
+ numberOfSigs);
+ return signatures;
+ }
+ return null;
+ }
+
private static void updateApplicationInfo(ApplicationInfo ai, long flags,
PackageUserState state) {
if ((flags & PackageManager.GET_META_DATA) == 0) {
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 77290fd..9f0a975 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -603,40 +603,51 @@
@NonNull
private String getEnergyConsumerName(EnergyConsumer consumer,
EnergyConsumer[] energyConsumers) {
- if (consumer.type != EnergyConsumerType.OTHER) {
- StringBuilder sb = new StringBuilder();
- sb.append(energyConsumerTypeToString(consumer.type));
- boolean hasOrdinal = consumer.ordinal != 0;
- if (!hasOrdinal) {
- // See if any other EnergyConsumer of the same type has an ordinal
- for (EnergyConsumer aConsumer : energyConsumers) {
- if (aConsumer.type == consumer.type && aConsumer.ordinal != 0) {
- hasOrdinal = true;
- break;
- }
+ StringBuilder sb = new StringBuilder();
+ switch (consumer.type) {
+ case EnergyConsumerType.BLUETOOTH:
+ sb.append("BLUETOOTH");
+ break;
+ case EnergyConsumerType.CPU_CLUSTER:
+ sb.append("CPU");
+ break;
+ case EnergyConsumerType.DISPLAY:
+ sb.append("DISPLAY");
+ break;
+ case EnergyConsumerType.GNSS:
+ sb.append("GNSS");
+ break;
+ case EnergyConsumerType.MOBILE_RADIO:
+ sb.append("MOBILE_RADIO");
+ break;
+ case EnergyConsumerType.WIFI:
+ sb.append("WIFI");
+ break;
+ case EnergyConsumerType.CAMERA:
+ sb.append("CAMERA");
+ break;
+ default:
+ if (consumer.name != null && !consumer.name.isBlank()) {
+ sb.append(consumer.name.toUpperCase(Locale.ENGLISH));
+ } else {
+ sb.append("CONSUMER_").append(consumer.type);
+ }
+ break;
+ }
+ boolean hasOrdinal = consumer.ordinal != 0;
+ if (!hasOrdinal) {
+ // See if any other EnergyConsumer of the same type has an ordinal
+ for (EnergyConsumer aConsumer : energyConsumers) {
+ if (aConsumer.type == consumer.type && aConsumer.ordinal != 0) {
+ hasOrdinal = true;
+ break;
}
}
- if (hasOrdinal) {
- sb.append('/').append(consumer.ordinal);
- }
- return sb.toString();
- } else {
- return consumer.name;
}
- }
-
- private static String energyConsumerTypeToString(int type) {
- switch(type) {
- case EnergyConsumerType.BLUETOOTH: return "BLUETOOTH";
- case EnergyConsumerType.CPU_CLUSTER: return "CPU";
- case EnergyConsumerType.DISPLAY: return "DISPLAY";
- case EnergyConsumerType.GNSS: return "GNSS";
- case EnergyConsumerType.MOBILE_RADIO: return "MOBILE_RADIO";
- case EnergyConsumerType.WIFI: return "WIFI";
- case EnergyConsumerType.OTHER: return "";
- default:
- throw new IllegalStateException("Unrecognized EnergyConsumerType: " + type);
+ if (hasOrdinal) {
+ sb.append('/').append(consumer.ordinal);
}
+ return sb.toString();
}
/**
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index fed6e7e..b2e808a 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -117,16 +117,16 @@
static final class CallerInfo {
public final VibrationAttributes attrs;
public final int uid;
- public final int displayId;
+ public final int deviceId;
public final String opPkg;
public final String reason;
- CallerInfo(@NonNull VibrationAttributes attrs, int uid, int displayId,
- String opPkg, String reason) {
+ CallerInfo(@NonNull VibrationAttributes attrs, int uid, int deviceId, String opPkg,
+ String reason) {
Objects.requireNonNull(attrs);
this.attrs = attrs;
this.uid = uid;
- this.displayId = displayId;
+ this.deviceId = deviceId;
this.opPkg = opPkg;
this.reason = reason;
}
@@ -138,14 +138,14 @@
CallerInfo that = (CallerInfo) o;
return Objects.equals(attrs, that.attrs)
&& uid == that.uid
- && displayId == that.displayId
+ && deviceId == that.deviceId
&& Objects.equals(opPkg, that.opPkg)
&& Objects.equals(reason, that.reason);
}
@Override
public int hashCode() {
- return Objects.hash(attrs, uid, displayId, opPkg, reason);
+ return Objects.hash(attrs, uid, deviceId, opPkg, reason);
}
@Override
@@ -153,7 +153,7 @@
return "CallerInfo{"
+ " uid=" + uid
+ ", opPkg=" + opPkg
- + ", displayId=" + displayId
+ + ", deviceId=" + deviceId
+ ", attrs=" + attrs
+ ", reason=" + reason
+ '}';
@@ -267,8 +267,8 @@
mStartTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mStartTime)),
mEndTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mEndTime)));
String callerInfoStr = String.format(Locale.ROOT,
- " | %s (uid=%d, displayId=%d) | usage: %s (audio=%s) | flags: %s | reason: %s",
- mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.displayId,
+ " | %s (uid=%d, deviceId=%d) | usage: %s (audio=%s) | flags: %s | reason: %s",
+ mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.deviceId,
mCallerInfo.attrs.usageToString(),
AudioAttributes.usageToString(mCallerInfo.attrs.getAudioUsage()),
Long.toBinaryString(mCallerInfo.attrs.getFlags()),
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 7f55836..839c207 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -61,7 +61,6 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
-import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -166,7 +165,6 @@
final MyUidObserver mUidObserver;
@VisibleForTesting
final SettingsBroadcastReceiver mSettingChangeReceiver;
- final VirtualDeviceListener mVirtualDeviceListener;
@GuardedBy("mLock")
private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@@ -180,6 +178,8 @@
@GuardedBy("mLock")
@Nullable
private PowerManagerInternal mPowerManagerInternal;
+ @Nullable
+ private VirtualDeviceManagerInternal mVirtualDeviceManagerInternal;
@GuardedBy("mLock")
private boolean mVibrateInputDevices;
@@ -207,8 +207,6 @@
mSettingObserver = new SettingsContentObserver(handler);
mUidObserver = new MyUidObserver();
mSettingChangeReceiver = new SettingsBroadcastReceiver();
- mVirtualDeviceListener = new VirtualDeviceListener();
-
mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
.getSystemUiServiceComponent().getPackageName();
@@ -272,13 +270,6 @@
}
});
- VirtualDeviceManagerInternal vdm = LocalServices.getService(
- VirtualDeviceManagerInternal.class);
- if (vdm != null) {
- vdm.registerVirtualDisplayListener(mVirtualDeviceListener);
- vdm.registerAppsOnVirtualDeviceListener(mVirtualDeviceListener);
- }
-
registerSettingsChangeReceiver(USER_SWITCHED_INTENT_FILTER);
registerSettingsChangeReceiver(INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER);
@@ -414,8 +405,14 @@
&& !BACKGROUND_PROCESS_USAGE_ALLOWLIST.contains(usage)) {
return Vibration.Status.IGNORED_BACKGROUND;
}
- if (mVirtualDeviceListener.isAppOrDisplayOnAnyVirtualDevice(callerInfo.uid,
- callerInfo.displayId)) {
+
+ if (callerInfo.deviceId != Context.DEVICE_ID_DEFAULT
+ && callerInfo.deviceId != Context.DEVICE_ID_INVALID) {
+ return Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE;
+ }
+
+ if (callerInfo.deviceId == Context.DEVICE_ID_INVALID
+ && isAppRunningOnAnyVirtualDevice(callerInfo.uid)) {
return Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE;
}
@@ -794,6 +791,15 @@
return out;
}
+ private boolean isAppRunningOnAnyVirtualDevice(int uid) {
+ if (mVirtualDeviceManagerInternal == null) {
+ mVirtualDeviceManagerInternal =
+ LocalServices.getService(VirtualDeviceManagerInternal.class);
+ }
+ return mVirtualDeviceManagerInternal != null
+ && mVirtualDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(uid);
+ }
+
/** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
@VisibleForTesting
final class SettingsContentObserver extends ContentObserver {
@@ -853,73 +859,4 @@
}
}
}
-
- /**
- * Implementation of Virtual Device listeners for the changes of virtual displays and of apps
- * running on any virtual device.
- */
- final class VirtualDeviceListener implements
- VirtualDeviceManagerInternal.VirtualDisplayListener,
- VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener {
- @GuardedBy("mLock")
- private final Set<Integer> mVirtualDisplays = new HashSet<>();
- @GuardedBy("mLock")
- private final Set<Integer> mAppsOnVirtualDevice = new HashSet<>();
-
-
- @Override
- public void onVirtualDisplayCreated(int displayId) {
- synchronized (mLock) {
- mVirtualDisplays.add(displayId);
- }
- }
-
- @Override
- public void onVirtualDisplayRemoved(int displayId) {
- synchronized (mLock) {
- mVirtualDisplays.remove(displayId);
- }
- }
-
-
- @Override
- public void onAppsOnAnyVirtualDeviceChanged(Set<Integer> allRunningUids) {
- synchronized (mLock) {
- mAppsOnVirtualDevice.clear();
- mAppsOnVirtualDevice.addAll(allRunningUids);
- }
- }
-
- /**
- * @param uid: uid of the calling app.
- * @param displayId: the id of a Display.
- * @return Returns true if:
- * <ul>
- * <li> the displayId is valid, and it's owned by a virtual device.</li>
- * <li> the displayId is invalid, and the calling app (uid) is running on a virtual
- * device.</li>
- * </ul>
- */
- public boolean isAppOrDisplayOnAnyVirtualDevice(int uid, int displayId) {
- if (displayId == Display.DEFAULT_DISPLAY) {
- // The default display is the primary physical display on the phone.
- return false;
- }
-
- synchronized (mLock) {
- if (displayId == Display.INVALID_DISPLAY) {
- // There is no Display object associated with the Context of calling
- // {@link SystemVibratorManager}, checking the calling UID instead.
- return mAppsOnVirtualDevice.contains(uid);
- } else {
- // Other valid display IDs representing valid logical displays will be
- // checked
- // against the active virtual displays set built with the registered
- // {@link VirtualDisplayListener}.
- return mVirtualDisplays.contains(displayId);
- }
- }
- }
-
- }
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index ace7777..cf33cc5 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -63,7 +63,6 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
-import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -385,7 +384,7 @@
return false;
}
AlwaysOnVibration alwaysOnVibration = new AlwaysOnVibration(alwaysOnId,
- new Vibration.CallerInfo(attrs, uid, Display.DEFAULT_DISPLAY, opPkg,
+ new Vibration.CallerInfo(attrs, uid, Context.DEVICE_ID_DEFAULT, opPkg,
null), effects);
mAlwaysOnEffects.put(alwaysOnId, alwaysOnVibration);
updateAlwaysOnLocked(alwaysOnVibration);
@@ -397,16 +396,16 @@
}
@Override // Binder call
- public void vibrate(int uid, int displayId, String opPkg, @NonNull CombinedVibration effect,
+ public void vibrate(int uid, int deviceId, String opPkg, @NonNull CombinedVibration effect,
@Nullable VibrationAttributes attrs, String reason, IBinder token) {
- vibrateWithPermissionCheck(uid, displayId, opPkg, effect, attrs, reason, token);
+ vibrateWithPermissionCheck(uid, deviceId, opPkg, effect, attrs, reason, token);
}
@Override // Binder call
public void performHapticFeedback(
- int uid, int displayId, String opPkg, int constant, boolean always, String reason,
+ int uid, int deviceId, String opPkg, int constant, boolean always, String reason,
IBinder token) {
- performHapticFeedbackInternal(uid, displayId, opPkg, constant, always, reason, token);
+ performHapticFeedbackInternal(uid, deviceId, opPkg, constant, always, reason, token);
}
/**
@@ -417,7 +416,7 @@
@VisibleForTesting
@Nullable
HalVibration performHapticFeedbackInternal(
- int uid, int displayId, String opPkg, int constant, boolean always, String reason,
+ int uid, int deviceId, String opPkg, int constant, boolean always, String reason,
IBinder token) {
HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider();
if (hapticVibrationProvider == null) {
@@ -433,7 +432,7 @@
VibrationAttributes attrs =
hapticVibrationProvider.getVibrationAttributesForHapticFeedback(
constant, /* bypassVibrationIntensitySetting= */ always);
- return vibrateWithoutPermissionCheck(uid, displayId, opPkg, combinedVibration, attrs,
+ return vibrateWithoutPermissionCheck(uid, deviceId, opPkg, combinedVibration, attrs,
"performHapticFeedback: " + reason, token);
}
@@ -444,7 +443,7 @@
*/
@VisibleForTesting
@Nullable
- HalVibration vibrateWithPermissionCheck(int uid, int displayId, String opPkg,
+ HalVibration vibrateWithPermissionCheck(int uid, int deviceId, String opPkg,
@NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs,
String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
@@ -452,24 +451,24 @@
attrs = fixupVibrationAttributes(attrs, effect);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.VIBRATE, "vibrate");
- return vibrateInternal(uid, displayId, opPkg, effect, attrs, reason, token);
+ return vibrateInternal(uid, deviceId, opPkg, effect, attrs, reason, token);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
- HalVibration vibrateWithoutPermissionCheck(int uid, int displayId, String opPkg,
+ HalVibration vibrateWithoutPermissionCheck(int uid, int deviceId, String opPkg,
@NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs,
String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate no perm check, reason = " + reason);
try {
- return vibrateInternal(uid, displayId, opPkg, effect, attrs, reason, token);
+ return vibrateInternal(uid, deviceId, opPkg, effect, attrs, reason, token);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
- private HalVibration vibrateInternal(int uid, int displayId, String opPkg,
+ private HalVibration vibrateInternal(int uid, int deviceId, String opPkg,
@NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs,
String reason, IBinder token) {
if (token == null) {
@@ -482,7 +481,7 @@
}
// Create Vibration.Stats as close to the received request as possible, for tracking.
HalVibration vib = new HalVibration(token, effect,
- new Vibration.CallerInfo(attrs, uid, displayId, opPkg, reason));
+ new Vibration.CallerInfo(attrs, uid, deviceId, opPkg, reason));
fillVibrationFallbacks(vib, effect);
if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
@@ -1558,10 +1557,9 @@
private ExternalVibrationHolder(ExternalVibration externalVibration) {
super(externalVibration.getToken(), new Vibration.CallerInfo(
externalVibration.getVibrationAttributes(), externalVibration.getUid(),
- // TODO(b/243604888): propagating displayID from IExternalVibration instead of
- // using INVALID_DISPLAY for all external vibrations.
- Display.INVALID_DISPLAY,
- externalVibration.getPackage(), null));
+ // TODO(b/249785241): Find a way to link ExternalVibration to a VirtualDevice
+ // instead of using DEVICE_ID_INVALID here and relying on the UID checks.
+ Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null));
this.externalVibration = externalVibration;
this.scale = IExternalVibratorService.SCALE_NONE;
mStatus = Vibration.Status.RUNNING;
@@ -1974,8 +1972,6 @@
boolean alreadyUnderExternalControl = false;
boolean waitForCompletion = false;
synchronized (mLock) {
- // TODO(b/243604888): propagating displayID from IExternalVibration instead of
- // using INVALID_DISPLAY for all external vibrations.
Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked(
vibHolder.callerInfo);
@@ -2184,7 +2180,7 @@
IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
: mShellCallbacksToken;
HalVibration vib = vibrateWithPermissionCheck(Binder.getCallingUid(),
- Display.DEFAULT_DISPLAY, SHELL_PACKAGE_NAME, combined, attrs,
+ Context.DEVICE_ID_DEFAULT, SHELL_PACKAGE_NAME, combined, attrs,
commonOptions.description, deathBinder);
maybeWaitOnVibration(vib, commonOptions);
}
@@ -2241,7 +2237,7 @@
IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
: mShellCallbacksToken;
HalVibration vib = performHapticFeedbackInternal(Binder.getCallingUid(),
- Display.DEFAULT_DISPLAY, SHELL_PACKAGE_NAME, constant,
+ Context.DEVICE_ID_DEFAULT, SHELL_PACKAGE_NAME, constant,
/* always= */ commonOptions.force, /* reason= */ commonOptions.description,
deathBinder);
maybeWaitOnVibration(vib, commonOptions);
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 1b5631f5..a2f5a38 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -256,13 +256,24 @@
mOriginatingPendingIntent = originatingPendingIntent;
mIntent = intent;
mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
+ if (originatingPendingIntent == null) {
+ // grant creator BAL privileges unless explicitly opted out
+ mBalAllowedByPiCreator =
+ checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+ == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
+ ? BackgroundStartPrivileges.NONE
+ : BackgroundStartPrivileges.ALLOW_BAL;
+ } else {
+ // for PendingIntents we restrict creator BAL based on target_sdk
+ mBalAllowedByPiCreator =
+ checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+ == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
+ ? BackgroundStartPrivileges.NONE
+ : BackgroundStartPrivileges.ALLOW_BAL;
+ }
mBalAllowedByPiSender =
- PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(checkedOptions,
- realCallingUid, mRealCallingPackage);
- mBalAllowedByPiCreator =
- checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
- == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
- ? BackgroundStartPrivileges.NONE : BackgroundStartPrivileges.ALLOW_BAL;
+ PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
+ checkedOptions, realCallingUid, mRealCallingPackage);
mAppSwitchState = mService.getBalAppSwitchesState();
mCallingUidProcState = mService.mActiveUids.getUidState(callingUid);
mIsCallingUidPersistentSystemProcess =
@@ -306,10 +317,14 @@
return name + "[debugOnly]";
}
- private boolean isPendingIntent() {
+ private boolean hasRealCaller() {
return mRealCallingUid != NO_PROCESS_UID;
}
+ private boolean isPendingIntent() {
+ return mOriginatingPendingIntent != null;
+ }
+
private String dump(BalVerdict resultIfPiCreatorAllowsBal) {
Preconditions.checkState(!isPendingIntent());
return dump(resultIfPiCreatorAllowsBal, null);
@@ -334,7 +349,9 @@
sb.append("; isCallingUidPersistentSystemProcess: ")
.append(mIsCallingUidPersistentSystemProcess);
sb.append("; balAllowedByPiCreator: ").append(mBalAllowedByPiCreator);
- if (isPendingIntent()) {
+ sb.append("; hasRealCaller: ").append(hasRealCaller());
+ sb.append("; isPendingIntent: ").append(isPendingIntent());
+ if (hasRealCaller()) {
sb.append("; balAllowedByPiSender: ").append(mBalAllowedByPiSender);
sb.append("; realCallingPackage: ")
.append(getDebugPackageName(mRealCallingPackage, mRealCallingUid));
@@ -351,13 +368,13 @@
sb.append("; mForcedBalByPiSender: ").append(mForcedBalByPiSender);
sb.append("; intent: ").append(mIntent);
sb.append("; callerApp: ").append(mCallerApp);
- if (isPendingIntent()) {
+ if (hasRealCaller()) {
sb.append("; realCallerApp: ").append(mRealCallerApp);
}
if (mCallerApp != null) {
sb.append("; inVisibleTask: ").append(mCallerApp.hasActivityInVisibleTask());
}
- if (isPendingIntent()) {
+ if (hasRealCaller()) {
if (mRealCallerApp != null) {
sb.append("; realInVisibleTask: ")
.append(mRealCallerApp.hasActivityInVisibleTask());
@@ -484,7 +501,7 @@
BalVerdict resultForCaller = checkBackgroundActivityStartAllowedByCaller(state);
- if (!state.isPendingIntent()) {
+ if (!state.hasRealCaller()) {
if (resultForCaller.allows()) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Background activity start allowed. "
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a778415..c9703db 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4648,7 +4648,7 @@
// Expanding pip into new rotation, so create a rotation leash
// until the display is rotated.
topActivity.getOrCreateFixedRotationLeash(
- topActivity.getSyncTransaction());
+ topActivity.getPendingTransaction());
}
lastParentBeforePip.moveToFront("movePinnedActivityToOriginalTask");
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index c0bf2ce..3e23fab 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1052,12 +1052,12 @@
* @return true if we are *guaranteed* to enter-pip. This means we return false if there's
* a chance we won't thus legacy-entry (via pause+userLeaving) will return false.
*/
- private boolean checkEnterPipOnFinish(@NonNull ActivityRecord ar,
- @Nullable ActivityRecord resuming) {
+ private boolean checkEnterPipOnFinish(@NonNull ActivityRecord ar) {
if (!mCanPipOnFinish || !ar.isVisible() || ar.getTask() == null || !ar.isState(RESUMED)) {
return false;
}
+ final ActivityRecord resuming = getVisibleTransientLaunch(ar.getTaskDisplayArea());
if (ar.pictureInPictureArgs != null && ar.pictureInPictureArgs.isAutoEnterEnabled()) {
if (!ar.getTask().isVisibleRequested() || didCommitTransientLaunch()) {
// force enable pip-on-task-switch now that we've committed to actually launching
@@ -1196,9 +1196,7 @@
final boolean isScreenOff = ar.mDisplayContent == null
|| ar.mDisplayContent.getDisplayInfo().state == Display.STATE_OFF;
if ((!visibleAtTransitionEnd || isScreenOff) && !ar.isVisibleRequested()) {
- final ActivityRecord resuming = getVisibleTransientLaunch(
- ar.getTaskDisplayArea());
- final boolean commitVisibility = !checkEnterPipOnFinish(ar, resuming);
+ final boolean commitVisibility = !checkEnterPipOnFinish(ar);
// Avoid commit visibility if entering pip or else we will get a sudden
// "flash" / surface going invisible for a split second.
if (commitVisibility) {
@@ -1431,7 +1429,7 @@
if (candidateActivity.getTaskDisplayArea() != taskDisplayArea) {
continue;
}
- if (!candidateActivity.isVisible()) {
+ if (!candidateActivity.isVisibleRequested()) {
continue;
}
return candidateActivity;
diff --git a/services/core/java/com/android/server/wm/WindowManagerFlags.java b/services/core/java/com/android/server/wm/WindowManagerFlags.java
index 4667710..b3a3650 100644
--- a/services/core/java/com/android/server/wm/WindowManagerFlags.java
+++ b/services/core/java/com/android/server/wm/WindowManagerFlags.java
@@ -49,5 +49,8 @@
final boolean mWallpaperOffsetAsync = Flags.wallpaperOffsetAsync();
+ final boolean mAllowsScreenSizeDecoupledFromStatusBarAndCutout =
+ Flags.allowsScreenSizeDecoupledFromStatusBarAndCutout();
+
/* End Available Flags */
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 55678c5..0a986c8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -336,7 +336,6 @@
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.power.ShutdownThread;
import com.android.server.utils.PriorityDump;
-import com.android.window.flags.Flags;
import dalvik.annotation.optimization.NeverCompile;
@@ -1194,7 +1193,7 @@
.getBoolean(R.bool.config_skipActivityRelaunchWhenDocking);
final boolean isScreenSizeDecoupledFromStatusBarAndCutout = context.getResources()
.getBoolean(R.bool.config_decoupleStatusBarAndDisplayCutoutFromScreenSize)
- && Flags.closeToSquareConfigIncludesStatusBar();
+ && mFlags.mAllowsScreenSizeDecoupledFromStatusBarAndCutout;
if (!isScreenSizeDecoupledFromStatusBarAndCutout) {
mDecorTypes = WindowInsets.Type.displayCutout() | WindowInsets.Type.navigationBars();
mConfigTypes = WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars();
diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp
index 80427b3..505421e 100644
--- a/services/core/jni/tvinput/JTvInputHal.cpp
+++ b/services/core/jni/tvinput/JTvInputHal.cpp
@@ -494,12 +494,21 @@
::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::setCallback(
const std::shared_ptr<TvInputCallbackWrapper>& in_callback) {
if (mIsHidl) {
- in_callback->aidlTvInputCallback = nullptr;
- return hidlSetCallback(in_callback == nullptr ? nullptr : in_callback->hidlTvInputCallback);
+ if (in_callback == nullptr) {
+ return hidlSetCallback(nullptr);
+ }
+ else {
+ in_callback->aidlTvInputCallback = nullptr;
+ return hidlSetCallback(in_callback->hidlTvInputCallback);
+ }
} else {
- in_callback->hidlTvInputCallback = nullptr;
- return mAidlTvInput->setCallback(in_callback == nullptr ? nullptr
- : in_callback->aidlTvInputCallback);
+ if (in_callback == nullptr) {
+ return mAidlTvInput->setCallback(nullptr);
+ }
+ else {
+ in_callback->hidlTvInputCallback = nullptr;
+ return mAidlTvInput->setCallback(in_callback->aidlTvInputCallback);
+ }
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 34d6755..9b62a2c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -9667,6 +9667,26 @@
}
}
+ @Override
+ public ComponentName getDeviceOwnerComponentOnUser(int userId) {
+ if (!mHasFeature) {
+ return null;
+ }
+ if (mInjector.userHandleGetCallingUserId() != userId) {
+ Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())
+ || hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ }
+ synchronized (getLockObject()) {
+ // There is only ever one device owner on a device so if the passed userId is the same
+ // as the device owner userId we know that the componentName returned by
+ // getDeviceOwnerComponent will be the correct one.
+ if (mOwners.getDeviceOwnerUserId() == userId || userId == UserHandle.USER_ALL) {
+ return mOwners.getDeviceOwnerComponent();
+ }
+ }
+ return null;
+ }
+
private int getDeviceOwnerUserIdUncheckedLocked() {
return mOwners.hasDeviceOwner() ? mOwners.getDeviceOwnerUserId() : UserHandle.USER_NULL;
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
index 15a5859..7db09f9 100644
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
@@ -16,7 +16,9 @@
package com.android.server.permission.access.permission
+import android.Manifest
import android.permission.PermissionManager
+import android.permission.flags.Flags
import android.util.Slog
import com.android.modules.utils.BinaryXmlPullParser
import com.android.modules.utils.BinaryXmlSerializer
@@ -273,7 +275,12 @@
/** These permissions are supported for virtual devices. */
// TODO: b/298661870 - Use new API to get the list of device aware permissions.
- val DEVICE_AWARE_PERMISSIONS = emptySet<String>()
+ val DEVICE_AWARE_PERMISSIONS =
+ if (Flags.deviceAwarePermissionApis()) {
+ setOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
+ } else {
+ emptySet<String>()
+ }
}
/**
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
new file mode 100644
index 0000000..72dc725
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
@@ -0,0 +1,259 @@
+/*
+ * 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.am;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.annotation.NonNull;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.TestLooperManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.SparseArray;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.AlarmManagerInternal;
+import com.android.server.DropBoxManagerInternal;
+import com.android.server.LocalServices;
+import com.android.server.appop.AppOpsService;
+import com.android.server.wm.ActivityTaskManagerService;
+
+import org.junit.Rule;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public abstract class BaseBroadcastQueueTest {
+
+ static final int USER_GUEST = 11;
+
+ static final String PACKAGE_ANDROID = "android";
+ static final String PACKAGE_PHONE = "com.android.phone";
+ static final String PACKAGE_RED = "com.example.red";
+ static final String PACKAGE_GREEN = "com.example.green";
+ static final String PACKAGE_BLUE = "com.example.blue";
+ static final String PACKAGE_YELLOW = "com.example.yellow";
+ static final String PACKAGE_ORANGE = "com.example.orange";
+
+ static final String PROCESS_SYSTEM = "system";
+
+ static final String CLASS_RED = "com.example.red.Red";
+ static final String CLASS_GREEN = "com.example.green.Green";
+ static final String CLASS_BLUE = "com.example.blue.Blue";
+ static final String CLASS_YELLOW = "com.example.yellow.Yellow";
+ static final String CLASS_ORANGE = "com.example.orange.Orange";
+
+ static final BroadcastProcessQueue.BroadcastPredicate BROADCAST_PREDICATE_ANY =
+ (r, i) -> true;
+
+ @Rule
+ public final ApplicationExitInfoTest.ServiceThreadRule
+ mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
+
+ @Mock
+ AppOpsService mAppOpsService;
+ @Mock
+ PackageManagerInternal mPackageManagerInt;
+ @Mock
+ UsageStatsManagerInternal mUsageStatsManagerInt;
+ @Mock
+ DropBoxManagerInternal mDropBoxManagerInt;
+ @Mock
+ AlarmManagerInternal mAlarmManagerInt;
+ @Mock
+ ProcessList mProcessList;
+
+ Context mContext;
+ ActivityManagerService mAms;
+ BroadcastConstants mConstants;
+ BroadcastSkipPolicy mSkipPolicy;
+ HandlerThread mHandlerThread;
+ TestLooperManager mLooper;
+ AtomicInteger mNextPid;
+
+ /**
+ * Map from PID to registered registered runtime receivers.
+ */
+ SparseArray<ReceiverList> mRegisteredReceivers = new SparseArray<>();
+
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mHandlerThread = new HandlerThread(getTag());
+ mHandlerThread.start();
+ // Pause all event processing until a test chooses to resume
+ mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
+ .acquireLooperManager(mHandlerThread.getLooper()));
+ mNextPid = new AtomicInteger(100);
+
+ LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
+ LocalServices.addService(DropBoxManagerInternal.class, mDropBoxManagerInt);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
+ LocalServices.removeServiceForTest(AlarmManagerInternal.class);
+ LocalServices.addService(AlarmManagerInternal.class, mAlarmManagerInt);
+ doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
+ doNothing().when(mPackageManagerInt).notifyComponentUsed(any(), anyInt(), any(), any());
+ doAnswer((invocation) -> {
+ return getUidForPackage(invocation.getArgument(0));
+ }).when(mPackageManagerInt).getPackageUid(any(), anyLong(), eq(UserHandle.USER_SYSTEM));
+
+ final ActivityManagerService realAms = new ActivityManagerService(
+ new TestInjector(mContext), mServiceThreadRule.getThread());
+ realAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+ realAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
+ realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
+ realAms.mOomAdjuster = spy(realAms.mOomAdjuster);
+ realAms.mPackageManagerInt = mPackageManagerInt;
+ realAms.mUsageStatsService = mUsageStatsManagerInt;
+ realAms.mProcessesReady = true;
+ mAms = spy(realAms);
+
+ mSkipPolicy = spy(new BroadcastSkipPolicy(mAms));
+ doReturn(null).when(mSkipPolicy).shouldSkipMessage(any(), any());
+ doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
+
+ mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
+ }
+
+ public void tearDown() throws Exception {
+ mHandlerThread.quit();
+ }
+
+ static int getUidForPackage(@NonNull String packageName) {
+ switch (packageName) {
+ case PACKAGE_ANDROID: return android.os.Process.SYSTEM_UID;
+ case PACKAGE_PHONE: return android.os.Process.PHONE_UID;
+ case PACKAGE_RED: return android.os.Process.FIRST_APPLICATION_UID + 1;
+ case PACKAGE_GREEN: return android.os.Process.FIRST_APPLICATION_UID + 2;
+ case PACKAGE_BLUE: return android.os.Process.FIRST_APPLICATION_UID + 3;
+ case PACKAGE_YELLOW: return android.os.Process.FIRST_APPLICATION_UID + 4;
+ case PACKAGE_ORANGE: return android.os.Process.FIRST_APPLICATION_UID + 5;
+ default: throw new IllegalArgumentException();
+ }
+ }
+
+ static int getUidForPackage(@NonNull String packageName, int userId) {
+ return UserHandle.getUid(userId, getUidForPackage(packageName));
+ }
+
+ private class TestInjector extends ActivityManagerService.Injector {
+ TestInjector(Context context) {
+ super(context);
+ }
+
+ @Override
+ public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile,
+ Handler handler) {
+ return mAppOpsService;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return mHandlerThread.getThreadHandler();
+ }
+
+ @Override
+ public ProcessList getProcessList(ActivityManagerService service) {
+ return mProcessList;
+ }
+ }
+
+ abstract String getTag();
+
+ static ApplicationInfo makeApplicationInfo(String packageName) {
+ return makeApplicationInfo(packageName, packageName, UserHandle.USER_SYSTEM);
+ }
+
+ static ApplicationInfo makeApplicationInfo(String packageName, String processName, int userId) {
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = packageName;
+ ai.processName = processName;
+ ai.uid = getUidForPackage(packageName, userId);
+ return ai;
+ }
+
+ static ResolveInfo withPriority(ResolveInfo info, int priority) {
+ info.priority = priority;
+ return info;
+ }
+
+ static BroadcastFilter withPriority(BroadcastFilter filter, int priority) {
+ filter.setPriority(priority);
+ return filter;
+ }
+
+ static ResolveInfo makeManifestReceiver(String packageName, String name) {
+ return makeManifestReceiver(packageName, name, UserHandle.USER_SYSTEM);
+ }
+
+ static ResolveInfo makeManifestReceiver(String packageName, String name, int userId) {
+ return makeManifestReceiver(packageName, packageName, name, userId);
+ }
+
+ static ResolveInfo makeManifestReceiver(String packageName, String processName,
+ String name, int userId) {
+ final ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = new ActivityInfo();
+ ri.activityInfo.packageName = packageName;
+ ri.activityInfo.processName = processName;
+ ri.activityInfo.name = name;
+ ri.activityInfo.applicationInfo = makeApplicationInfo(packageName, processName, userId);
+ return ri;
+ }
+
+ BroadcastFilter makeRegisteredReceiver(ProcessRecord app) {
+ return makeRegisteredReceiver(app, 0);
+ }
+
+ BroadcastFilter makeRegisteredReceiver(ProcessRecord app, int priority) {
+ final ReceiverList receiverList = mRegisteredReceivers.get(app.getPid());
+ return makeRegisteredReceiver(receiverList, priority);
+ }
+
+ static BroadcastFilter makeRegisteredReceiver(ReceiverList receiverList, int priority) {
+ final IntentFilter filter = new IntentFilter();
+ filter.setPriority(priority);
+ final BroadcastFilter res = new BroadcastFilter(filter, receiverList,
+ receiverList.app.info.packageName, null, null, null, receiverList.uid,
+ receiverList.userId, false, false, true);
+ receiverList.add(res);
+ return res;
+ }
+}
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 08f5d03..2378416 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -31,17 +31,6 @@
import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_PRIORITIZED;
import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList;
import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList;
-import static com.android.server.am.BroadcastQueueTest.CLASS_BLUE;
-import static com.android.server.am.BroadcastQueueTest.CLASS_GREEN;
-import static com.android.server.am.BroadcastQueueTest.CLASS_RED;
-import static com.android.server.am.BroadcastQueueTest.CLASS_YELLOW;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_BLUE;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_GREEN;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_RED;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_YELLOW;
-import static com.android.server.am.BroadcastQueueTest.getUidForPackage;
-import static com.android.server.am.BroadcastQueueTest.makeManifestReceiver;
-import static com.android.server.am.BroadcastQueueTest.withPriority;
import static com.android.server.am.BroadcastRecord.isReceiverEquals;
import static com.google.common.truth.Truth.assertThat;
@@ -74,17 +63,15 @@
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.BundleMerger;
import android.os.DropBoxManager;
-import android.os.HandlerThread;
import android.os.Process;
import android.os.SystemClock;
-import android.os.TestLooperManager;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.IndentingPrintWriter;
import android.util.Pair;
@@ -108,11 +95,12 @@
import java.util.Objects;
@SmallTest
-public final class BroadcastQueueModernImplTest {
+public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
+ private static final String TAG = "BroadcastQueueModernImplTest";
+
private static final int TEST_UID = android.os.Process.FIRST_APPLICATION_UID;
private static final int TEST_UID2 = android.os.Process.FIRST_APPLICATION_UID + 1;
- @Mock ActivityManagerService mAms;
@Mock ProcessRecord mProcess;
@Mock BroadcastProcessQueue mQueue1;
@@ -120,11 +108,6 @@
@Mock BroadcastProcessQueue mQueue3;
@Mock BroadcastProcessQueue mQueue4;
- HandlerThread mHandlerThread;
- TestLooperManager mLooper;
-
- BroadcastConstants mConstants;
- private BroadcastSkipPolicy mSkipPolicy;
BroadcastQueueModernImpl mImpl;
BroadcastProcessQueue mHead;
@@ -136,22 +119,12 @@
@Before
public void setUp() throws Exception {
- mHandlerThread = new HandlerThread(getClass().getSimpleName());
- mHandlerThread.start();
+ super.setUp();
- // Pause all event processing until a test chooses to resume
- mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
- .acquireLooperManager(mHandlerThread.getLooper()));
-
- mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
mConstants.DELAY_URGENT_MILLIS = -120_000;
mConstants.DELAY_NORMAL_MILLIS = 10_000;
mConstants.DELAY_CACHED_MILLIS = 120_000;
- mSkipPolicy = spy(new BroadcastSkipPolicy(mAms));
- doReturn(null).when(mSkipPolicy).shouldSkipMessage(any(), any());
- doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
-
final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
public void addBroadcastToHistoryLocked(BroadcastRecord original) {
// Ignored
@@ -169,7 +142,12 @@
@After
public void tearDown() throws Exception {
- mHandlerThread.quit();
+ super.tearDown();
+ }
+
+ @Override
+ public String getTag() {
+ return TAG;
}
/**
@@ -225,11 +203,6 @@
List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), false);
}
- private BroadcastRecord makeOrderedBroadcastRecord(Intent intent) {
- return makeBroadcastRecord(intent, BroadcastOptions.makeBasic(),
- List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), true);
- }
-
private BroadcastRecord makeBroadcastRecord(Intent intent, List receivers) {
return makeBroadcastRecord(intent, BroadcastOptions.makeBasic(), receivers, false);
}
@@ -246,8 +219,8 @@
private BroadcastRecord makeBroadcastRecord(Intent intent, BroadcastOptions options,
List receivers, IIntentReceiver resultTo, boolean ordered) {
- return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, 42, false, null,
- null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo,
+ return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, TEST_UID, false,
+ null, null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo,
Activity.RESULT_OK, null, null, ordered, false, false, UserHandle.USER_SYSTEM,
BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN);
}
@@ -259,12 +232,12 @@
private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue,
BroadcastRecord record, int recordIndex, long enqueueTime) {
- queue.enqueueOrReplaceBroadcast(record, recordIndex, (r, i) -> {
- throw new UnsupportedOperationException();
- });
record.enqueueTime = enqueueTime;
record.enqueueRealTime = enqueueTime;
record.enqueueClockTime = enqueueTime;
+ queue.enqueueOrReplaceBroadcast(record, recordIndex, (r, i) -> {
+ throw new UnsupportedOperationException();
+ });
}
@Test
@@ -419,6 +392,7 @@
assertFalse(queue.isRunnable());
assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
queue.getRunnableAtReason());
+ assertTrue(queue.shouldBeDeferred());
assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
}
@@ -445,6 +419,7 @@
assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
assertTrue(queue.isRunnable());
assertEquals(BroadcastProcessQueue.REASON_CACHED, queue.getRunnableAtReason());
+ assertTrue(queue.shouldBeDeferred());
assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
}
@@ -526,11 +501,13 @@
queue.invalidateRunnableAt();
assertThat(queue.getRunnableAt()).isGreaterThan(airplaneRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
mConstants.MAX_PENDING_BROADCASTS = 1;
queue.invalidateRunnableAt();
assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_MAX_PENDING, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
@Test
@@ -549,10 +526,12 @@
queue.setProcessAndUidState(mProcess, true, false);
assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_FOREGROUND, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
queue.setProcessAndUidState(mProcess, false, false);
assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
@Test
@@ -570,6 +549,7 @@
assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_TOP_PROCESS, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
doReturn(ActivityManager.PROCESS_STATE_SERVICE).when(mProcess).getSetProcState();
queue.setProcessAndUidState(mProcess, false, false);
@@ -580,6 +560,7 @@
List.of(makeMockRegisteredReceiver())), 0);
assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
@Test
@@ -594,16 +575,19 @@
assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
doReturn(true).when(mProcess).isPersistent();
queue.setProcessAndUidState(mProcess, false, false);
assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_PERSISTENT, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
doReturn(false).when(mProcess).isPersistent();
queue.setProcessAndUidState(mProcess, false, false);
assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
@Test
@@ -618,6 +602,7 @@
assertThat(queue.getRunnableAt()).isEqualTo(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_CORE_UID, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
@Test
@@ -636,10 +621,12 @@
assertEquals(Long.MAX_VALUE, queue.getRunnableAt());
assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
queue.getRunnableAtReason());
+ assertTrue(queue.shouldBeDeferred());
queue.setProcessAndUidState(mProcess, false, false);
assertThat(queue.getRunnableAt()).isEqualTo(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_CORE_UID, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
/**
@@ -1575,6 +1562,216 @@
verifyPendingRecords(redQueue, List.of(userPresent, timeTick));
}
+ @Test
+ public void testDeliveryDeferredForCached() throws Exception {
+ final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
+ final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED));
+
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+ final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick,
+ List.of(makeRegisteredReceiver(greenProcess, 0)));
+
+ final Intent batteryChanged = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ final BroadcastOptions optionsBatteryChanged =
+ BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord batteryChangedRecord = makeBroadcastRecord(batteryChanged,
+ optionsBatteryChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 10),
+ makeRegisteredReceiver(redProcess, 0)),
+ false /* ordered */);
+
+ mImpl.enqueueBroadcastLocked(timeTickRecord);
+ mImpl.enqueueBroadcastLocked(batteryChangedRecord);
+
+ final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED,
+ getUidForPackage(PACKAGE_RED));
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_BLOCKED, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ true /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED, greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ // Once the broadcasts to green process are deferred, broadcasts to red process
+ // shouldn't be blocked anymore.
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to green process should be deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ final Intent packageChanged = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+ final BroadcastRecord packageChangedRecord = makeBroadcastRecord(packageChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 0)));
+ mImpl.enqueueBroadcastLocked(packageChangedRecord);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED, greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to the green process, including the newly enqueued one, should be
+ // deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ false /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ }
+
+ @Test
+ public void testDeliveryDeferredForCached_withInfiniteDeferred() throws Exception {
+ final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
+ final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED));
+
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+ final BroadcastOptions optionsTimeTick = BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, optionsTimeTick,
+ List.of(makeRegisteredReceiver(greenProcess, 0)), false /* ordered */);
+
+ final Intent batteryChanged = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ final BroadcastOptions optionsBatteryChanged =
+ BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord batteryChangedRecord = makeBroadcastRecord(batteryChanged,
+ optionsBatteryChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 10),
+ makeRegisteredReceiver(redProcess, 0)),
+ false /* ordered */);
+
+ mImpl.enqueueBroadcastLocked(timeTickRecord);
+ mImpl.enqueueBroadcastLocked(batteryChangedRecord);
+
+ final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED,
+ getUidForPackage(PACKAGE_RED));
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_BLOCKED, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ true /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
+ greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ // Once the broadcasts to green process are deferred, broadcasts to red process
+ // shouldn't be blocked anymore.
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to green process should be deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ final Intent packageChanged = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+ final BroadcastOptions optionsPackageChanged =
+ BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord packageChangedRecord = makeBroadcastRecord(packageChanged,
+ optionsPackageChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 0)), false /* ordered */);
+ mImpl.enqueueBroadcastLocked(packageChangedRecord);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
+ greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to the green process, including the newly enqueued one, should be
+ // deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ false /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ }
+
+ // TODO: Reuse BroadcastQueueTest.makeActiveProcessRecord()
+ private ProcessRecord makeProcessRecord(ApplicationInfo info) {
+ final ProcessRecord r = spy(new ProcessRecord(mAms, info, info.processName, info.uid));
+ r.setPid(mNextPid.incrementAndGet());
+ return r;
+ }
+
+ BroadcastFilter makeRegisteredReceiver(ProcessRecord app, int priority) {
+ final IIntentReceiver receiver = mock(IIntentReceiver.class);
+ final ReceiverList receiverList = new ReceiverList(mAms, app, app.getPid(), app.info.uid,
+ UserHandle.getUserId(app.info.uid), receiver);
+ return makeRegisteredReceiver(receiverList, priority);
+ }
+
private Intent createPackageChangedIntent(int uid, List<String> componentNameList) {
final Intent packageChangedIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
packageChangedIntent.putExtra(Intent.EXTRA_UID, uid);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 1c8e949..3364545 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -62,58 +62,36 @@
import android.app.IApplicationThread;
import android.app.UidObserver;
import android.app.usage.UsageEvents.Event;
-import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
-import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.DeadObjectException;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.PowerExemptionManager;
import android.os.SystemClock;
-import android.os.TestLooperManager;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
import android.util.Pair;
-import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.AlarmManagerInternal;
-import com.android.server.DropBoxManagerInternal;
-import com.android.server.LocalServices;
-import com.android.server.am.ActivityManagerService.Injector;
-import com.android.server.appop.AppOpsService;
-import com.android.server.wm.ActivityTaskManagerService;
-
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.mockito.ArgumentMatcher;
import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
import org.mockito.verification.VerificationMode;
-import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.Writer;
@@ -126,7 +104,6 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.UnaryOperator;
@@ -136,13 +113,9 @@
@MediumTest
@RunWith(Parameterized.class)
@SuppressWarnings("GuardedBy")
-public class BroadcastQueueTest {
+public class BroadcastQueueTest extends BaseBroadcastQueueTest {
private static final String TAG = "BroadcastQueueTest";
- @Rule
- public final ApplicationExitInfoTest.ServiceThreadRule
- mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
-
private final Impl mImpl;
private enum Impl {
@@ -150,30 +123,8 @@
MODERN,
}
- private Context mContext;
- private HandlerThread mHandlerThread;
- private TestLooperManager mLooper;
- private AtomicInteger mNextPid;
-
- @Mock
- private AppOpsService mAppOpsService;
- @Mock
- private ProcessList mProcessList;
- @Mock
- private DropBoxManagerInternal mDropBoxManagerInt;
- @Mock
- private PackageManagerInternal mPackageManagerInt;
- @Mock
- private UsageStatsManagerInternal mUsageStatsManagerInt;
- @Mock
- private AlarmManagerInternal mAlarmManagerInt;
-
- private ActivityManagerService mAms;
private BroadcastQueue mQueue;
- BroadcastConstants mConstants;
- private BroadcastSkipPolicy mSkipPolicy;
private UidObserver mUidObserver;
- private UidObserver mUidCachedStateObserver;
/**
* Desired behavior of the next
@@ -183,11 +134,6 @@
ProcessStartBehavior.SUCCESS);
/**
- * Map from PID to registered registered runtime receivers.
- */
- private SparseArray<ReceiverList> mRegisteredReceivers = new SparseArray<>();
-
- /**
* Collection of all active processes during current test run.
*/
private List<ProcessRecord> mActiveProcesses = new ArrayList<>();
@@ -208,41 +154,8 @@
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
+ super.setUp();
- mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
- mHandlerThread = new HandlerThread(TAG);
- mHandlerThread.start();
-
- // Pause all event processing until a test chooses to resume
- mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
- .acquireLooperManager(mHandlerThread.getLooper()));
-
- mNextPid = new AtomicInteger(100);
-
- LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
- LocalServices.addService(DropBoxManagerInternal.class, mDropBoxManagerInt);
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
- LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
- LocalServices.removeServiceForTest(AlarmManagerInternal.class);
- LocalServices.addService(AlarmManagerInternal.class, mAlarmManagerInt);
- doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
- doNothing().when(mPackageManagerInt).notifyComponentUsed(any(), anyInt(), any(), any());
- doAnswer((invocation) -> {
- return getUidForPackage(invocation.getArgument(0));
- }).when(mPackageManagerInt).getPackageUid(any(), anyLong(), eq(UserHandle.USER_SYSTEM));
-
- final ActivityManagerService realAms = new ActivityManagerService(
- new TestInjector(mContext), mServiceThreadRule.getThread());
- realAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
- realAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
- realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
- realAms.mOomAdjuster = spy(realAms.mOomAdjuster);
- realAms.mPackageManagerInt = mPackageManagerInt;
- realAms.mUsageStatsService = mUsageStatsManagerInt;
- realAms.mProcessesReady = true;
- mAms = spy(realAms);
doAnswer((invocation) -> {
Log.v(TAG, "Intercepting startProcessLocked() for "
+ Arrays.toString(invocation.getArguments()));
@@ -321,21 +234,11 @@
return null;
}).when(mAms).registerUidObserver(any(), anyInt(),
eq(ActivityManager.PROCESS_STATE_TOP), any());
- doAnswer((invocation) -> {
- mUidCachedStateObserver = invocation.getArgument(0);
- return null;
- }).when(mAms).registerUidObserver(any(), anyInt(),
- eq(ActivityManager.PROCESS_STATE_LAST_ACTIVITY), any());
- mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
mConstants.TIMEOUT = 200;
mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0;
mConstants.PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 500;
- mSkipPolicy = spy(new BroadcastSkipPolicy(mAms));
- doReturn(null).when(mSkipPolicy).shouldSkipMessage(any(), any());
- doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
-
final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
public void addBroadcastToHistoryLocked(BroadcastRecord original) {
// Ignored
@@ -358,7 +261,7 @@
@After
public void tearDown() throws Exception {
- mHandlerThread.quit();
+ super.tearDown();
// Verify that all processes have finished handling broadcasts
for (ProcessRecord app : mActiveProcesses) {
@@ -369,26 +272,9 @@
}
}
- private class TestInjector extends Injector {
- TestInjector(Context context) {
- super(context);
- }
-
- @Override
- public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile,
- Handler handler) {
- return mAppOpsService;
- }
-
- @Override
- public Handler getUiHandler(ActivityManagerService service) {
- return mHandlerThread.getThreadHandler();
- }
-
- @Override
- public ProcessList getProcessList(ActivityManagerService service) {
- return mProcessList;
- }
+ @Override
+ public String getTag() {
+ return TAG;
}
private enum ProcessStartBehavior {
@@ -534,62 +420,6 @@
return Pair.create(app.getPid(), intent.getAction());
}
- static ApplicationInfo makeApplicationInfo(String packageName) {
- return makeApplicationInfo(packageName, packageName, UserHandle.USER_SYSTEM);
- }
-
- static ApplicationInfo makeApplicationInfo(String packageName, String processName, int userId) {
- final ApplicationInfo ai = new ApplicationInfo();
- ai.packageName = packageName;
- ai.processName = processName;
- ai.uid = getUidForPackage(packageName, userId);
- return ai;
- }
-
- static ResolveInfo withPriority(ResolveInfo info, int priority) {
- info.priority = priority;
- return info;
- }
-
- static BroadcastFilter withPriority(BroadcastFilter filter, int priority) {
- filter.setPriority(priority);
- return filter;
- }
-
- static ResolveInfo makeManifestReceiver(String packageName, String name) {
- return makeManifestReceiver(packageName, name, UserHandle.USER_SYSTEM);
- }
-
- static ResolveInfo makeManifestReceiver(String packageName, String name, int userId) {
- return makeManifestReceiver(packageName, packageName, name, userId);
- }
-
- static ResolveInfo makeManifestReceiver(String packageName, String processName, String name,
- int userId) {
- final ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = new ActivityInfo();
- ri.activityInfo.packageName = packageName;
- ri.activityInfo.processName = processName;
- ri.activityInfo.name = name;
- ri.activityInfo.applicationInfo = makeApplicationInfo(packageName, processName, userId);
- return ri;
- }
-
- private BroadcastFilter makeRegisteredReceiver(ProcessRecord app) {
- return makeRegisteredReceiver(app, 0);
- }
-
- private BroadcastFilter makeRegisteredReceiver(ProcessRecord app, int priority) {
- final ReceiverList receiverList = mRegisteredReceivers.get(app.getPid());
- final IntentFilter filter = new IntentFilter();
- filter.setPriority(priority);
- final BroadcastFilter res = new BroadcastFilter(filter, receiverList,
- receiverList.app.info.packageName, null, null, null, receiverList.uid,
- receiverList.userId, false, false, true);
- receiverList.add(res);
- return res;
- }
-
private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp,
List<Object> receivers) {
return makeBroadcastRecord(intent, callerApp, BroadcastOptions.makeBasic(),
@@ -776,41 +606,6 @@
eq(userId), anyInt(), anyInt(), any());
}
- static final int USER_GUEST = 11;
-
- static final String PACKAGE_ANDROID = "android";
- static final String PACKAGE_PHONE = "com.android.phone";
- static final String PACKAGE_RED = "com.example.red";
- static final String PACKAGE_GREEN = "com.example.green";
- static final String PACKAGE_BLUE = "com.example.blue";
- static final String PACKAGE_YELLOW = "com.example.yellow";
- static final String PACKAGE_ORANGE = "com.example.orange";
-
- static final String PROCESS_SYSTEM = "system";
-
- static final String CLASS_RED = "com.example.red.Red";
- static final String CLASS_GREEN = "com.example.green.Green";
- static final String CLASS_BLUE = "com.example.blue.Blue";
- static final String CLASS_YELLOW = "com.example.yellow.Yellow";
- static final String CLASS_ORANGE = "com.example.orange.Orange";
-
- static int getUidForPackage(@NonNull String packageName) {
- switch (packageName) {
- case PACKAGE_ANDROID: return android.os.Process.SYSTEM_UID;
- case PACKAGE_PHONE: return android.os.Process.PHONE_UID;
- case PACKAGE_RED: return android.os.Process.FIRST_APPLICATION_UID + 1;
- case PACKAGE_GREEN: return android.os.Process.FIRST_APPLICATION_UID + 2;
- case PACKAGE_BLUE: return android.os.Process.FIRST_APPLICATION_UID + 3;
- case PACKAGE_YELLOW: return android.os.Process.FIRST_APPLICATION_UID + 4;
- case PACKAGE_ORANGE: return android.os.Process.FIRST_APPLICATION_UID + 5;
- default: throw new IllegalArgumentException();
- }
- }
-
- static int getUidForPackage(@NonNull String packageName, int userId) {
- return UserHandle.getUid(userId, getUidForPackage(packageName));
- }
-
/**
* Baseline verification of common debugging infrastructure, mostly to make
* sure it doesn't crash.
diff --git a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index df46054..1838fe8 100644
--- a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -1081,7 +1081,7 @@
assertThat(result.powerMonitors).isNotNull();
assertThat(Arrays.stream(result.powerMonitors).map(PowerMonitor::getName).toList())
.containsAtLeast(
- "energyconsumer0",
+ "ENERGYCONSUMER0",
"BLUETOOTH/1",
"[channelname0]:channelsubsystem0",
"[channelname1]:channelsubsystem1");
@@ -1131,7 +1131,7 @@
Map<String, PowerMonitor> map =
Arrays.stream(supportedPowerMonitorsResult.powerMonitors)
.collect(Collectors.toMap(PowerMonitor::getName, pm -> pm));
- PowerMonitor consumer1 = map.get("energyconsumer0");
+ PowerMonitor consumer1 = map.get("ENERGYCONSUMER0");
PowerMonitor consumer2 = map.get("BLUETOOTH/1");
PowerMonitor measurement1 = map.get("[channelname0]:channelsubsystem0");
PowerMonitor measurement2 = map.get("[channelname1]:channelsubsystem1");
@@ -1196,6 +1196,6 @@
supportedPowerMonitorsResult = new GetSupportedPowerMonitorsResult();
mService.getSupportedPowerMonitorsImpl(supportedPowerMonitorsResult);
assertThat(Arrays.stream(supportedPowerMonitorsResult.powerMonitors)
- .map(PowerMonitor::getName).toList()).contains("energyconsumer0");
+ .map(PowerMonitor::getName).toList()).contains("ENERGYCONSUMER0");
}
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 2598a6b..cdff623 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -259,8 +259,6 @@
@Mock
private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
@Mock
- private VirtualDeviceManagerInternal.VirtualDisplayListener mDisplayListener;
- @Mock
private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener mAppsOnVirtualDeviceListener;
@Mock
IPowerManager mIPowerManagerMock;
@@ -724,28 +722,6 @@
}
@Test
- public void onVirtualDisplayCreatedLocked_listenersNotified() {
- mLocalService.registerVirtualDisplayListener(mDisplayListener);
-
- mLocalService.onVirtualDisplayCreated(DISPLAY_ID_1);
- TestableLooper.get(this).processAllMessages();
-
- verify(mDisplayListener).onVirtualDisplayCreated(DISPLAY_ID_1);
- }
-
- @Test
- public void onVirtualDisplayRemovedLocked_listenersNotified() {
- mLocalService.registerVirtualDisplayListener(mDisplayListener);
-
- addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
-
- mLocalService.onVirtualDisplayRemoved(mDeviceImpl, DISPLAY_ID_1);
- TestableLooper.get(this).processAllMessages();
-
- verify(mDisplayListener).onVirtualDisplayRemoved(DISPLAY_ID_1);
- }
-
- @Test
public void onAppsOnVirtualDeviceChanged_singleVirtualDevice_listenersNotified() {
ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2));
mLocalService.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index 4406d83..ea11395 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -426,7 +426,7 @@
}
@Test
- public void testOnPackageChanged_removingDisallowedPackage() {
+ public void testOnPackageChanged_removingPackage_removeFromDisallowed() {
NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
VersionedPackage a1 = new VersionedPackage("pkg1", 243);
NotificationListenerFilter nlf2 =
@@ -440,6 +440,25 @@
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0))
.getDisallowedPackages()).isEmpty();
+ assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))
+ .getDisallowedPackages()).isEmpty();
+ }
+
+ @Test
+ public void testOnPackageChanged_notRemovingPackage_staysInDisallowed() {
+ NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+ VersionedPackage a1 = new VersionedPackage("pkg1", 243);
+ NotificationListenerFilter nlf2 =
+ new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1}));
+ mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
+ mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf2);
+
+ String[] pkgs = new String[] {"pkg1"};
+ int[] uids = new int[] {243};
+ mListeners.onPackagesChanged(false, pkgs, uids);
+
+ assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))
+ .getDisallowedPackages()).contains(a1);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 6792cfe..3803244 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -43,6 +43,7 @@
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MAX;
import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
@@ -72,6 +73,7 @@
import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
@@ -163,6 +165,7 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.AssociationInfo;
+import android.companion.AssociationRequest;
import android.companion.ICompanionDeviceManager;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.BroadcastReceiver;
@@ -3820,6 +3823,7 @@
when(mCompanionMgr.getAssociations(PKG, mUserId))
.thenReturn(singletonList(mock(AssociationInfo.class)));
mListener = mock(ManagedServices.ManagedServiceInfo.class);
+ mListener.component = new ComponentName(PKG, PKG);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
@@ -3870,6 +3874,7 @@
when(mCompanionMgr.getAssociations(PKG, mUserId))
.thenReturn(emptyList());
mListener = mock(ManagedServices.ManagedServiceInfo.class);
+ mListener.component = new ComponentName(PKG, PKG);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
try {
@@ -12777,6 +12782,145 @@
verify(mSnoozeHelper).clearData(anyInt());
}
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setNotificationPolicy_mappedToImplicitRule() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mService.setCallerIsNormalPackage();
+ ZenModeHelper zenHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+
+ NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
+ mBinderService.setNotificationPolicy("package", policy);
+
+ verify(zenHelper).applyGlobalPolicyAsImplicitZenRule(eq("package"), anyInt(), eq(policy));
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setNotificationPolicy_systemCaller_setsGlobalPolicy() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenModeHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+ mService.isSystemUid = true;
+
+ NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
+ mBinderService.setNotificationPolicy("package", policy);
+
+ verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyBoolean());
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setNotificationPolicy_watchCompanionApp_setsGlobalPolicy() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mService.setCallerIsNormalPackage();
+ ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenModeHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+ when(mCompanionMgr.getAssociations(anyString(), anyInt()))
+ .thenReturn(ImmutableList.of(
+ new AssociationInfo.Builder(1, mUserId, "package")
+ .setDisplayName("My watch")
+ .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH)
+ .build()));
+
+ NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
+ mBinderService.setNotificationPolicy("package", policy);
+
+ verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyBoolean());
+ }
+
+ @Test
+ @DisableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setNotificationPolicy_withoutCompat_setsGlobalPolicy() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mService.setCallerIsNormalPackage();
+ ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenModeHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+
+ NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
+ mBinderService.setNotificationPolicy("package", policy);
+
+ verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyBoolean());
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void getNotificationPolicy_mappedFromImplicitRule() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mService.setCallerIsNormalPackage();
+ ZenModeHelper zenHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+
+ mBinderService.getNotificationPolicy("package");
+
+ verify(zenHelper).getNotificationPolicyFromImplicitZenRule(eq("package"));
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setInterruptionFilter_mappedToImplicitRule() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mService.setCallerIsNormalPackage();
+ ZenModeHelper zenHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+
+ mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
+
+ verify(zenHelper).applyGlobalZenModeAsImplicitZenRule(eq("package"), anyInt(),
+ eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS));
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setInterruptionFilter_systemCaller_setsGlobalPolicy() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mService.setCallerIsNormalPackage();
+ ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenModeHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+ mService.isSystemUid = true;
+
+ mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
+
+ verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
+ eq("package"), anyString(), anyInt(), anyBoolean());
+ }
+
+ @Test
+ @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+ public void setInterruptionFilter_watchCompanionApp_setsGlobalPolicy() throws RemoteException {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+ mService.mZenModeHelper = zenModeHelper;
+ when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+ .thenReturn(true);
+ when(mCompanionMgr.getAssociations(anyString(), anyInt()))
+ .thenReturn(ImmutableList.of(
+ new AssociationInfo.Builder(1, mUserId, "package")
+ .setDisplayName("My watch")
+ .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH)
+ .build()));
+
+ mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
+
+ verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
+ eq("package"), anyString(), anyInt(), anyBoolean());
+ }
+
private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName)
throws RemoteException {
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, testName, mUid, 0,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 47f15b8..1e3b728 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -19,6 +19,7 @@
import static com.android.server.notification.SnoozeHelper.EXTRA_KEY;
import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -73,6 +74,14 @@
public class SnoozeHelperTest extends UiServiceTestCase {
private static final String TEST_CHANNEL_ID = "test_channel_id";
+ private static final String XML_TAG_NAME = "snoozed-notifications";
+ private static final String XML_SNOOZED_NOTIFICATION = "notification";
+ private static final String XML_SNOOZED_NOTIFICATION_CONTEXT = "context";
+ private static final String XML_SNOOZED_NOTIFICATION_KEY = "key";
+ private static final String XML_SNOOZED_NOTIFICATION_TIME = "time";
+ private static final String XML_SNOOZED_NOTIFICATION_CONTEXT_ID = "id";
+ private static final String XML_SNOOZED_NOTIFICATION_VERSION_LABEL = "version";
+
@Mock SnoozeHelper.Callback mCallback;
@Mock AlarmManager mAm;
@Mock ManagedServices.UserProfiles mUserProfiles;
@@ -316,6 +325,53 @@
}
@Test
+ public void testSnoozeLimit_maximumPersisted() throws XmlPullParserException, IOException {
+ final long snoozeTimeout = 1234;
+ final String snoozeContext = "ctx";
+ // Serialize & deserialize notifications so that only persisted lists are used
+ TypedXmlSerializer serializer = Xml.newFastSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ serializer.startTag(null, XML_TAG_NAME);
+ // Serialize maximum number of timed + context snoozed notifications, half of each
+ for (int i = 0; i < CONCURRENT_SNOOZE_LIMIT; i++) {
+ final boolean timedNotification = i % 2 == 0;
+ if (timedNotification) {
+ serializer.startTag(null, XML_SNOOZED_NOTIFICATION);
+ } else {
+ serializer.startTag(null, XML_SNOOZED_NOTIFICATION_CONTEXT);
+ }
+ serializer.attributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, 1);
+ serializer.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, "key" + i);
+ if (timedNotification) {
+ serializer.attributeLong(null, XML_SNOOZED_NOTIFICATION_TIME, snoozeTimeout);
+ serializer.endTag(null, XML_SNOOZED_NOTIFICATION);
+ } else {
+ serializer.attribute(null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID, snoozeContext);
+ serializer.endTag(null, XML_SNOOZED_NOTIFICATION_CONTEXT);
+ }
+ }
+ serializer.endTag(null, XML_TAG_NAME);
+ serializer.endDocument();
+ serializer.flush();
+
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), "utf-8");
+ mSnoozeHelper.readXml(parser, 1);
+ // Verify that we can't snooze any more notifications
+ // and that the limit is caused by persisted notifications
+ assertThat(mSnoozeHelper.canSnooze(1)).isFalse();
+ assertThat(mSnoozeHelper.isSnoozed(UserHandle.USER_SYSTEM, "pkg", "key0")).isFalse();
+ assertThat(mSnoozeHelper.getSnoozeTimeForUnpostedNotification(UserHandle.USER_SYSTEM,
+ "pkg", "key0")).isEqualTo(snoozeTimeout);
+ assertThat(
+ mSnoozeHelper.getSnoozeContextForUnpostedNotification(UserHandle.USER_SYSTEM, "pkg",
+ "key1")).isEqualTo(snoozeContext);
+ }
+
+ @Test
public void testCancelByApp() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
@@ -611,6 +667,7 @@
@Test
public void repostGroupSummary_repostsSummary() throws Exception {
+ final int snoozeDuration = 1000;
IntArray profileIds = new IntArray();
profileIds.add(UserHandle.USER_SYSTEM);
when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
@@ -618,10 +675,14 @@
"pkg", 1, "one", UserHandle.SYSTEM, "group1", true);
NotificationRecord r2 = getNotificationRecord(
"pkg", 2, "two", UserHandle.SYSTEM, "group1", false);
- mSnoozeHelper.snooze(r, 1000);
- mSnoozeHelper.snooze(r2, 1000);
+ final long snoozeTime = System.currentTimeMillis() + snoozeDuration;
+ mSnoozeHelper.snooze(r, snoozeDuration);
+ mSnoozeHelper.snooze(r2, snoozeDuration);
assertEquals(2, mSnoozeHelper.getSnoozed().size());
assertEquals(2, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
+ // Verify that summary notification was added to the persisted list
+ assertThat(mSnoozeHelper.getSnoozeTimeForUnpostedNotification(UserHandle.USER_SYSTEM, "pkg",
+ r.getKey())).isAtLeast(snoozeTime);
mSnoozeHelper.repostGroupSummary("pkg", UserHandle.USER_SYSTEM, r.getGroupKey());
@@ -630,6 +691,39 @@
assertEquals(1, mSnoozeHelper.getSnoozed().size());
assertEquals(1, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
+ // Verify that summary notification was removed from the persisted list
+ assertThat(mSnoozeHelper.getSnoozeTimeForUnpostedNotification(UserHandle.USER_SYSTEM, "pkg",
+ r.getKey())).isEqualTo(0);
+ }
+
+ @Test
+ public void snoozeWithContext_repostGroupSummary_removesPersisted() throws Exception {
+ final String snoozeContext = "zzzzz";
+ IntArray profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_SYSTEM);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
+ NotificationRecord r = getNotificationRecord(
+ "pkg", 1, "one", UserHandle.SYSTEM, "group1", true);
+ NotificationRecord r2 = getNotificationRecord(
+ "pkg", 2, "two", UserHandle.SYSTEM, "group1", false);
+ mSnoozeHelper.snooze(r, snoozeContext);
+ mSnoozeHelper.snooze(r2, snoozeContext);
+ assertEquals(2, mSnoozeHelper.getSnoozed().size());
+ assertEquals(2, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
+ // Verify that summary notification was added to the persisted list
+ assertThat(mSnoozeHelper.getSnoozeContextForUnpostedNotification(UserHandle.USER_SYSTEM,
+ "pkg", r.getKey())).isEqualTo(snoozeContext);
+
+ mSnoozeHelper.repostGroupSummary("pkg", UserHandle.USER_SYSTEM, r.getGroupKey());
+
+ verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r, false);
+ verify(mCallback, never()).repost(UserHandle.USER_SYSTEM, r2, false);
+
+ assertEquals(1, mSnoozeHelper.getSnoozed().size());
+ assertEquals(1, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
+ // Verify that summary notification was removed from the persisted list
+ assertThat(mSnoozeHelper.getSnoozeContextForUnpostedNotification(UserHandle.USER_SYSTEM,
+ "pkg", r.getKey())).isNull();
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index 27e8f36..8f30f41 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -54,6 +54,15 @@
return mRankingHelper;
}
+ /**
+ * Sets {@link #isSystemUid} and {@link #isSystemAppId} to {@code false}, so that calls to NMS
+ * methods don't succeed {@link #isCallingUidSystem()} and similar checks.
+ */
+ void setCallerIsNormalPackage() {
+ isSystemUid = false;
+ isSystemAppId = false;
+ }
+
@Override
protected boolean isCallingUidSystem() {
countSystemChecks++;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
new file mode 100644
index 0000000..6cc1c43
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.notification;
+
+import static com.android.server.notification.ZenAdapters.notificationPolicyToZenPolicy;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.NotificationManager.Policy;
+import android.service.notification.ZenPolicy;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ZenAdaptersTest extends UiServiceTestCase {
+
+ @Test
+ public void notificationPolicyToZenPolicy_allCallers() {
+ Policy policy = new Policy(Policy.PRIORITY_CATEGORY_CALLS, Policy.PRIORITY_SENDERS_ANY, 0);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getPriorityCategoryCalls()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ assertThat(zenPolicy.getPriorityCallSenders()).isEqualTo(ZenPolicy.PEOPLE_TYPE_ANYONE);
+ }
+
+ @Test
+ public void notificationPolicyToZenPolicy_starredCallers() {
+ Policy policy = new Policy(Policy.PRIORITY_CATEGORY_CALLS, Policy.PRIORITY_SENDERS_STARRED,
+ 0);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getPriorityCategoryCalls()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ assertThat(zenPolicy.getPriorityCallSenders()).isEqualTo(ZenPolicy.PEOPLE_TYPE_STARRED);
+ }
+
+ @Test
+ public void notificationPolicyToZenPolicy_repeatCallers() {
+ Policy policy = new Policy(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getPriorityCategoryCalls()).isEqualTo(ZenPolicy.STATE_DISALLOW);
+ assertThat(zenPolicy.getPriorityCategoryRepeatCallers()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ assertThat(zenPolicy.getPriorityCallSenders()).isEqualTo(ZenPolicy.PEOPLE_TYPE_NONE);
+ }
+
+ @Test
+ public void notificationPolicyToZenPolicy_noCallers() {
+ Policy policy = new Policy(0, 0, 0);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getPriorityCategoryCalls()).isEqualTo(ZenPolicy.STATE_DISALLOW);
+ assertThat(zenPolicy.getPriorityCallSenders()).isEqualTo(ZenPolicy.PEOPLE_TYPE_NONE);
+ }
+
+ @Test
+ public void notificationPolicyToZenPolicy_conversationsAllowedSendersUnset() {
+ Policy policy = new Policy(Policy.PRIORITY_CATEGORY_CONVERSATIONS, 0, 0);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getPriorityCategoryConversations()).isEqualTo(ZenPolicy.STATE_UNSET);
+ }
+
+ @Test
+ public void notificationPolicyToZenPolicy_conversationsNotAllowedSendersUnset() {
+ Policy policy = new Policy(0, 0, 0);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getPriorityCategoryConversations()).isEqualTo(
+ ZenPolicy.STATE_DISALLOW);
+ }
+
+ @Test
+ public void notificationPolicyToZenPolicy_setEffects() {
+ Policy policy = new Policy(0, 0, 0,
+ Policy.SUPPRESSED_EFFECT_BADGE | Policy.SUPPRESSED_EFFECT_LIGHTS);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getVisualEffectBadge()).isEqualTo(ZenPolicy.STATE_DISALLOW);
+ assertThat(zenPolicy.getVisualEffectLights()).isEqualTo(ZenPolicy.STATE_DISALLOW);
+
+ assertThat(zenPolicy.getVisualEffectAmbient()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ assertThat(zenPolicy.getVisualEffectFullScreenIntent()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ assertThat(zenPolicy.getVisualEffectNotificationList()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ assertThat(zenPolicy.getVisualEffectPeek()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ assertThat(zenPolicy.getVisualEffectStatusBar()).isEqualTo(ZenPolicy.STATE_ALLOW);
+ }
+
+ @Test
+ public void notificationPolicyToZenPolicy_unsetEffects() {
+ Policy policy = new Policy(0, 0, 0);
+
+ ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+ assertThat(zenPolicy.getVisualEffectAmbient()).isEqualTo(ZenPolicy.STATE_UNSET);
+ assertThat(zenPolicy.getVisualEffectBadge()).isEqualTo(ZenPolicy.STATE_UNSET);
+ assertThat(zenPolicy.getVisualEffectFullScreenIntent()).isEqualTo(ZenPolicy.STATE_UNSET);
+ assertThat(zenPolicy.getVisualEffectLights()).isEqualTo(ZenPolicy.STATE_UNSET);
+ assertThat(zenPolicy.getVisualEffectNotificationList()).isEqualTo(ZenPolicy.STATE_UNSET);
+ assertThat(zenPolicy.getVisualEffectPeek()).isEqualTo(ZenPolicy.STATE_UNSET);
+ assertThat(zenPolicy.getVisualEffectStatusBar()).isEqualTo(ZenPolicy.STATE_UNSET);
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index e8201fd..37aeb57 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -22,6 +22,7 @@
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
@@ -32,6 +33,7 @@
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
+import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
@@ -40,8 +42,11 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_OFF;
import static android.service.notification.Condition.STATE_FALSE;
import static android.service.notification.Condition.STATE_TRUE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS;
import static com.android.os.dnd.DNDProtoEnums.PEOPLE_STARRED;
@@ -50,6 +55,8 @@
import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW;
import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -72,6 +79,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -82,6 +90,7 @@
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
@@ -92,6 +101,7 @@
import android.media.AudioSystem;
import android.media.VolumePolicy;
import android.net.Uri;
+import android.os.Parcel;
import android.os.Process;
import android.os.UserHandle;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -100,6 +110,7 @@
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ScheduleInfo;
+import android.service.notification.ZenModeConfig.ZenRule;
import android.service.notification.ZenModeDiff;
import android.service.notification.ZenPolicy;
import android.test.suitebuilder.annotation.SmallTest;
@@ -124,6 +135,7 @@
import com.android.server.notification.ManagedServices.UserProfiles;
import com.google.common.collect.ImmutableList;
+import com.google.common.truth.Correspondence;
import com.google.protobuf.InvalidProtocolBufferException;
import org.junit.Before;
@@ -157,27 +169,29 @@
private static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
private static final String SCHEDULE_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
private static final String CUSTOM_PKG_NAME = "not.android";
+ private static final String CUSTOM_APP_LABEL = "This is not Android";
private static final int CUSTOM_PKG_UID = 1;
private static final String CUSTOM_RULE_ID = "custom_rule";
- private final String NAME = "name";
- private final ComponentName OWNER = new ComponentName("pkg", "cls");
- private final ComponentName CONFIG_ACTIVITY = new ComponentName("pkg", "act");
- private final ZenPolicy POLICY = new ZenPolicy.Builder().allowAlarms(true).build();
- private final Uri CONDITION_ID = new Uri.Builder().scheme("scheme")
+ private static final String NAME = "name";
+ private static final ComponentName OWNER = new ComponentName("pkg", "cls");
+ private static final ComponentName CONFIG_ACTIVITY = new ComponentName("pkg", "act");
+ private static final ZenPolicy POLICY = new ZenPolicy.Builder().allowAlarms(true).build();
+ private static final Uri CONDITION_ID = new Uri.Builder().scheme("scheme")
.authority("authority")
.appendPath("path")
.appendPath("test")
.build();
- private final Condition CONDITION = new Condition(CONDITION_ID, "", Condition.STATE_TRUE);
- private final String TRIGGER_DESC = "Every Night, 10pm to 6am";
- private final int TYPE = TYPE_BEDTIME;
- private final boolean ALLOW_MANUAL = true;
- private final int ICON_RES_ID = 1234;
- private final int INTERRUPTION_FILTER = Settings.Global.ZEN_MODE_ALARMS;
- private final boolean ENABLED = true;
- private final int CREATION_TIME = 123;
+ private static final Condition CONDITION = new Condition(CONDITION_ID, "",
+ Condition.STATE_TRUE);
+ private static final String TRIGGER_DESC = "Every Night, 10pm to 6am";
+ private static final int TYPE = TYPE_BEDTIME;
+ private static final boolean ALLOW_MANUAL = true;
+ private static final int ICON_RES_ID = 1234;
+ private static final int INTERRUPTION_FILTER = Settings.Global.ZEN_MODE_ALARMS;
+ private static final boolean ENABLED = true;
+ private static final int CREATION_TIME = 123;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -227,6 +241,10 @@
.thenReturn(CUSTOM_PKG_UID);
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
new String[] {pkg});
+ ApplicationInfo mockAppInfo = mock(ApplicationInfo.class);
+ when(mockAppInfo.loadLabel(any())).thenReturn(CUSTOM_APP_LABEL);
+ when(mPackageManager.getApplicationInfo(eq(CUSTOM_PKG_NAME), anyInt()))
+ .thenReturn(mockAppInfo);
mZenModeHelper.mPm = mPackageManager;
mZenModeEventLogger.reset();
@@ -334,7 +352,7 @@
@Test
public void testZenOff_NoMuteApplied() {
- mZenModeHelper.mZenMode = Settings.Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
mZenModeHelper.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS
| PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
@@ -635,7 +653,7 @@
// 3. apply zen off - verify zen is set to previous ringer (normal)
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.applyZenToRingerMode();
verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -721,7 +739,7 @@
// 3. apply zen off - verify ringer remains normal
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.applyZenToRingerMode();
verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -746,7 +764,7 @@
// 3. apply zen-off - verify ringer is still silent
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.applyZenToRingerMode();
verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
mZenModeHelper.TAG);
@@ -781,7 +799,7 @@
// 4. apply zen off - verify ringer still silenced
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.applyZenToRingerMode();
verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
mZenModeHelper.TAG);
@@ -795,7 +813,7 @@
// apply zen off multiple times - verify ringer is not set to normal
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.mConfig = null; // will evaluate config to zen mode off
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
@@ -809,7 +827,7 @@
public void testSilentRingerSavedOnZenOff_startsZenOn() {
AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
mZenModeHelper.mAudioManager = mAudioManager;
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.mConfig = new ZenModeConfig();
// previously set silent ringer
@@ -836,7 +854,7 @@
public void testVibrateRingerSavedOnZenOff_startsZenOn() {
AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
mZenModeHelper.mAudioManager = mAudioManager;
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.mConfig = new ZenModeConfig();
// previously set silent ringer
@@ -1209,7 +1227,7 @@
.allowMedia(false)
.allowRepeatCallers(false)
.allowCalls(ZenPolicy.PEOPLE_TYPE_NONE)
- .allowMessages(ZenPolicy.PEOPLE_TYPE_CONTACTS)
+ .allowMessages(PEOPLE_TYPE_CONTACTS)
.allowEvents(true)
.allowReminders(false)
.build();
@@ -2023,10 +2041,10 @@
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeHelper.mZenMode);
// and also that it works to turn it back off again
- mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, null, "",
Process.SYSTEM_UID, true);
- assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
+ assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode);
}
@Test
@@ -2041,7 +2059,7 @@
// Now turn zen mode off, but via a different package UID -- this should get registered as
// "not an action by the user" because some other app is changing zen mode
- mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "", CUSTOM_PKG_UID,
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, null, "", CUSTOM_PKG_UID,
false);
// In total, this should be 2 loggable changes
@@ -2060,7 +2078,7 @@
// - resulting DNDPolicyProto the same as the values in setupZenConfig()
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
mZenModeEventLogger.getEventId(0));
- assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
+ assertEquals(ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getNewZenMode(0));
assertEquals(DNDProtoEnums.MANUAL_RULE, mZenModeEventLogger.getChangedRuleType(0));
assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
@@ -2080,7 +2098,7 @@
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_OFF.getId(),
mZenModeEventLogger.getEventId(1));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getPrevZenMode(1));
- assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getNewZenMode(1));
+ assertEquals(ZEN_MODE_OFF, mZenModeEventLogger.getNewZenMode(1));
assertEquals(DNDProtoEnums.MANUAL_RULE, mZenModeEventLogger.getChangedRuleType(1));
assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
assertFalse(mZenModeEventLogger.getIsUserAction(1));
@@ -2144,7 +2162,7 @@
// - zen policy is the same as the set-up zen config
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
mZenModeEventLogger.getEventId(0));
- assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
+ assertEquals(ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getNewZenMode(0));
assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(0));
assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
@@ -2157,7 +2175,7 @@
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_OFF.getId(),
mZenModeEventLogger.getEventId(1));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getPrevZenMode(1));
- assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getNewZenMode(1));
+ assertEquals(ZEN_MODE_OFF, mZenModeEventLogger.getNewZenMode(1));
assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(1));
assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
assertTrue(mZenModeEventLogger.getIsUserAction(1));
@@ -2201,7 +2219,7 @@
// Turn zen mode off; we want to make sure policy changes do not get logged when zen mode
// is off.
- mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, null, "",
Process.SYSTEM_UID, true);
// Change the policy again
@@ -2305,7 +2323,7 @@
// what the event should reflect. At this time, the policy is the same as initial setup.
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
mZenModeEventLogger.getEventId(0));
- assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
+ assertEquals(ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getNewZenMode(0));
assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
assertFalse(mZenModeEventLogger.getIsUserAction(0));
@@ -2355,7 +2373,7 @@
mZenModeHelper.evaluateZenModeLocked("test", true);
// Check that the change actually took: zen mode should be off now
- assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
+ assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode);
// but still, nothing should've been logged
assertEquals(0, mZenModeEventLogger.numLoggedChanges());
@@ -2483,7 +2501,7 @@
true);
// Turn off manual mode, call from a package: don't reset UID even though enabler is set
- mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null,
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null,
CUSTOM_PKG_NAME, "", 12345, false);
// And likewise when turning it back on again
@@ -2660,8 +2678,11 @@
}
@Test
- public void testCreateAutomaticZenRule_allFields() {
+ public void zenRuleToAutomaticZenRule_allFields() {
mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
+ new String[] {OWNER.getPackageName()});
+
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.configurationActivity = CONFIG_ACTIVITY;
rule.component = OWNER;
@@ -2682,7 +2703,8 @@
rule.iconResId = ICON_RES_ID;
rule.triggerDescription = TRIGGER_DESC;
- AutomaticZenRule actual = mZenModeHelper.createAutomaticZenRule(rule);
+ mZenModeHelper.mConfig.automaticRules.put(rule.id, rule);
+ AutomaticZenRule actual = mZenModeHelper.getAutomaticZenRule(rule.id);
assertEquals(NAME, actual.getName());
assertEquals(OWNER, actual.getOwner());
@@ -2915,8 +2937,253 @@
assertEquals(false, mZenModeHelper.mConfig.automaticRules.get(createdId).snoozing);
}
+ @Test
+ public void applyGlobalZenModeAsImplicitZenRule_createsImplicitRuleAndActivatesIt() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+ assertThat(mZenModeHelper.mConfig.automaticRules.values())
+ .comparingElementsUsing(IGNORE_TIMESTAMPS)
+ .containsExactly(
+ expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ null, true));
+ }
+
+ @Test
+ public void applyGlobalZenModeAsImplicitZenRule_updatesImplicitRuleAndActivatesIt() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, "test", "test", 0, true);
+ assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_ALARMS);
+
+ assertThat(mZenModeHelper.mConfig.automaticRules.values())
+ .comparingElementsUsing(IGNORE_TIMESTAMPS)
+ .containsExactly(
+ expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_ALARMS, null, true));
+ }
+
+ @Test
+ public void applyGlobalZenModeAsImplicitZenRule_modeOff_deactivatesImplicitRule() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
+ .isEqualTo(STATE_TRUE);
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_OFF);
+
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
+ .isEqualTo(STATE_FALSE);
+ }
+
+ @Test
+ public void applyGlobalZenModeAsImplicitZenRule_modeOffButNoPreviousRule_ignored() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_OFF);
+
+ assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
+ }
+
+ @Test
+ public void applyGlobalZenModeAsImplicitZenRule_update_unsnoozesRule() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isFalse();
+
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, "test", "test", 0, true);
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isTrue();
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_ALARMS);
+
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isFalse();
+ assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
+ .isEqualTo(STATE_TRUE);
+ }
+
+ @Test
+ public void applyGlobalZenModeAsImplicitZenRule_flagOff_ignored() {
+ mSetFlagsRule.disableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ withoutWtfCrash(
+ () -> mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME,
+ CUSTOM_PKG_UID,
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS));
+
+ assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
+ }
+
+ @Test
+ public void applyGlobalPolicyAsImplicitZenRule_createsImplicitRule() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ Policy policy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
+ PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
+ Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, policy);
+
+ ZenPolicy expectedZenPolicy = new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .allowCalls(PEOPLE_TYPE_CONTACTS)
+ .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
+ .hideAllVisualEffects()
+ .build();
+ assertThat(mZenModeHelper.mConfig.automaticRules.values())
+ .comparingElementsUsing(IGNORE_TIMESTAMPS)
+ .containsExactly(
+ expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ expectedZenPolicy, /* conditionActive= */ null));
+ }
+
+ @Test
+ public void applyGlobalPolicyAsImplicitZenRule_updatesImplicitRule() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ Policy original = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
+ PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
+ Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ original);
+
+ // Change priorityCallSenders: contacts -> starred.
+ Policy updated = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
+ PRIORITY_SENDERS_STARRED, PRIORITY_SENDERS_STARRED,
+ Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, updated);
+
+ ZenPolicy expectedZenPolicy = new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .allowCalls(PEOPLE_TYPE_STARRED)
+ .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
+ .hideAllVisualEffects()
+ .build();
+ assertThat(mZenModeHelper.mConfig.automaticRules.values())
+ .comparingElementsUsing(IGNORE_TIMESTAMPS)
+ .containsExactly(
+ expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ expectedZenPolicy, /* conditionActive= */ null));
+ }
+
+ @Test
+ public void applyGlobalPolicyAsImplicitZenRule_flagOff_ignored() {
+ mSetFlagsRule.disableFlags(android.app.Flags.FLAG_MODES_API);
+ mZenModeHelper.mConfig.automaticRules.clear();
+
+ withoutWtfCrash(
+ () -> mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME,
+ CUSTOM_PKG_UID, new Policy(0, 0, 0)));
+
+ assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
+ }
+
+ @Test
+ public void getNotificationPolicyFromImplicitZenRule_returnsSetPolicy() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+ Policy writtenPolicy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
+ PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
+ Policy.getAllSuppressedVisualEffects(), STATE_FALSE,
+ CONVERSATION_SENDERS_IMPORTANT);
+ mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ writtenPolicy);
+
+ Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
+ CUSTOM_PKG_NAME);
+
+ assertThat(readPolicy).isEqualTo(writtenPolicy);
+ }
+
+ @Test
+ public void getNotificationPolicyFromImplicitZenRule_ruleWithoutPolicy_returnsGlobalPolicy() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ ZEN_MODE_ALARMS);
+ mZenModeHelper.mConfig.allowCalls = true;
+ mZenModeHelper.mConfig.allowConversations = false;
+
+ Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
+ CUSTOM_PKG_NAME);
+
+ assertThat(readPolicy).isNotNull();
+ assertThat(readPolicy.allowCalls()).isTrue();
+ assertThat(readPolicy.allowConversations()).isFalse();
+ }
+
+ @Test
+ public void getNotificationPolicyFromImplicitZenRule_noImplicitRule_returnsGlobalPolicy() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+ mZenModeHelper.mConfig.allowCalls = true;
+ mZenModeHelper.mConfig.allowConversations = false;
+
+ Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
+ CUSTOM_PKG_NAME);
+
+ assertThat(readPolicy).isNotNull();
+ assertThat(readPolicy.allowCalls()).isTrue();
+ assertThat(readPolicy.allowConversations()).isFalse();
+ }
+
+ private static final Correspondence<ZenRule, ZenRule> IGNORE_TIMESTAMPS =
+ Correspondence.transforming(zr -> {
+ Parcel p = Parcel.obtain();
+ try {
+ zr.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ ZenRule copy = new ZenRule(p);
+ copy.creationTime = 0;
+ return copy;
+ } finally {
+ p.recycle();
+ }
+ },
+ "Ignoring timestamps");
+
+ private ZenRule expectedImplicitRule(String ownerPkg, int zenMode, ZenPolicy policy,
+ @Nullable Boolean conditionActive) {
+ ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.id = "implicit_" + ownerPkg;
+ rule.conditionId = Uri.parse("condition://android/implicit/" + ownerPkg);
+ if (conditionActive != null) {
+ rule.condition = conditionActive
+ ? new Condition(rule.conditionId,
+ mContext.getString(R.string.zen_mode_implicit_activated), STATE_TRUE)
+ : new Condition(rule.conditionId,
+ mContext.getString(R.string.zen_mode_implicit_deactivated),
+ STATE_FALSE);
+ }
+ rule.zenMode = zenMode;
+ rule.zenPolicy = policy;
+ rule.pkg = ownerPkg;
+ rule.name = CUSTOM_APP_LABEL;
+ rule.enabled = true;
+ return rule;
+ }
+
private void setupZenConfig() {
- mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
mZenModeHelper.mConfig.allowAlarms = false;
mZenModeHelper.mConfig.allowMedia = false;
mZenModeHelper.mConfig.allowSystem = false;
@@ -2965,6 +3232,15 @@
assertEquals(STATE_ALLOW, dndProto.getNotificationList().getNumber());
}
+ private static void withoutWtfCrash(Runnable test) {
+ Log.TerribleFailureHandler oldHandler = Log.setWtfHandler((tag, what, system) -> {});
+ try {
+ test.run();
+ } finally {
+ Log.setWtfHandler(oldHandler);
+ }
+ }
+
/**
* Wrapper to use TypedXmlPullParser as XmlResourceParser for Resources.getXml()
*/
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index 7a2bb5a..f080341 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -75,8 +75,6 @@
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
-import android.util.ArraySet;
-import android.view.Display;
import androidx.test.InstrumentationRegistry;
@@ -103,7 +101,7 @@
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private static final int UID = 1;
- private static final int VIRTUAL_DISPLAY_ID = 1;
+ private static final int VIRTUAL_DEVICE_ID = 1;
private static final String SYSUI_PACKAGE_NAME = "sysui";
private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
@@ -137,9 +135,6 @@
private VibrationSettings mVibrationSettings;
private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
private BroadcastReceiver mRegisteredBatteryBroadcastReceiver;
- private VirtualDeviceManagerInternal.VirtualDisplayListener mRegisteredVirtualDisplayListener;
- private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener
- mRegisteredAppsOnVirtualDeviceListener;
@Before
public void setUp() throws Exception {
@@ -155,14 +150,6 @@
}).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
when(mPackageManagerInternalMock.getSystemUiServiceComponent())
.thenReturn(new ComponentName(SYSUI_PACKAGE_NAME, ""));
- doAnswer(invocation -> {
- mRegisteredVirtualDisplayListener = invocation.getArgument(0);
- return null;
- }).when(mVirtualDeviceManagerInternalMock).registerVirtualDisplayListener(any());
- doAnswer(invocation -> {
- mRegisteredAppsOnVirtualDeviceListener = invocation.getArgument(0);
- return null;
- }).when(mVirtualDeviceManagerInternalMock).registerAppsOnVirtualDeviceListener(any());
removeServicesForTest();
addServicesForTest();
@@ -654,62 +641,20 @@
}
@Test
- public void shouldIgnoreVibrationFromVirtualDisplays_displayNonVirtual_neverIgnored() {
- // Vibrations from the primary display is never ignored regardless of the creation and
- // removal of virtual displays and of the changes of apps running on virtual displays.
- mRegisteredVirtualDisplayListener.onVirtualDisplayCreated(VIRTUAL_DISPLAY_ID);
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
- new ArraySet<>(Arrays.asList(UID)));
+ public void shouldIgnoreVibrationFromVirtualDevices_defaultDevice_neverIgnored() {
+ // Vibrations from the primary device is never ignored.
for (int usage : ALL_USAGES) {
- assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY);
- }
-
- mRegisteredVirtualDisplayListener.onVirtualDisplayRemoved(VIRTUAL_DISPLAY_ID);
- for (int usage : ALL_USAGES) {
- assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY);
- }
-
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(new ArraySet<>());
- for (int usage : ALL_USAGES) {
- assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY);
+ assertVibrationNotIgnoredForUsageAndDevice(usage, Context.DEVICE_ID_DEFAULT);
}
}
@Test
- public void shouldIgnoreVibrationFromVirtualDisplays_displayVirtual() {
- // Ignore the vibration when the coming display id represents a virtual display.
- mRegisteredVirtualDisplayListener.onVirtualDisplayCreated(VIRTUAL_DISPLAY_ID);
-
+ public void shouldIgnoreVibrationFromVirtualDevices_virtualDevice_alwaysIgnored() {
+ // Ignore the vibration when the coming device id represents a virtual device.
for (int usage : ALL_USAGES) {
- assertVibrationIgnoredForUsageAndDisplay(usage, VIRTUAL_DISPLAY_ID,
+ assertVibrationIgnoredForUsageAndDevice(usage, VIRTUAL_DEVICE_ID,
Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE);
}
-
- // Stop ignoring when the virtual display is removed.
- mRegisteredVirtualDisplayListener.onVirtualDisplayRemoved(VIRTUAL_DISPLAY_ID);
- for (int usage : ALL_USAGES) {
- assertVibrationNotIgnoredForUsageAndDisplay(usage, VIRTUAL_DISPLAY_ID);
- }
- }
-
-
- @Test
- public void shouldIgnoreVibrationFromVirtualDisplays_appsOnVirtualDisplay() {
- // Ignore when the passed-in display id is invalid and the calling uid is on a virtual
- // display.
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
- new ArraySet<>(Arrays.asList(UID)));
- for (int usage : ALL_USAGES) {
- assertVibrationIgnoredForUsageAndDisplay(usage, Display.INVALID_DISPLAY,
- Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE);
- }
-
- // Stop ignoring when the app is no longer on virtual display.
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(new ArraySet<>());
- for (int usage : ALL_USAGES) {
- assertVibrationNotIgnoredForUsageAndDisplay(usage, Display.INVALID_DISPLAY);
- }
-
}
@Test
@@ -932,13 +877,13 @@
private void assertVibrationIgnoredForUsage(@VibrationAttributes.Usage int usage,
Vibration.Status expectedStatus) {
- assertVibrationIgnoredForUsageAndDisplay(usage, Display.DEFAULT_DISPLAY, expectedStatus);
+ assertVibrationIgnoredForUsageAndDevice(usage, Context.DEVICE_ID_DEFAULT, expectedStatus);
}
- private void assertVibrationIgnoredForUsageAndDisplay(@VibrationAttributes.Usage int usage,
- int displayId, Vibration.Status expectedStatus) {
+ private void assertVibrationIgnoredForUsageAndDevice(@VibrationAttributes.Usage int usage,
+ int deviceId, Vibration.Status expectedStatus) {
Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(
- VibrationAttributes.createForUsage(usage), UID, displayId, null, null);
+ VibrationAttributes.createForUsage(usage), UID, deviceId, null, null);
assertEquals(errorMessageForUsage(usage), expectedStatus,
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
@@ -946,7 +891,7 @@
private void assertVibrationIgnoredForAttributes(VibrationAttributes attrs,
Vibration.Status expectedStatus) {
Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID,
- Display.DEFAULT_DISPLAY, null, null);
+ Context.DEVICE_ID_DEFAULT, null, null);
assertEquals(errorMessageForAttributes(attrs), expectedStatus,
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
@@ -957,27 +902,27 @@
private void assertVibrationNotIgnoredForUsageAndFlags(@VibrationAttributes.Usage int usage,
@VibrationAttributes.Flag int flags) {
- assertVibrationNotIgnoredForUsageAndFlagsAndDisplay(usage, Display.DEFAULT_DISPLAY, flags);
+ assertVibrationNotIgnoredForUsageAndFlagsAndDevice(usage, Context.DEVICE_ID_DEFAULT, flags);
}
- private void assertVibrationNotIgnoredForUsageAndDisplay(@VibrationAttributes.Usage int usage,
- int displayId) {
- assertVibrationNotIgnoredForUsageAndFlagsAndDisplay(usage, displayId, /* flags= */ 0);
+ private void assertVibrationNotIgnoredForUsageAndDevice(@VibrationAttributes.Usage int usage,
+ int deviceId) {
+ assertVibrationNotIgnoredForUsageAndFlagsAndDevice(usage, deviceId, /* flags= */ 0);
}
- private void assertVibrationNotIgnoredForUsageAndFlagsAndDisplay(
- @VibrationAttributes.Usage int usage, int displayId,
+ private void assertVibrationNotIgnoredForUsageAndFlagsAndDevice(
+ @VibrationAttributes.Usage int usage, int deviceId,
@VibrationAttributes.Flag int flags) {
Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(
new VibrationAttributes.Builder().setUsage(usage).setFlags(flags).build(), UID,
- displayId, null, null);
+ deviceId, null, null);
assertNull(errorMessageForUsage(usage),
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
private void assertVibrationNotIgnoredForAttributes(VibrationAttributes attrs) {
Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID,
- Display.DEFAULT_DISPLAY, null, null);
+ Context.DEVICE_ID_DEFAULT, null, null);
assertNull(errorMessageForAttributes(attrs),
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
@@ -1032,7 +977,7 @@
private Vibration.CallerInfo createCallerInfo(int uid, String opPkg,
@VibrationAttributes.Usage int usage) {
VibrationAttributes attrs = VibrationAttributes.createForUsage(usage);
- return new Vibration.CallerInfo(attrs, uid, VIRTUAL_DISPLAY_ID, opPkg, null);
+ return new Vibration.CallerInfo(attrs, uid, VIRTUAL_DEVICE_ID, opPkg, null);
}
private void setBatteryReceiverRegistrationResult(Intent result) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 085241f..b0aef47 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -88,7 +88,7 @@
private static final int TEST_TIMEOUT_MILLIS = 900;
private static final int UID = Process.ROOT_UID;
- private static final int DISPLAY_ID = 10;
+ private static final int DEVICE_ID = 10;
private static final int VIBRATOR_ID = 1;
private static final String PACKAGE_NAME = "package";
private static final VibrationAttributes ATTRS = new VibrationAttributes.Builder().build();
@@ -250,7 +250,7 @@
Vibration.EndInfo cancelVibrationInfo = new Vibration.EndInfo(
Vibration.Status.CANCELLED_SUPERSEDED, new Vibration.CallerInfo(
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ALARM), /* uid= */
- 1, /* displayId= */ -1, /* opPkg= */ null, /* reason= */ null));
+ 1, /* deviceId= */ -1, /* opPkg= */ null, /* reason= */ null));
mVibrationConductor.notifyCancelled(
cancelVibrationInfo,
/* immediate= */ false);
@@ -1641,7 +1641,7 @@
private HalVibration createVibration(CombinedVibration effect) {
return new HalVibration(mVibrationToken, effect,
- new Vibration.CallerInfo(ATTRS, UID, DISPLAY_ID, PACKAGE_NAME, "reason"));
+ new Vibration.CallerInfo(ATTRS, UID, DEVICE_ID, PACKAGE_NAME, "reason"));
}
private SparseArray<VibratorController> createVibratorControllers() {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 3dfaed6..3fce9e7 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -84,10 +84,8 @@
import android.os.vibrator.VibrationEffectSegment;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
-import android.util.ArraySet;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
import android.view.flags.Flags;
@@ -129,7 +127,7 @@
// be cancelled in the body of the individual test.
private static final int CLEANUP_TIMEOUT_MILLIS = 100;
private static final int UID = Process.ROOT_UID;
- private static final int VIRTUAL_DISPLAY_ID = 1;
+ private static final int VIRTUAL_DEVICE_ID = 1;
private static final String PACKAGE_NAME = "package";
private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
@@ -190,9 +188,6 @@
private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
private VibratorManagerService.ExternalVibratorService mExternalVibratorService;
private VibrationConfig mVibrationConfig;
- private VirtualDeviceManagerInternal.VirtualDisplayListener mRegisteredVirtualDisplayListener;
- private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener
- mRegisteredAppsOnVirtualDeviceListener;
private InputManagerGlobal.TestSession mInputManagerGlobalSession;
private InputManager mInputManager;
@@ -223,14 +218,6 @@
mRegisteredPowerModeListener = invocation.getArgument(0);
return null;
}).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
- doAnswer(invocation -> {
- mRegisteredVirtualDisplayListener = invocation.getArgument(0);
- return null;
- }).when(mVirtualDeviceManagerInternalMock).registerVirtualDisplayListener(any());
- doAnswer(invocation -> {
- mRegisteredAppsOnVirtualDeviceListener = invocation.getArgument(0);
- return null;
- }).when(mVirtualDeviceManagerInternalMock).registerAppsOnVirtualDeviceListener(any());
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
@@ -273,6 +260,7 @@
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
// Ignore potential exceptions about the looper having never dispatched any messages.
mTestLooper.stopAutoDispatchAndIgnoreExceptions();
if (mInputManagerGlobalSession != null) {
@@ -1510,65 +1498,33 @@
}
@Test
- public void vibrate_withVirtualDisplayChange_ignoreVibrationFromVirtualDisplay()
- throws Exception {
+ public void vibrate_ignoreVibrationFromVirtualDevice() throws Exception {
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
- mRegisteredVirtualDisplayListener.onVirtualDisplayCreated(VIRTUAL_DISPLAY_ID);
- vibrateWithDisplay(service,
- VIRTUAL_DISPLAY_ID,
+ vibrateWithDevice(service,
+ VIRTUAL_DEVICE_ID,
CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.createOneShot(1000, 100))
.combine(),
HAPTIC_FEEDBACK_ATTRS);
- // Haptic feedback ignored when it's from a virtual display.
+ // Haptic feedback ignored when it's from a virtual device.
assertFalse(waitUntil(s -> s.isVibrating(1), service, /* timeout= */ 50));
- mRegisteredVirtualDisplayListener.onVirtualDisplayRemoved(VIRTUAL_DISPLAY_ID);
- vibrateWithDisplay(service,
- VIRTUAL_DISPLAY_ID,
+ vibrateWithDevice(service,
+ Context.DEVICE_ID_DEFAULT,
CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.createOneShot(1000, 100))
.combine(),
HAPTIC_FEEDBACK_ATTRS);
- // Haptic feedback played normally when the virtual display is removed.
+ // Haptic feedback played normally when it's from the default device.
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
cancelVibrate(service); // Clean up long-ish effect.
}
@Test
- public void vibrate_withAppsOnVirtualDisplayChange_ignoreVibrationFromVirtualDisplay()
- throws Exception {
- mockVibrators(1);
- VibratorManagerService service = createSystemReadyService();
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
- new ArraySet<>(Arrays.asList(UID)));
- vibrateWithDisplay(service,
- Display.INVALID_DISPLAY,
- CombinedVibration.startParallel()
- .addVibrator(1, VibrationEffect.createOneShot(1000, 100))
- .combine(),
- HAPTIC_FEEDBACK_ATTRS);
-
- // Haptic feedback ignored when it's from an app running virtual display.
- assertFalse(waitUntil(s -> s.isVibrating(1), service, /* timeout= */ 50));
-
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(new ArraySet<>());
- vibrateWithDisplay(service,
- Display.INVALID_DISPLAY,
- CombinedVibration.startParallel()
- .addVibrator(1, VibrationEffect.createOneShot(1000, 100))
- .combine(),
- HAPTIC_FEEDBACK_ATTRS);
- // Haptic feedback played normally when the same app no long runs on a virtual display.
- assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
- cancelVibrate(service); // Clean up long-ish effect.
- }
-
- @Test
public void vibrate_prebakedAndComposedVibrationsWithFallbacks_playsFallbackOnlyForPredefined()
throws Exception {
mockVibrators(1);
@@ -1685,7 +1641,7 @@
}
@Test
- public void onExternalVibration_ignoreVibrationFromVirtualDevices() throws Exception {
+ public void onExternalVibration_ignoreVibrationFromVirtualDevices() {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
createSystemReadyService();
@@ -1697,8 +1653,8 @@
int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
- mRegisteredAppsOnVirtualDeviceListener.onAppsOnAnyVirtualDeviceChanged(
- new ArraySet<>(Arrays.asList(UID)));
+ when(mVirtualDeviceManagerInternalMock.isAppRunningOnAnyVirtualDevice(UID))
+ .thenReturn(true);
scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
}
@@ -2396,7 +2352,7 @@
private HalVibration performHapticFeedbackAndWaitUntilFinished(VibratorManagerService service,
int constant, boolean always) throws InterruptedException {
HalVibration vib =
- service.performHapticFeedbackInternal(UID, Display.DEFAULT_DISPLAY, PACKAGE_NAME,
+ service.performHapticFeedbackInternal(UID, Context.DEVICE_ID_DEFAULT, PACKAGE_NAME,
constant, always, "some reason", service);
if (vib != null) {
vib.waitForEnd();
@@ -2414,7 +2370,7 @@
private HalVibration vibrateAndWaitUntilFinished(VibratorManagerService service,
CombinedVibration effect, VibrationAttributes attrs) throws InterruptedException {
HalVibration vib =
- service.vibrateWithPermissionCheck(UID, Display.DEFAULT_DISPLAY, PACKAGE_NAME,
+ service.vibrateWithPermissionCheck(UID, Context.DEVICE_ID_DEFAULT, PACKAGE_NAME,
effect, attrs, "some reason", service);
if (vib != null) {
vib.waitForEnd();
@@ -2430,12 +2386,12 @@
private void vibrate(VibratorManagerService service, CombinedVibration effect,
VibrationAttributes attrs) {
- vibrateWithDisplay(service, Display.DEFAULT_DISPLAY, effect, attrs);
+ vibrateWithDevice(service, Context.DEVICE_ID_DEFAULT, effect, attrs);
}
- private void vibrateWithDisplay(VibratorManagerService service, int displayId,
+ private void vibrateWithDevice(VibratorManagerService service, int deviceId,
CombinedVibration effect, VibrationAttributes attrs) {
- service.vibrate(UID, displayId, PACKAGE_NAME, effect, attrs, "some reason", service);
+ service.vibrate(UID, deviceId, PACKAGE_NAME, effect, attrs, "some reason", service);
}
private boolean waitUntil(Predicate<VibratorManagerService> predicate,
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index f64ab22..63de41f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -206,6 +206,7 @@
static final int MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED = 9;
static final int MSG_UID_REMOVED = 10;
static final int MSG_USER_STARTED = 11;
+ static final int MSG_NOTIFY_USAGE_EVENT_LISTENER = 12;
private final Object mLock = new Object();
private Handler mHandler;
@@ -315,6 +316,16 @@
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
return true;
}
+ case MSG_NOTIFY_USAGE_EVENT_LISTENER: {
+ final int userId = msg.arg1;
+ final Event event = (Event) msg.obj;
+ synchronized (mUsageEventListeners) {
+ final int size = mUsageEventListeners.size();
+ for (int i = 0; i < size; ++i) {
+ mUsageEventListeners.valueAt(i).onUsageEvent(userId, event);
+ }
+ }
+ }
}
return false;
};
@@ -532,9 +543,6 @@
}
reportEvent(unlockEvent, userId);
- mIoHandler.obtainMessage(MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK,
- userId, 0).sendToTarget();
-
// Remove all the stats stored in system DE.
deleteRecursively(new File(Environment.getDataSystemDeDirectory(userId), "usagestats"));
@@ -546,6 +554,8 @@
userService.persistActiveStats();
}
}
+
+ mIoHandler.obtainMessage(MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK, userId, 0).sendToTarget();
}
/**
@@ -1240,12 +1250,7 @@
service.reportEvent(event);
}
- synchronized (mUsageEventListeners) {
- final int size = mUsageEventListeners.size();
- for (int i = 0; i < size; ++i) {
- mUsageEventListeners.valueAt(i).onUsageEvent(userId, event);
- }
- }
+ mIoHandler.obtainMessage(MSG_NOTIFY_USAGE_EVENT_LISTENER, userId, 0, event).sendToTarget();
}
private String getUsageSourcePackage(Event event) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
index bb6cf52..fb18375 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
@@ -52,9 +52,12 @@
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_UNEXPECTED_CALLBACK;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECT_UNEXPECTED_CALLBACK;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__TRAINING_DATA;
-import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__TRAINING_DATA_SECURITY_EXCEPTION;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__TRAINING_DATA_EGRESS_LIMIT_REACHED;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__TRAINING_DATA_REMOTE_EXCEPTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__TRAINING_DATA_SECURITY_EXCEPTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_DETECTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_REJECTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_TRAINING_DATA;
import static com.android.server.voiceinteraction.HotwordDetectionConnection.ENFORCE_HOTWORD_PHRASE_ID;
import android.annotation.NonNull;
@@ -73,7 +76,9 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
+import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
@@ -94,6 +99,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.infra.AndroidFuture;
+import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.policy.AppOpsPolicy;
import com.android.server.voiceinteraction.VoiceInteractionManagerServiceImpl.DetectorRemoteExceptionListener;
@@ -180,6 +186,13 @@
private static final int METRICS_CALLBACK_ON_STATUS_REPORTED_EXCEPTION =
HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_STATUS_REPORTED_EXCEPTION;
+ private static final int HOTWORD_EVENT_TYPE_DETECTION =
+ HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_DETECTION;
+ private static final int HOTWORD_EVENT_TYPE_REJECTION =
+ HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_REJECTION;
+ private static final int HOTWORD_EVENT_TYPE_TRAINING_DATA =
+ HOTWORD_EVENT_EGRESS_SIZE__EVENT_TYPE__HOTWORD_TRAINING_DATA;
+
private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool();
// TODO: This may need to be a Handler(looper)
final ScheduledExecutorService mScheduledExecutorService;
@@ -516,6 +529,7 @@
if (result != null) {
Slog.i(TAG, "Egressed 'hotword rejected result' "
+ "from hotword trusted process");
+ logEgressSizeStats(result);
if (mDebugHotwordLogging) {
Slog.i(TAG, "Egressed detected result: " + result);
}
@@ -608,6 +622,7 @@
Slog.i(TAG, "Egressed "
+ HotwordDetectedResult.getUsageSize(newResult)
+ " bits from hotword trusted process");
+ logEgressSizeStats(newResult);
if (mDebugHotwordLogging) {
Slog.i(TAG,
"Egressed detected result: " + newResult);
@@ -624,6 +639,32 @@
mVoiceInteractionServiceUid);
}
+ void logEgressSizeStats(HotwordTrainingData data) {
+ logEgressSizeStats(data, HOTWORD_EVENT_TYPE_TRAINING_DATA);
+ }
+
+ void logEgressSizeStats(HotwordDetectedResult data) {
+ logEgressSizeStats(data, HOTWORD_EVENT_TYPE_DETECTION);
+
+ }
+
+ void logEgressSizeStats(HotwordRejectedResult data) {
+ logEgressSizeStats(data, HOTWORD_EVENT_TYPE_REJECTION);
+ }
+
+ /** Logs event size stats for events egressed from trusted hotword detection service. */
+ private void logEgressSizeStats(Parcelable data, int eventType) {
+ BackgroundThread.getExecutor().execute(() -> {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeValue(data);
+ int dataSizeBytes = parcel.dataSize();
+ parcel.recycle();
+
+ HotwordMetricsLogger.writeHotwordDataEgressSize(eventType, dataSizeBytes,
+ getDetectorType(), mVoiceInteractionServiceUid);
+ });
+ }
+
/** Used to send training data.
*
* @hide
@@ -723,6 +764,7 @@
mVoiceInteractionServiceUid);
throw e;
}
+ logEgressSizeStats(data);
}
void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
index 6418f3e..2938a58 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
@@ -186,6 +186,7 @@
if (mDebugHotwordLogging) {
Slog.i(TAG, "Egressed detected result: " + newResult);
}
+ logEgressSizeStats(newResult);
}
}
@@ -228,6 +229,7 @@
if (mDebugHotwordLogging && result != null) {
Slog.i(TAG, "Egressed rejected result: " + result);
}
+ logEgressSizeStats(result);
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java
index f7b66a2..ca72c85 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordMetricsLogger.java
@@ -34,6 +34,9 @@
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__NORMAL_DETECTOR;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
import static com.android.internal.util.LatencyTracker.ACTION_SHOW_VOICE_INTERACTION;
import android.content.Context;
@@ -120,6 +123,16 @@
}
/**
+ * Logs hotword event egress size metrics.
+ */
+ public static void writeHotwordDataEgressSize(int eventType, long eventSize, int detectorType,
+ int uid) {
+ int metricsDetectorType = getHotwordEventEgressSizeDetectorType(detectorType);
+ FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_EGRESS_SIZE_ATOM_REPORTED,
+ eventType, eventSize, metricsDetectorType, uid);
+ }
+
+ /**
* Starts a {@link LatencyTracker} log for the time it takes to show the
* {@link android.service.voice.VoiceInteractionSession} system UI after a voice trigger.
*
@@ -224,4 +237,15 @@
return AUDIO_EGRESS_NORMAL_DETECTOR;
}
}
+
+ private static int getHotwordEventEgressSizeDetectorType(int detectorType) {
+ switch (detectorType) {
+ case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE:
+ return HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
+ case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP:
+ return HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
+ default:
+ return HOTWORD_EVENT_EGRESS_SIZE__DETECTOR_TYPE__NORMAL_DETECTOR;
+ }
+ }
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
index 2e23eff..9de7f9a 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
@@ -179,6 +179,7 @@
}
Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(newResult)
+ " bits from hotword trusted process");
+ logEgressSizeStats(newResult);
if (mDebugHotwordLogging) {
Slog.i(TAG, "Egressed detected result: " + newResult);
}
@@ -194,6 +195,7 @@
HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE,
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED,
mVoiceInteractionServiceUid);
+ logEgressSizeStats(result);
// onRejected isn't allowed here, and we are not expecting it.
}
diff --git a/telephony/java/android/telephony/satellite/ISatelliteCapabilitiesCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteCapabilitiesCallback.aidl
new file mode 100644
index 0000000..4c37a6d
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/ISatelliteCapabilitiesCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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 android.telephony.satellite;
+
+import android.telephony.satellite.SatelliteCapabilities;
+
+/**
+ * Interface for satellite capabilities change callback.
+ * @hide
+ */
+oneway interface ISatelliteCapabilitiesCallback {
+ /**
+ * Called when satellite capability has changed.
+ *
+ * @param capabilities The new satellite capability.
+ */
+ void onSatelliteCapabilitiesChanged(in SatelliteCapabilities capabilities);
+}
+
diff --git a/telephony/java/android/telephony/satellite/SatelliteCapabilitiesCallback.java b/telephony/java/android/telephony/satellite/SatelliteCapabilitiesCallback.java
new file mode 100644
index 0000000..b68dd5a
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteCapabilitiesCallback.java
@@ -0,0 +1,39 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * A callback class for satellite capabilities change events.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public interface SatelliteCapabilitiesCallback {
+ /**
+ * Called when satellite capability has changed.
+ * @param capabilities The new satellite capabilities.
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ void onSatelliteCapabilitiesChanged(@NonNull SatelliteCapabilities capabilities);
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index f18cbea..71786b3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -81,6 +81,9 @@
new ConcurrentHashMap<>();
private static final ConcurrentHashMap<NtnSignalStrengthCallback, INtnSignalStrengthCallback>
sNtnSignalStrengthCallbackMap = new ConcurrentHashMap<>();
+ private static final ConcurrentHashMap<SatelliteCapabilitiesCallback,
+ ISatelliteCapabilitiesCallback>
+ sSatelliteCapabilitiesCallbackMap = new ConcurrentHashMap<>();
private final int mSubId;
@@ -2018,7 +2021,7 @@
* {@link TelephonyManager#unregisterTelephonyCallback(TelephonyCallback)}..
* </p>
*
- * @param callback The callback that was passed to
+ * @param callback The callback that was passed to.
* {@link #registerForNtnSignalStrengthChanged(Executor, NtnSignalStrengthCallback)}.
*
* @throws SecurityException if the caller doesn't have required permission.
@@ -2046,9 +2049,84 @@
loge("unregisterForNtnSignalStrengthChanged() RemoteException: " + ex);
ex.rethrowFromSystemServer();
}
-
}
+ /**
+ * Registers for satellite capabilities change event from the satellite service.
+ *
+ * @param executor The executor on which the callback will be called.
+ * @param callback The callback to handle the satellite capabilities changed event.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @SatelliteResult public int registerForSatelliteCapabilitiesChanged(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull SatelliteCapabilitiesCallback callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ ISatelliteCapabilitiesCallback internalCallback =
+ new ISatelliteCapabilitiesCallback.Stub() {
+ @Override
+ public void onSatelliteCapabilitiesChanged(
+ SatelliteCapabilities capabilities) {
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> callback.onSatelliteCapabilitiesChanged(
+ capabilities)));
+ }
+ };
+ sSatelliteCapabilitiesCallbackMap.put(callback, internalCallback);
+ return telephony.registerForSatelliteCapabilitiesChanged(mSubId, internalCallback);
+ } else {
+ throw new IllegalStateException("Telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ loge("registerForSatelliteCapabilitiesChanged() RemoteException: " + ex);
+ ex.rethrowFromSystemServer();
+ }
+ return SATELLITE_RESULT_REQUEST_FAILED;
+ }
+
+ /**
+ * Unregisters for satellite capabilities change event from the satellite service.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param callback The callback that was passed to.
+ * {@link #registerForSatelliteCapabilitiesChanged(Executor, SatelliteCapabilitiesCallback)}.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public void unregisterForSatelliteCapabilitiesChanged(
+ @NonNull SatelliteCapabilitiesCallback callback) {
+ Objects.requireNonNull(callback);
+ ISatelliteCapabilitiesCallback internalCallback =
+ sSatelliteCapabilitiesCallbackMap.remove(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ if (internalCallback != null) {
+ telephony.unregisterForSatelliteCapabilitiesChanged(mSubId, internalCallback);
+ } else {
+ loge("unregisterForSatelliteCapabilitiesChanged: No internal callback.");
+ }
+ } else {
+ throw new IllegalStateException("Telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ loge("unregisterForSatelliteCapabilitiesChanged() RemoteException: " + ex);
+ ex.rethrowFromSystemServer();
+ }
+ }
private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
diff --git a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
index d44ddfa..ccca5ad 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
@@ -19,6 +19,7 @@
import android.telephony.satellite.stub.NtnSignalStrength;
import android.telephony.satellite.stub.NTRadioTechnology;
import android.telephony.satellite.stub.PointingInfo;
+import android.telephony.satellite.stub.SatelliteCapabilities;
import android.telephony.satellite.stub.SatelliteDatagram;
import android.telephony.satellite.stub.SatelliteModemState;
@@ -62,7 +63,15 @@
/**
* Called when NTN signal strength changes.
+ *
* @param ntnSignalStrength The new NTN signal strength.
*/
void onNtnSignalStrengthChanged(in NtnSignalStrength ntnSignalStrength);
+
+ /**
+ * Called when satellite capabilities of the satellite service have changed.
+ *
+ * @param SatelliteCapabilities The current satellite capabilities.
+ */
+ void onSatelliteCapabilitiesChanged(in SatelliteCapabilities capabilities);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index c212e35..15a20cb 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -68,6 +68,7 @@
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.aidl.IRcsConfigCallback;
import android.telephony.satellite.INtnSignalStrengthCallback;
+import android.telephony.satellite.ISatelliteCapabilitiesCallback;
import android.telephony.satellite.ISatelliteDatagramCallback;
import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
import android.telephony.satellite.ISatelliteProvisionStateCallback;
@@ -3123,4 +3124,27 @@
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
void unregisterForNtnSignalStrengthChanged(int subId,
in INtnSignalStrengthCallback callback);
+
+ /**
+ * Registers for satellite capabilities change event from the satellite service.
+ *
+ * @param executor The executor on which the callback will be called.
+ * @param callback The callback to handle the satellite capabilities changed event.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ int registerForSatelliteCapabilitiesChanged(int subId,
+ in ISatelliteCapabilitiesCallback callback);
+
+ /**
+ * Unregisters for satellite capabilities change event from the satellite service.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param callback The callback that was passed to.
+ * {@link #registerForSatelliteCapabilitiesChanged(Executor, SatelliteCapabilitiesCallback)}.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ void unregisterForSatelliteCapabilitiesChanged(int subId,
+ in ISatelliteCapabilitiesCallback callback);
}
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
index 421ceb7..07b7338 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
@@ -50,7 +50,7 @@
public class VibratorManagerServicePermissionTest {
private static final String PACKAGE_NAME = "com.android.framework.permission.tests";
- private static final int DISPLAY_ID = 1;
+ private static final int DEVICE_ID = 1;
private static final CombinedVibration EFFECT =
CombinedVibration.createParallel(
VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
@@ -107,7 +107,7 @@
@Test
public void testVibrateWithoutPermissionFails() throws RemoteException {
expectSecurityException("VIBRATE");
- mVibratorService.vibrate(Process.myUid(), DISPLAY_ID, PACKAGE_NAME, EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.myUid(), DEVICE_ID, PACKAGE_NAME, EFFECT, ATTRS,
"testVibrate",
new Binder());
}
@@ -117,7 +117,7 @@
throws RemoteException {
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE);
- mVibratorService.vibrate(Process.myUid(), DISPLAY_ID, PACKAGE_NAME, EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.myUid(), DEVICE_ID, PACKAGE_NAME, EFFECT, ATTRS,
"testVibrate",
new Binder());
}
@@ -127,7 +127,7 @@
expectSecurityException("UPDATE_APP_OPS_STATS");
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE);
- mVibratorService.vibrate(Process.SYSTEM_UID, DISPLAY_ID, "android", EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.SYSTEM_UID, DEVICE_ID, "android", EFFECT, ATTRS,
"testVibrate",
new Binder());
}
@@ -137,7 +137,7 @@
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE,
Manifest.permission.UPDATE_APP_OPS_STATS);
- mVibratorService.vibrate(Process.SYSTEM_UID, DISPLAY_ID, "android", EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.SYSTEM_UID, DEVICE_ID, "android", EFFECT, ATTRS,
"testVibrate",
new Binder());
}
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerStub.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java
similarity index 95%
rename from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerStub.java
rename to tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java
index dab8e89..eec7226 100644
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerStub.java
+++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java
@@ -32,5 +32,5 @@
*/
@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
@Retention(RetentionPolicy.CLASS)
-public @interface HostSideTestStaticInitializerStub {
+public @interface HostSideTestStaticInitializerKeep {
}
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
index 25abbac..c770b9c 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
@@ -65,7 +65,7 @@
*/
public static void onThrowMethodCalled() {
// TODO: Maybe add call tracking?
- throw new AssumptionViolatedException("This method is not supported on the host side");
+ throw new RuntimeException("This method is not supported on the host side");
}
/**
diff --git a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
index 43f9cb9b..c371b5d 100644
--- a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
+++ b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
@@ -40,5 +40,5 @@
--class-load-hook-annotation
android.hosttest.annotation.HostSideTestClassLoadHook
---stub-static-initializer-annotation
- android.hosttest.annotation.HostSideTestStaticInitializerStub
+--keep-static-initializer-annotation
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
index 0068afb..89daa20 100755
--- a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
+++ b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
@@ -96,8 +96,8 @@
android.hosttest.annotation.HostSideTestNativeSubstitutionClass \
--class-load-hook-annotation \
android.hosttest.annotation.HostSideTestClassLoadHook \
- --stub-static-initializer-annotation \
- android.hosttest.annotation.HostSideTestStaticInitializerStub \
+ --keep-static-initializer-annotation \
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep \
$filter_arg \
|& tee $HOSTSTUBGEN_OUT
HOSTSTUBGEN_RC=${PIPESTATUS[0]}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index f32dc72..07bd2dc 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -189,7 +189,7 @@
options.substituteAnnotations,
options.nativeSubstituteAnnotations,
options.classLoadHookAnnotations,
- options.stubStaticInitializerAnnotations,
+ options.keepStaticInitializerAnnotations,
annotationAllowedClassesFilter,
filter,
)
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index aab02b8..da53487 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -47,7 +47,7 @@
var substituteAnnotations: MutableSet<String> = mutableSetOf(),
var nativeSubstituteAnnotations: MutableSet<String> = mutableSetOf(),
var classLoadHookAnnotations: MutableSet<String> = mutableSetOf(),
- var stubStaticInitializerAnnotations: MutableSet<String> = mutableSetOf(),
+ var keepStaticInitializerAnnotations: MutableSet<String> = mutableSetOf(),
var packageRedirects: MutableList<Pair<String, String>> = mutableListOf(),
@@ -166,8 +166,8 @@
ret.classLoadHookAnnotations +=
ensureUniqueAnnotation(ai.nextArgRequired(arg))
- "--stub-static-initializer-annotation" ->
- ret.stubStaticInitializerAnnotations +=
+ "--keep-static-initializer-annotation" ->
+ ret.keepStaticInitializerAnnotations +=
ensureUniqueAnnotation(ai.nextArgRequired(arg))
"--package-redirect" ->
@@ -318,6 +318,7 @@
substituteAnnotations=$substituteAnnotations,
nativeSubstituteAnnotations=$nativeSubstituteAnnotations,
classLoadHookAnnotations=$classLoadHookAnnotations,
+ keepStaticInitializerAnnotations=$keepStaticInitializerAnnotations,
packageRedirects=$packageRedirects,
$annotationAllowedClassesFile=$annotationAllowedClassesFile,
defaultClassLoadHook=$defaultClassLoadHook,
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
index eda6761..248121c 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
@@ -52,7 +52,7 @@
substituteAnnotations_: Set<String>,
nativeSubstituteAnnotations_: Set<String>,
classLoadHookAnnotations_: Set<String>,
- stubStaticInitializerAnnotations_: Set<String>,
+ keepStaticInitializerAnnotations_: Set<String>,
private val annotationAllowedClassesFilter: ClassFilter,
fallback: OutputFilter,
) : DelegatingFilter(fallback) {
@@ -65,8 +65,8 @@
private var substituteAnnotations = convertToInternalNames(substituteAnnotations_)
private var nativeSubstituteAnnotations = convertToInternalNames(nativeSubstituteAnnotations_)
private var classLoadHookAnnotations = convertToInternalNames(classLoadHookAnnotations_)
- private var stubStaticInitializerAnnotations =
- convertToInternalNames(stubStaticInitializerAnnotations_)
+ private var keepStaticInitializerAnnotations =
+ convertToInternalNames(keepStaticInitializerAnnotations_)
/** Annotations that control API visibility. */
private var visibilityAnnotations: Set<String> = convertToInternalNames(
@@ -240,9 +240,9 @@
val cn = classes.getClass(className)
if (methodName == CLASS_INITIALIZER_NAME && descriptor == CLASS_INITIALIZER_DESC) {
- findAnyAnnotation(cn.name, stubStaticInitializerAnnotations,
+ findAnyAnnotation(cn.name, keepStaticInitializerAnnotations,
cn.visibleAnnotations, cn.invisibleAnnotations)?.let {
- return FilterPolicy.Stub.withReason(reasonAnnotation)
+ return FilterPolicy.Keep.withReason(reasonAnnotation)
}
}
@@ -377,7 +377,7 @@
throw HostStubGenInternalException("Policy $policy shouldn't show up here")
}
- val suffix = getAnnotationField(an, "suffix") ?: return@let
+ val suffix = getAnnotationField(an, "suffix", false) ?: "\$ravenwood"
val renameFrom = mn.name + suffix
val renameTo = mn.name
@@ -387,13 +387,17 @@
}
// This mn has "SubstituteWith". This means,
- // 1. Re move the "rename-to" method, so add it to substitutedMethods.
+ // 1. Re move the "rename-to" method, so add it to substitutedMethods.
policiesFromSubstitution[MethodKey(renameTo, mn.desc)] =
FilterPolicy.Remove.withReason("substitute-to")
+ // If the policy is "stub", use "stub".
+ // Otherwise, it must be "keep" or "throw", but there's no point in using
+ // "throw", so let's use "keep".
+ val newPolicy = if (policy.needsInStub) policy else FilterPolicy.Keep
// 2. We also keep the from-to in the map.
policiesFromSubstitution[MethodKey(renameFrom, mn.desc)] =
- policy.withReason("substitute-from")
+ newPolicy.withReason("substitute-from")
substituteToMethods[MethodKey(renameFrom, mn.desc)] = renameTo
log.v("Substitution found: %s%s -> %s", renameFrom, mn.desc, renameTo)
@@ -405,10 +409,11 @@
/**
* Return the (String) value of 'value' parameter from an annotation.
*/
- private fun getAnnotationField(an: AnnotationNode, name: String): String? {
+ private fun getAnnotationField(an: AnnotationNode, name: String,
+ required: Boolean = true): String? {
try {
val suffix = findAnnotationValueAsString(an, name)
- if (suffix == null) {
+ if (suffix == null && required) {
errors.onErrorFound("Annotation \"${an.desc}\" must have field $name")
}
return suffix
@@ -438,4 +443,4 @@
return ret
}
}
-}
\ No newline at end of file
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
index 9c0fa69..84856ac 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
@@ -25,6 +25,7 @@
import com.android.hoststubgen.asm.isAnnotation
import com.android.hoststubgen.asm.isAutoGeneratedEnumMember
import com.android.hoststubgen.asm.isEnum
+import com.android.hoststubgen.asm.isSynthetic
import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate
import org.objectweb.asm.tree.ClassNode
@@ -33,6 +34,8 @@
* - "keep all anonymous inner classes if the outer class is keep".
* (But anonymous inner classes should never be in "stub")
* - For classes in stub, make sure private parameterless constructors are also in stub, if any.
+ *
+ * TODO: Do we need a way to make anonymous class methods and lambdas "throw"?
*/
class ImplicitOutputFilter(
private val errors: HostStubGenErrors,
@@ -74,10 +77,11 @@
descriptor: String
): FilterPolicyWithReason {
val fallback = super.getPolicyForMethod(className, methodName, descriptor)
+ val classPolicy = outermostFilter.getPolicyForClass(className)
// If the class is in the stub, then we need to put the private constructor in the stub too,
// to prevent the class from getting instantiated.
- if (outermostFilter.getPolicyForClass(className).policy.needsInStub &&
+ if (classPolicy.policy.needsInStub &&
!fallback.policy.needsInStub &&
(methodName == "<init>") && // Constructor?
(descriptor == "()V")) { // Has zero parameters?
@@ -102,8 +106,6 @@
" [original throw reason: ${fallback.reason}]")
}
- val classPolicy = super.getPolicyForClass(className)
-
log.d("Class ${cn.name} Class policy: $classPolicy")
if (classPolicy.policy.needsInImpl) {
// Do it only when the class needs to be kept...
@@ -111,9 +113,11 @@
// Member policy should be "keep" or "stub".
val memberPolicy = classPolicy.policy.resolveClassWidePolicy()
+ val mn = classes.findMethod(className, methodName, descriptor)
+
// Keep (or stub) the generated enum members.
if (cn.isEnum()) {
- classes.findMethod(className, methodName, descriptor)?.let { mn ->
+ mn?.let { mn ->
if (isAutoGeneratedEnumMember(mn)) {
return memberPolicy.withReason(classPolicy.reason).wrapReason("enum")
}
@@ -124,6 +128,15 @@
if (cn.isAnnotation()) {
return memberPolicy.withReason(classPolicy.reason).wrapReason("annotation")
}
+
+ mn?.let {
+ if (mn.isSynthetic()) {
+ // For synthetic methods (such as lambdas), let's just inherit the class's
+ // policy.
+ return memberPolicy.withReason(classPolicy.reason).wrapReason(
+ "synthetic method")
+ }
+ }
}
return fallback
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
index 639fb16..00cbfe3 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
@@ -117,7 +117,7 @@
if (( $two_way )) ; then
echo "# Running meld..."
- run meld --diff ${files[0]} ${files[1]} --diff ${files[1]} ${files[2]}
+ run meld --diff ${files[0]} ${files[1]} --diff ${files[1]} ${files[2]} --diff ${files[0]} ${files[2]}
fi
if (( $any_file_changed == 0 )) ; then
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 78a4fa6..3474ae4 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -84,17 +84,17 @@
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
-## Class: android/hosttest/annotation/HostSideTestStaticInitializerStub.class
- Compiled from "HostSideTestStaticInitializerStub.java"
-public interface android.hosttest.annotation.HostSideTestStaticInitializerStub extends java.lang.annotation.Annotation
+## Class: android/hosttest/annotation/HostSideTestStaticInitializerKeep.class
+ Compiled from "HostSideTestStaticInitializerKeep.java"
+public interface android.hosttest.annotation.HostSideTestStaticInitializerKeep extends java.lang.annotation.Annotation
minor version: 0
major version: 61
flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
- this_class: #x // android/hosttest/annotation/HostSideTestStaticInitializerStub
+ this_class: #x // android/hosttest/annotation/HostSideTestStaticInitializerKeep
super_class: #x // java/lang/Object
interfaces: 1, fields: 0, methods: 0, attributes: 2
}
-SourceFile: "HostSideTestStaticInitializerStub.java"
+SourceFile: "HostSideTestStaticInitializerKeep.java"
RuntimeVisibleAnnotations:
x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
@@ -878,7 +878,7 @@
x: #x()
android.hosttest.annotation.HostSideTestStub
x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerStub
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
Compiled from "TinyFrameworkEnumComplex.java"
public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
@@ -1401,6 +1401,315 @@
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
}
SourceFile: "TinyFrameworkForTextPolicy.java"
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: bipush 8
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: bipush 7
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: bipush 6
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_5
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+}
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+BootstrapMethods:
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$new$0:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier$2:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$static$1:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_4
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_3
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_2
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_1
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+}
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+BootstrapMethods:
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$new$0:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier$2:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$static$1:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index df63815..a1aae8a 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -405,7 +405,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 1, attributes: 3
+ interfaces: 0, fields: 2, methods: 0, attributes: 3
public static boolean sInitialized;
descriptor: Z
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -420,16 +420,6 @@
x: #x()
android.hosttest.annotation.HostSideTestStub
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
}
SourceFile: "TinyFrameworkClassWithInitializerStub.java"
RuntimeVisibleAnnotations:
@@ -445,7 +435,7 @@
x: #x()
android.hosttest.annotation.HostSideTestStub
x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerStub
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
Compiled from "TinyFrameworkEnumComplex.java"
public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
@@ -784,6 +774,263 @@
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 7, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 7, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index 2218d8d..29626f2 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -797,7 +797,7 @@
x: #x()
android.hosttest.annotation.HostSideTestStub
x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerStub
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
Compiled from "TinyFrameworkEnumComplex.java"
public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
@@ -1323,6 +1323,325 @@
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 6
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: bipush 8
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: bipush 7
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: bipush 6
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_5
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+BootstrapMethods:
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$new$0:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier$2:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$static$1:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 6
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_4
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_3
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_2
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: iconst_1
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+BootstrapMethods:
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$new$0:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier$2:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$static$1:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index df63815..a1aae8a 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -405,7 +405,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
super_class: #x // java/lang/Object
- interfaces: 0, fields: 2, methods: 1, attributes: 3
+ interfaces: 0, fields: 2, methods: 0, attributes: 3
public static boolean sInitialized;
descriptor: Z
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -420,16 +420,6 @@
x: #x()
android.hosttest.annotation.HostSideTestStub
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
}
SourceFile: "TinyFrameworkClassWithInitializerStub.java"
RuntimeVisibleAnnotations:
@@ -445,7 +435,7 @@
x: #x()
android.hosttest.annotation.HostSideTestStub
x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerStub
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
Compiled from "TinyFrameworkEnumComplex.java"
public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
@@ -784,6 +774,263 @@
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 7, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 7, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index 3ac9c6a..ed7e7d3 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -1045,7 +1045,7 @@
x: #x()
android.hosttest.annotation.HostSideTestStub
x: #x()
- android.hosttest.annotation.HostSideTestStaticInitializerStub
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
Compiled from "TinyFrameworkEnumComplex.java"
public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
@@ -1695,6 +1695,411 @@
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 6
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String getSupplier
+ x: ldc #x // String ()Ljava/util/function/Supplier;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String getSupplier_static
+ x: ldc #x // String ()Ljava/util/function/Supplier;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String lambda$getSupplier_static$3
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: bipush 8
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String lambda$getSupplier$2
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: bipush 7
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String lambda$static$1
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: bipush 6
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String lambda$new$0
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_5
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String <clinit>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+BootstrapMethods:
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$new$0:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier$2:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$static$1:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
+ Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 6
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String getSupplier
+ x: ldc #x // String ()Ljava/util/function/Supplier;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String getSupplier_static
+ x: ldc #x // String ()Ljava/util/function/Supplier;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static java.lang.Integer lambda$getSupplier_static$3();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String lambda$getSupplier_static$3
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_4
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$getSupplier$2();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String lambda$getSupplier$2
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_3
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$static$1();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String lambda$static$1
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_2
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ private static java.lang.Integer lambda$new$0();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String lambda$new$0
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_1
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String <clinit>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x()
+ android.hosttest.annotation.HostSideTestStaticInitializerKeep
+BootstrapMethods:
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$new$0:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier$2:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+ x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ Method arguments:
+ #x ()Ljava/lang/Object;
+ #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$static$1:()Ljava/lang/Integer;
+ #x ()Ljava/lang/Integer;
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
index 998acf2..ea1ad93 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
@@ -16,14 +16,13 @@
package com.android.hoststubgen.test.tinyframework;
import android.hosttest.annotation.HostSideTestClassLoadHook;
-import android.hosttest.annotation.HostSideTestStaticInitializerStub;
+import android.hosttest.annotation.HostSideTestStaticInitializerKeep;
import android.hosttest.annotation.HostSideTestStub;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
@HostSideTestClassLoadHook(
"com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded")
@HostSideTestStub
-@HostSideTestStaticInitializerStub
+@HostSideTestStaticInitializerKeep
public class TinyFrameworkClassWithInitializerStub {
static {
sInitialized = true;
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
new file mode 100644
index 0000000..0d1203b
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
@@ -0,0 +1,77 @@
+/*
+ * 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.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestStaticInitializerKeep;
+import android.hosttest.annotation.HostSideTestStub;
+
+import java.util.function.Supplier;
+
+
+/**
+ * In this class, we explicitly mark each member as "stub". (rather than using WholeClassStub)
+ *
+ * This means the actual generated lambda functions would be removed by default.
+ *
+ * Implicit filter should take care of them.
+ */
+@HostSideTestStub
+@HostSideTestStaticInitializerKeep
+public class TinyFrameworkLambdas {
+ @HostSideTestStub
+ public TinyFrameworkLambdas() {
+ }
+
+ @HostSideTestStub
+ public final Supplier<Integer> mSupplier = () -> 1;
+
+ @HostSideTestStub
+ public static final Supplier<Integer> sSupplier = () -> 2;
+
+ @HostSideTestStub
+ public Supplier<Integer> getSupplier() {
+ return () -> 3;
+ }
+
+ @HostSideTestStub
+ public static Supplier<Integer> getSupplier_static() {
+ return () -> 4;
+ }
+
+ @HostSideTestStub
+ @HostSideTestStaticInitializerKeep
+ public static class Nested {
+ @HostSideTestStub
+ public Nested() {
+ }
+
+ @HostSideTestStub
+ public final Supplier<Integer> mSupplier = () -> 5;
+
+ @HostSideTestStub
+ public static final Supplier<Integer> sSupplier = () -> 6;
+
+ @HostSideTestStub
+ public Supplier<Integer> getSupplier() {
+ return () -> 7;
+ }
+
+ @HostSideTestStub
+ public static Supplier<Integer> getSupplier_static() {
+ return () -> 8;
+ }
+ }
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/LargeTest.java
similarity index 66%
copy from ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/LargeTest.java
index 8cdc1ff..76bbcad 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/LargeTest.java
@@ -13,24 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.ravenwood.annotations;
+package com.android.hoststubgen.test.tinyframework;
-import static java.lang.annotation.ElementType.TYPE;
-
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
- * QUESTIONS ABOUT IT.
- *
- * TODO: Javadoc
- *
- * @hide
+ * We don't want to use any android classes in this module, so we create our own copy of it here.
*/
-@Target({TYPE})
-@Retention(RetentionPolicy.CLASS)
-public @interface RavenwoodNativeSubstitutionClass {
- String value();
-}
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface LargeTest {}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java
index 6b5110e..d57735b 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java
@@ -22,9 +22,10 @@
/**
* Contains simple micro-benchmarks.
*/
+@LargeTest
public class TinyFrameworkBenchmark {
private static final int MINIMAL_ITERATION = 1000;
- private static final int MEASURE_SECONDS = 3;
+ private static final int MEASURE_SECONDS = 1;
private static final DecimalFormat sFormatter = new DecimalFormat("#,###");
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index ecb181b..b015661 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -107,6 +107,46 @@
}
@Test
+ public void testLambda1() {
+ assertThat(new TinyFrameworkLambdas().mSupplier.get()).isEqualTo(1);
+ }
+
+ @Test
+ public void testLambda2() {
+ assertThat(TinyFrameworkLambdas.sSupplier.get()).isEqualTo(2);
+ }
+
+ @Test
+ public void testLambda3() {
+ assertThat(new TinyFrameworkLambdas().getSupplier().get()).isEqualTo(3);
+ }
+
+ @Test
+ public void testLambda4() {
+ assertThat(TinyFrameworkLambdas.getSupplier_static().get()).isEqualTo(4);
+ }
+
+ @Test
+ public void testLambda5() {
+ assertThat(new TinyFrameworkLambdas.Nested().mSupplier.get()).isEqualTo(5);
+ }
+
+ @Test
+ public void testLambda6() {
+ assertThat(TinyFrameworkLambdas.Nested.sSupplier.get()).isEqualTo(6);
+ }
+
+ @Test
+ public void testLambda7() {
+ assertThat(new TinyFrameworkLambdas.Nested().getSupplier().get()).isEqualTo(7);
+ }
+
+ @Test
+ public void testLambda8() {
+ assertThat(TinyFrameworkLambdas.Nested.getSupplier_static().get()).isEqualTo(8);
+ }
+
+ @Test
public void testNativeSubstitutionClass() {
assertThat(TinyFrameworkNative.nativeAddTwo(3)).isEqualTo(5);
}
diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh
index 4afa2d7..2dac089 100755
--- a/tools/hoststubgen/scripts/run-all-tests.sh
+++ b/tools/hoststubgen/scripts/run-all-tests.sh
@@ -53,4 +53,6 @@
# These tests should all pass.
run-ravenwood-test ${READY_TEST_MODULES[*]}
+run atest CtsUtilTestCasesRavenwood
+
echo ""${0##*/}" finished, with no unexpected failures. Ready to submit!"
\ No newline at end of file