Merge "Fix clip calculation for disabled clip bounds" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 44f3d70..52200bf 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -180,6 +180,18 @@
srcs: ["core/java/android/nfc/*.aconfig"],
}
+cc_aconfig_library {
+ name: "android_nfc_flags_aconfig_c_lib",
+ vendor_available: true,
+ aconfig_declarations: "android.nfc.flags-aconfig",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.nfcservices",
+ "nfc_nci.st21nfc.default",
+ ],
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
java_aconfig_library {
name: "android.nfc.flags-aconfig-java",
aconfig_declarations: "android.nfc.flags-aconfig",
diff --git a/core/api/current.txt b/core/api/current.txt
index 3f4a34b..d80b5cb 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
@@ -28698,14 +28698,17 @@
}
public final class NfcAdapter {
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean allowTransaction();
method public void disableForegroundDispatch(android.app.Activity);
method public void disableReaderMode(android.app.Activity);
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean disallowTransaction();
method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]);
method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle);
method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
method @Nullable public android.nfc.NfcAntennaInfo getNfcAntennaInfo();
method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler);
method public boolean isEnabled();
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean isObserveModeSupported();
method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionEnabled();
method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionSupported();
method public boolean isSecureNfcEnabled();
@@ -28813,6 +28816,7 @@
method public boolean removeAidsForService(android.content.ComponentName, String);
method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean setOffHostForService(@NonNull android.content.ComponentName, @NonNull String);
method public boolean setPreferredService(android.app.Activity, android.content.ComponentName);
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setServiceObserveModeDefault(@NonNull android.content.ComponentName, boolean);
method public boolean supportsAidPrefixRegistration();
method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean unsetOffHostForService(@NonNull android.content.ComponentName);
method public boolean unsetPreferredService(android.app.Activity);
@@ -28832,9 +28836,20 @@
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract void onDeactivated(int);
method public abstract byte[] processCommandApdu(byte[], android.os.Bundle);
+ method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.os.Bundle>);
method public final void sendResponseApdu(byte[]);
field public static final int DEACTIVATION_DESELECTED = 1; // 0x1
field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_DATA_KEY = "android.nfc.cardemulation.DATA";
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_GAIN_KEY = "android.nfc.cardemulation.GAIN";
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_TIMESTAMP_KEY = "android.nfc.cardemulation.TIMESTAMP";
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_A = 65; // 0x0041 'A'
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_B = 66; // 0x0042 'B'
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_F = 70; // 0x0046 'F'
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_TYPE_KEY = "android.nfc.cardemulation.TYPE";
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_OFF = 88; // 0x0058 'X'
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_ON = 79; // 0x004f 'O'
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_UNKNOWN = 85; // 0x0055 'U'
field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_APDU_SERVICE";
field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.host_apdu_service";
}
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/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/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index aca6d06..5d06978 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -3402,8 +3402,8 @@
new Key<Long>("android.sensor.exposureTime", long.class);
/**
- * <p>Duration from start of frame exposure to
- * start of next frame exposure.</p>
+ * <p>Duration from start of frame readout to
+ * start of next frame readout.</p>
* <p>The maximum frame rate that can be supported by a camera subsystem is
* a function of many factors:</p>
* <ul>
@@ -3464,6 +3464,10 @@
* <p>For more details about stalling, see {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
* OFF; otherwise the auto-exposure algorithm will override this value.</p>
+ * <p><em>Note:</em> Prior to Android 13, this field was described as measuring the duration from
+ * start of frame exposure to start of next frame exposure, which doesn't reflect the
+ * definition from sensor manufacturer. A mobile sensor defines the frame duration as
+ * intervals between sensor readouts.</p>
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
* See {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}, {@link android.hardware.camera2.params.StreamConfigurationMap }.
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 1c66f82..0d204f3 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -4103,8 +4103,8 @@
new Key<Long>("android.sensor.exposureTime", long.class);
/**
- * <p>Duration from start of frame exposure to
- * start of next frame exposure.</p>
+ * <p>Duration from start of frame readout to
+ * start of next frame readout.</p>
* <p>The maximum frame rate that can be supported by a camera subsystem is
* a function of many factors:</p>
* <ul>
@@ -4165,6 +4165,10 @@
* <p>For more details about stalling, see {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
* OFF; otherwise the auto-exposure algorithm will override this value.</p>
+ * <p><em>Note:</em> Prior to Android 13, this field was described as measuring the duration from
+ * start of frame exposure to start of next frame exposure, which doesn't reflect the
+ * definition from sensor manufacturer. A mobile sensor defines the frame duration as
+ * intervals between sensor readouts.</p>
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
* See {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}, {@link android.hardware.camera2.params.StreamConfigurationMap }.
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/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 0c95c2e..f6beec1 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -84,4 +84,6 @@
boolean isReaderOptionSupported();
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
boolean enableReaderOption(boolean enable);
+ boolean isObserveModeSupported();
+ boolean setObserveMode(boolean enabled);
}
diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl
index c7b3b2c..191385a 100644
--- a/core/java/android/nfc/INfcCardEmulation.aidl
+++ b/core/java/android/nfc/INfcCardEmulation.aidl
@@ -30,6 +30,7 @@
boolean isDefaultServiceForAid(int userHandle, in ComponentName service, String aid);
boolean setDefaultServiceForCategory(int userHandle, in ComponentName service, String category);
boolean setDefaultForNextTap(int userHandle, in ComponentName service);
+ boolean setServiceObserveModeDefault(int userId, in android.content.ComponentName service, boolean enable);
boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup);
boolean setOffHostForService(int userHandle, in ComponentName service, in String offHostSecureElement);
boolean unsetOffHostForService(int userHandle, in ComponentName service);
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index c897595..98a980f 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -1081,6 +1081,61 @@
}
}
+
+ /**
+ * Returns whether the device supports observer mode or not. When observe
+ * mode is enabled, the NFC hardware will listen for NFC readers, but not
+ * respond to them. When observe mode is disabled, the NFC hardware will
+ * resoond to the reader and proceed with the transaction.
+ * @return true if the mode is supported, false otherwise.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean isObserveModeSupported() {
+ try {
+ return sService.isObserveModeSupported();
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
+ }
+
+ /**
+ * Disables observe mode to allow the transaction to proceed. See
+ * {@link #isObserveModeSupported()} for a description of observe mode and
+ * use {@link #disallowTransaction()} to enable observe mode and block
+ * transactions again.
+ *
+ * @return boolean indicating success or failure.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean allowTransaction() {
+ try {
+ return sService.setObserveMode(false);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
+ }
+
+ /**
+ * Signals that the transaction has completed and observe mode may be
+ * reenabled. See {@link #isObserveModeSupported()} for a description of
+ * observe mode and use {@link #allowTransaction()} to disable observe
+ * mode and allow transactions to proceed.
+ *
+ * @return boolean indicating success or failure.
+ */
+
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean disallowTransaction() {
+ try {
+ return sService.setObserveMode(true);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
+ }
+
/**
* Resumes default polling for the current device state if polling is paused. Calling
* this while polling is not paused is a no-op.
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index d048b59..58b6179 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -328,6 +328,24 @@
return SELECTION_MODE_ASK_IF_CONFLICT;
}
}
+ /**
+ * Sets whether the system should default to observe mode or not when
+ * the service is in the foreground or the default payment service.
+ *
+ * @param service The component name of the service
+ * @param enable Whether the servic should default to observe mode or not
+ * @return whether the change was successful.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean setServiceObserveModeDefault(@NonNull ComponentName service, boolean enable) {
+ try {
+ return sService.setServiceObserveModeDefault(mContext.getUser().getIdentifier(),
+ service, enable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ }
+ return false;
+ }
/**
* Registers a list of AIDs for a specific category for the
diff --git a/core/java/android/nfc/cardemulation/HostApduService.java b/core/java/android/nfc/cardemulation/HostApduService.java
index 55d0e73..7cd2533 100644
--- a/core/java/android/nfc/cardemulation/HostApduService.java
+++ b/core/java/android/nfc/cardemulation/HostApduService.java
@@ -16,11 +16,14 @@
package android.nfc.cardemulation;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -29,6 +32,9 @@
import android.os.RemoteException;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* <p>HostApduService is a convenience {@link Service} class that can be
* extended to emulate an NFC card inside an Android
@@ -230,9 +236,99 @@
/**
* @hide
*/
+ public static final int MSG_POLLING_LOOP = 4;
+
+ /**
+ * @hide
+ */
public static final String KEY_DATA = "data";
/**
+ * POLLING_LOOP_TYPE_KEY is the Bundle key for the type of
+ * polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String POLLING_LOOP_TYPE_KEY = "android.nfc.cardemulation.TYPE";
+
+ /**
+ * POLLING_LOOP_TYPE_A is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop is for NFC-A.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_A = 'A';
+
+ /**
+ * POLLING_LOOP_TYPE_B is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop is for NFC-B.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_B = 'B';
+
+ /**
+ * POLLING_LOOP_TYPE_F is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop is for NFC-F.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_F = 'F';
+
+ /**
+ * POLLING_LOOP_TYPE_ON is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop turns on.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_ON = 'O';
+
+ /**
+ * POLLING_LOOP_TYPE_OFF is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop turns off.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_OFF = 'X';
+
+ /**
+ * POLLING_LOOP_TYPE_UNKNOWN is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop frame isn't recognized.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_UNKNOWN = 'U';
+
+ /**
+ * POLLING_LOOP_DATA is the Bundle key for the raw data of captured from
+ * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the frame type isn't recognized.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String POLLING_LOOP_DATA_KEY = "android.nfc.cardemulation.DATA";
+
+ /**
+ * POLLING_LOOP_GAIN_KEY is the Bundle key for the field strength of
+ * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the frame type isn't recognized.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String POLLING_LOOP_GAIN_KEY = "android.nfc.cardemulation.GAIN";
+
+ /**
+ * POLLING_LOOP_TIMESTAMP_KEY is the Bundle key for the timestamp of
+ * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the frame type isn't recognized.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String POLLING_LOOP_TIMESTAMP_KEY = "android.nfc.cardemulation.TIMESTAMP";
+
+ /**
+ * @hide
+ */
+ public static final String POLLING_LOOP_FRAMES_BUNDLE_KEY =
+ "android.nfc.cardemulation.POLLING_FRAMES";
+
+ /**
* Messenger interface to NfcService for sending responses.
* Only accessed on main thread by the message handler.
*
@@ -255,6 +351,7 @@
byte[] apdu = dataBundle.getByteArray(KEY_DATA);
if (apdu != null) {
+ HostApduService has = HostApduService.this;
byte[] responseApdu = processCommandApdu(apdu, null);
if (responseApdu != null) {
if (mNfcService == null) {
@@ -306,6 +403,12 @@
Log.e(TAG, "RemoteException calling into NfcService.");
}
break;
+ case MSG_POLLING_LOOP:
+ ArrayList<Bundle> frames =
+ msg.getData().getParcelableArrayList(POLLING_LOOP_FRAMES_BUNDLE_KEY,
+ Bundle.class);
+ processPollingFrames(frames);
+ break;
default:
super.handleMessage(msg);
}
@@ -366,6 +469,21 @@
}
}
+ /**
+ * This method is called when a polling frame has been received from a
+ * remote device. If the device is in observe mode, the service should
+ * call {@link NfcAdapter#allowTransaction()} once it is ready to proceed
+ * with the transaction. If the device is not in observe mode, the service
+ * can use this polling frame information to determine how to proceed if it
+ * subsequently has {@link #processCommandApdu(byte[], Bundle)} called. The
+ * service must override this method inorder to receive polling frames,
+ * otherwise the base implementation drops the frame.
+ *
+ * @param frame A description of the polling frame.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public void processPollingFrames(@NonNull List<Bundle> frame) {
+ }
/**
* <p>This method will be called when a command APDU has been received
diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig
index cd50ace..17e0427 100644
--- a/core/java/android/nfc/flags.aconfig
+++ b/core/java/android/nfc/flags.aconfig
@@ -20,3 +20,31 @@
description: "Flag for NFC user restriction"
bug: "291187960"
}
+
+flag {
+ name: "nfc_observe_mode"
+ namespace: "nfc"
+ description: "Enable NFC Observe Mode"
+ bug: "294217286"
+}
+
+flag {
+ name: "nfc_read_polling_loop"
+ namespace: "nfc"
+ description: "Enable NFC Polling Loop Notifications"
+ bug: "294217286"
+}
+
+flag {
+ name: "nfc_observe_mode_st_shim"
+ namespace: "nfc"
+ description: "Enable NFC Observe Mode ST shim"
+ bug: "294217286"
+}
+
+flag {
+ name: "nfc_read_polling_loop_st_shim"
+ namespace: "nfc"
+ description: "Enable NFC Polling Loop Notifications ST shim"
+ bug: "294217286"
+}
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/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 0ad6c99..b600b22 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -35,4 +35,11 @@
name: "fullscreen_dim_flag"
description: "Whether to allow showing fullscreen dim on ActivityEmbedding split"
bug: "253533308"
+}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "activity_embedding_interactive_divider_flag"
+ description: "Whether the interactive divider feature is enabled"
+ bug: "293654166"
}
\ 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/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3496994..698c5ba 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4303,6 +4303,9 @@
<!-- Whether the device must be screen on before routing data to this service.
The default is true.-->
<attr name="requireDeviceScreenOn" format="boolean"/>
+ <!-- Whether the device should default to observe mode when this service is
+ default or in the foreground. -->
+ <attr name="defaultToObserveMode" format="boolean"/>
</declare-styleable>
<!-- Use <code>offhost-apdu-service</code> as the root tag of the XML resource that
@@ -4327,6 +4330,9 @@
<!-- Whether the device must be screen on before routing data to this service.
The default is false.-->
<attr name="requireDeviceScreenOn"/>
+ <!-- Whether the device should default to observe mode when this service is
+ default or in the foreground. -->
+ <attr name="defaultToObserveMode"/>
</declare-styleable>
<!-- Specify one or more <code>aid-group</code> elements inside a
diff --git a/media/java/android/media/tv/ad/ITvAdManager.aidl b/media/java/android/media/tv/ad/ITvAdManager.aidl
new file mode 100644
index 0000000..92cc923
--- /dev/null
+++ b/media/java/android/media/tv/ad/ITvAdManager.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.ad;
+
+/**
+ * Interface to the TV AD service.
+ * @hide
+ */
+interface ITvAdManager {
+ void startAdService(in IBinder sessionToken, int userId);
+}
diff --git a/media/java/android/media/tv/ad/ITvAdSession.aidl b/media/java/android/media/tv/ad/ITvAdSession.aidl
new file mode 100644
index 0000000..b834f1b9
--- /dev/null
+++ b/media/java/android/media/tv/ad/ITvAdSession.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.ad;
+
+/**
+ * Sub-interface of ITvAdService which is created per session and has its own context.
+ * @hide
+ */
+oneway interface ITvAdSession {
+ void startAdService();
+}
diff --git a/media/java/android/media/tv/ad/TvAdManager.java b/media/java/android/media/tv/ad/TvAdManager.java
new file mode 100644
index 0000000..aa5a290
--- /dev/null
+++ b/media/java/android/media/tv/ad/TvAdManager.java
@@ -0,0 +1,65 @@
+/*
+ * 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.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Central system API to the overall client-side TV AD architecture, which arbitrates interaction
+ * between applications and AD services.
+ * @hide
+ */
+public class TvAdManager {
+ private static final String TAG = "TvAdManager";
+
+ private final ITvAdManager mService;
+ private final int mUserId;
+
+ public TvAdManager(ITvAdManager service, int userId) {
+ mService = service;
+ mUserId = userId;
+ }
+
+ /**
+ * The Session provides the per-session functionality of AD service.
+ */
+ public static final class Session {
+ private final IBinder mToken;
+ private final ITvAdManager mService;
+ private final int mUserId;
+
+ private Session(IBinder token, ITvAdManager service, int userId) {
+ mToken = token;
+ mService = service;
+ mUserId = userId;
+ }
+
+ void startAdService() {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.startAdService(mToken, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+}
diff --git a/media/java/android/media/tv/ad/TvAdService.java b/media/java/android/media/tv/ad/TvAdService.java
new file mode 100644
index 0000000..61101f0
--- /dev/null
+++ b/media/java/android/media/tv/ad/TvAdService.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.ad;
+
+import android.app.Service;
+import android.view.KeyEvent;
+
+/**
+ * The TvAdService class represents a TV client-side advertisement service.
+ * @hide
+ */
+public abstract class TvAdService extends Service {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "TvAdService";
+
+ /**
+ * Base class for derived classes to implement to provide a TV AD session.
+ */
+ public abstract static class Session implements KeyEvent.Callback {
+ /**
+ * Starts TvAdService session.
+ */
+ public void onStartAdService() {
+ }
+
+ void startAdService() {
+ onStartAdService();
+ }
+ }
+
+ /**
+ * Implements the internal ITvAdService interface.
+ */
+ public static class ITvAdSessionWrapper extends ITvAdSession.Stub {
+ private final Session mSessionImpl;
+
+ public ITvAdSessionWrapper(Session mSessionImpl) {
+ this.mSessionImpl = mSessionImpl;
+ }
+
+ @Override
+ public void startAdService() {
+ mSessionImpl.startAdService();
+ }
+ }
+}
diff --git a/media/java/android/media/tv/ad/TvAdView.java b/media/java/android/media/tv/ad/TvAdView.java
new file mode 100644
index 0000000..1a3771a
--- /dev/null
+++ b/media/java/android/media/tv/ad/TvAdView.java
@@ -0,0 +1,57 @@
+/*
+ * 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.content.Context;
+import android.util.Log;
+import android.view.ViewGroup;
+
+/**
+ * Displays contents of TV AD services.
+ * @hide
+ */
+public class TvAdView extends ViewGroup {
+ private static final String TAG = "TvAdView";
+ private static final boolean DEBUG = false;
+
+ // TODO: create session
+ private TvAdManager.Session mSession;
+
+ public TvAdView(Context context) {
+ super(context, /* attrs = */null, /* defStyleAttr = */0);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "onLayout (left=" + l + ", top=" + t + ", right=" + r + ", bottom=" + b + ",)");
+ }
+ }
+
+ /**
+ * Starts the AD service.
+ */
+ public void startAdService() {
+ if (DEBUG) {
+ Log.d(TAG, "start");
+ }
+ if (mSession != null) {
+ mSession.startAdService();
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 3928767..7eb7dac 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -208,12 +208,8 @@
val isSplitAroundTheFoldRequired by viewModel.isFoldSplitRequired.collectAsState()
val isSplitAroundTheFold =
foldPosture == FoldPosture.Tabletop && !outputOnly && isSplitAroundTheFoldRequired
- val currentSceneKey by
- remember(isSplitAroundTheFold) {
- mutableStateOf(
- if (isSplitAroundTheFold) SceneKeys.SplitSceneKey else SceneKeys.ContiguousSceneKey
- )
- }
+ val currentSceneKey =
+ if (isSplitAroundTheFold) SceneKeys.SplitSceneKey else SceneKeys.ContiguousSceneKey
SceneTransitionLayout(
currentScene = currentSceneKey,
@@ -405,8 +401,7 @@
if (visibility == UserInputAreaVisibility.INPUT_ONLY) {
PatternBouncer(
viewModel = nonNullViewModel,
- modifier =
- Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false).then(modifier)
+ modifier = modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
)
}
else -> Unit
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/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/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 508f52c..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
@@ -141,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/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index ced96f1..cd3ecb3 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -316,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")
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/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
index 12a083e..5e19439 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
@@ -116,7 +116,7 @@
)
override fun forceUpdate() {
- forceUpdates.tryEmit(Unit)
+ tileScope.launch { forceUpdates.emit(Unit) }
}
override fun onUserChanged(user: UserHandle) {
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/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/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/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 84d2b37..404621d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -34,7 +34,6 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_VOLUME_CONTROL;
import static com.android.internal.jank.InteractionJankMonitor.Configuration.Builder;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.volume.Events.DISMISS_REASON_POSTURE_CHANGED;
import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
@@ -83,7 +82,6 @@
import android.util.SparseBooleanArray;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
-import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.AccessibilityDelegate;
@@ -120,7 +118,6 @@
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.VolumeDialog;
@@ -304,7 +301,6 @@
private final DevicePostureController mDevicePostureController;
private @DevicePostureController.DevicePostureInt int mDevicePosture;
private int mOrientation;
- private final FeatureFlags mFeatureFlags;
private final Lazy<SecureSettings> mSecureSettings;
private int mDialogTimeoutMillis;
@@ -323,9 +319,7 @@
DevicePostureController devicePostureController,
Looper looper,
DumpManager dumpManager,
- FeatureFlags featureFlags,
Lazy<SecureSettings> secureSettings) {
- mFeatureFlags = featureFlags;
mContext =
new ContextThemeWrapper(context, R.style.volume_dialog_theme);
mHandler = new H(looper);
@@ -1373,14 +1367,12 @@
private void provideTouchFeedbackH(int newRingerMode) {
VibrationEffect effect = null;
- int hapticConstant = HapticFeedbackConstants.NO_HAPTICS;
switch (newRingerMode) {
case RINGER_MODE_NORMAL:
mController.scheduleTouchFeedback();
break;
case RINGER_MODE_SILENT:
effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
- hapticConstant = HapticFeedbackConstants.TOGGLE_OFF;
break;
case RINGER_MODE_VIBRATE:
// Feedback handled by onStateChange, for feedback both when user toggles
@@ -1388,11 +1380,8 @@
break;
default:
effect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
- hapticConstant = HapticFeedbackConstants.TOGGLE_ON;
}
- if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- mDialogView.performHapticFeedback(hapticConstant);
- } else if (effect != null) {
+ if (effect != null) {
mController.vibrate(effect);
}
}
@@ -1820,22 +1809,7 @@
&& mState.ringerModeInternal != -1
&& mState.ringerModeInternal != state.ringerModeInternal
&& state.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
-
- if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- if (mShowing) {
- // The dialog view is responsible for triggering haptics in the oneway API
- mDialogView.performHapticFeedback(HapticFeedbackConstants.TOGGLE_ON);
- }
- /*
- TODO(b/290642122): If the dialog is not showing, we have the case where haptics is
- enabled by dragging the volume slider of Settings to a value of 0. This must be
- handled by view Slices in Settings whilst using the performHapticFeedback API.
- */
-
- } else {
- // Old behavior only active if the oneway API is not used.
- mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK));
- }
+ mController.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK));
}
mState = state;
mDynamic.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index e3b3c21..53217d4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -22,7 +22,6 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.VolumeDialog;
@@ -65,7 +64,6 @@
CsdWarningDialog.Factory csdFactory,
DevicePostureController devicePostureController,
DumpManager dumpManager,
- FeatureFlags featureFlags,
Lazy<SecureSettings> secureSettings) {
VolumeDialogImpl impl = new VolumeDialogImpl(
context,
@@ -82,7 +80,6 @@
devicePostureController,
Looper.getMainLooper(),
dumpManager,
- featureFlags,
secureSettings);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
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 a6c4f19..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
@@ -240,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/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/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
deleted file mode 100644
index d3b7daa..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
+++ /dev/null
@@ -1,120 +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.qs.tiles.viewmodel
-
-import android.os.UserHandle
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.classifier.FalsingManagerFake
-import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-// TODO(b/299909368): Add more tests
-@MediumTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() {
-
- @Mock private lateinit var qsTileLogger: QSTileLogger
- @Mock private lateinit var qsTileAnalytics: QSTileAnalytics
-
- private val fakeUserRepository = FakeUserRepository()
- private val fakeQSTileDataInteractor = FakeQSTileDataInteractor<Any>()
- private val fakeQSTileUserActionInteractor = FakeQSTileUserActionInteractor<Any>()
- private val fakeDisabledByPolicyInteractor = FakeDisabledByPolicyInteractor()
- private val fakeFalsingManager = FalsingManagerFake()
-
- private val testCoroutineDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testCoroutineDispatcher)
-
- private lateinit var underTest: QSTileViewModel
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- underTest = createViewModel(testScope)
- }
-
- @Test
- fun testDoesntListenStateUntilCreated() =
- testScope.runTest {
- assertThat(fakeQSTileDataInteractor.dataRequests).isEmpty()
-
- assertThat(fakeQSTileDataInteractor.dataRequests).isEmpty()
-
- underTest.state.launchIn(backgroundScope)
- runCurrent()
-
- assertThat(fakeQSTileDataInteractor.dataRequests).isNotEmpty()
- assertThat(fakeQSTileDataInteractor.dataRequests.first())
- .isEqualTo(FakeQSTileDataInteractor.DataRequest(UserHandle.of(0)))
- }
-
- private fun createViewModel(
- scope: TestScope,
- config: QSTileConfig = TEST_QS_TILE_CONFIG,
- ): QSTileViewModel =
- QSTileViewModelImpl(
- config,
- { fakeQSTileUserActionInteractor },
- { fakeQSTileDataInteractor },
- {
- object : QSTileDataToStateMapper<Any> {
- override fun map(config: QSTileConfig, data: Any): QSTileState =
- QSTileState.build(
- { Icon.Resource(0, ContentDescription.Resource(0)) },
- ""
- ) {}
- }
- },
- fakeDisabledByPolicyInteractor,
- fakeUserRepository,
- fakeFalsingManager,
- qsTileAnalytics,
- qsTileLogger,
- FakeSystemClock(),
- testCoroutineDispatcher,
- scope.backgroundScope,
- )
-
- private companion object {
-
- val TEST_QS_TILE_CONFIG = QSTileConfigTestBuilder.build {}
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
new file mode 100644
index 0000000..3a0ebdb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.viewmodel
+
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class QSTileViewModelTest : SysuiTestCase() {
+
+ @Mock private lateinit var qsTileLogger: QSTileLogger
+ @Mock private lateinit var qsTileAnalytics: QSTileAnalytics
+
+ private val tileConfig =
+ QSTileConfigTestBuilder.build { policy = QSTilePolicy.Restricted("test_restriction") }
+
+ private val userRepository = FakeUserRepository()
+ private val tileDataInteractor = FakeQSTileDataInteractor<String>()
+ private val tileUserActionInteractor = FakeQSTileUserActionInteractor<String>()
+ private val disabledByPolicyInteractor = FakeDisabledByPolicyInteractor()
+ private val falsingManager = FalsingManagerFake()
+
+ private val testCoroutineDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testCoroutineDispatcher)
+
+ private lateinit var underTest: QSTileViewModel
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest = createViewModel(testScope)
+ }
+
+ @Test
+ fun stateReceivedForTheData() =
+ testScope.runTest {
+ val testTileData = "test_tile_data"
+ val states = collectValues(underTest.state)
+ runCurrent()
+
+ tileDataInteractor.emitData(testTileData)
+ runCurrent()
+
+ assertThat(states()).isNotEmpty()
+ assertThat(states().first().label).isEqualTo(testTileData)
+ verify(qsTileLogger).logInitialRequest(eq(tileConfig.tileSpec))
+ }
+
+ @Test
+ fun doesntListenDataIfStateIsntListened() =
+ testScope.runTest {
+ assertThat(tileDataInteractor.dataSubscriptionCount.value).isEqualTo(0)
+
+ underTest.state.launchIn(backgroundScope)
+ runCurrent()
+
+ assertThat(tileDataInteractor.dataSubscriptionCount.value).isEqualTo(1)
+ }
+
+ @Test
+ fun doesntListenAvailabilityIfAvailabilityIsntListened() =
+ testScope.runTest {
+ assertThat(tileDataInteractor.availabilitySubscriptionCount.value).isEqualTo(0)
+
+ underTest.isAvailable.launchIn(backgroundScope)
+ runCurrent()
+
+ assertThat(tileDataInteractor.availabilitySubscriptionCount.value).isEqualTo(1)
+ }
+
+ @Test
+ fun doesntListedDataAfterDestroy() =
+ testScope.runTest {
+ underTest.state.launchIn(backgroundScope)
+ underTest.isAvailable.launchIn(backgroundScope)
+ runCurrent()
+
+ underTest.destroy()
+ runCurrent()
+
+ assertThat(tileDataInteractor.dataSubscriptionCount.value).isEqualTo(0)
+ assertThat(tileDataInteractor.availabilitySubscriptionCount.value).isEqualTo(0)
+ }
+
+ @Test
+ fun forceUpdateTriggersData() =
+ testScope.runTest {
+ underTest.state.launchIn(backgroundScope)
+ runCurrent()
+
+ underTest.forceUpdate()
+ runCurrent()
+
+ assertThat(tileDataInteractor.triggers.last())
+ .isInstanceOf(DataUpdateTrigger.ForceUpdate::class.java)
+ verify(qsTileLogger).logForceUpdate(eq(tileConfig.tileSpec))
+ }
+
+ @Test
+ fun userChangeUpdatesData() =
+ testScope.runTest {
+ underTest.state.launchIn(backgroundScope)
+ runCurrent()
+
+ underTest.onUserChanged(USER)
+ runCurrent()
+
+ assertThat(tileDataInteractor.dataRequests.last())
+ .isEqualTo(FakeQSTileDataInteractor.DataRequest(USER))
+ }
+
+ @Test
+ fun userChangeUpdatesAvailability() =
+ testScope.runTest {
+ underTest.isAvailable.launchIn(backgroundScope)
+ runCurrent()
+
+ underTest.onUserChanged(USER)
+ runCurrent()
+
+ assertThat(tileDataInteractor.availabilityRequests.last())
+ .isEqualTo(FakeQSTileDataInteractor.AvailabilityRequest(USER))
+ }
+
+ private fun createViewModel(
+ scope: TestScope,
+ config: QSTileConfig = tileConfig,
+ ): QSTileViewModel =
+ QSTileViewModelImpl(
+ config,
+ { tileUserActionInteractor },
+ { tileDataInteractor },
+ {
+ object : QSTileDataToStateMapper<String> {
+ override fun map(config: QSTileConfig, data: String): QSTileState =
+ QSTileState.build(
+ { Icon.Resource(0, ContentDescription.Resource(0)) },
+ data
+ ) {}
+ }
+ },
+ disabledByPolicyInteractor,
+ userRepository,
+ falsingManager,
+ qsTileAnalytics,
+ qsTileLogger,
+ FakeSystemClock(),
+ testCoroutineDispatcher,
+ scope.backgroundScope,
+ )
+
+ private companion object {
+
+ val USER = UserHandle.of(1)!!
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
new file mode 100644
index 0000000..ea8acc7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
@@ -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 com.android.systemui.qs.tiles.viewmodel
+
+import androidx.test.filters.MediumTest
+import com.android.settingslib.RestrictedLockUtils
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
+import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameter
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+/** Tests all possible [QSTileUserAction]s. If you need */
+@MediumTest
+@RunWith(Parameterized::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class QSTileViewModelUserInputTest : SysuiTestCase() {
+
+ @Mock private lateinit var qsTileLogger: QSTileLogger
+ @Mock private lateinit var qsTileAnalytics: QSTileAnalytics
+
+ @Parameter lateinit var userAction: QSTileUserAction
+
+ private val tileConfig =
+ QSTileConfigTestBuilder.build { policy = QSTilePolicy.Restricted("test_restriction") }
+
+ private val userRepository = FakeUserRepository()
+ private val tileDataInteractor = FakeQSTileDataInteractor<String>()
+ private val tileUserActionInteractor = FakeQSTileUserActionInteractor<String>()
+ private val disabledByPolicyInteractor = FakeDisabledByPolicyInteractor()
+ private val falsingManager = FalsingManagerFake()
+
+ private val testCoroutineDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testCoroutineDispatcher)
+
+ private lateinit var underTest: QSTileViewModel
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest = createViewModel(testScope)
+ }
+
+ @Test
+ fun userInputTriggersData() =
+ testScope.runTest {
+ tileDataInteractor.emitData("initial_data")
+ underTest.state.launchIn(backgroundScope)
+ runCurrent()
+
+ underTest.onActionPerformed(userAction)
+ runCurrent()
+
+ assertThat(tileDataInteractor.triggers.last())
+ .isInstanceOf(DataUpdateTrigger.UserInput::class.java)
+ verify(qsTileLogger)
+ .logUserAction(eq(userAction), eq(tileConfig.tileSpec), eq(true), eq(true))
+ verify(qsTileLogger)
+ .logUserActionPipeline(
+ eq(tileConfig.tileSpec),
+ eq(userAction),
+ any(),
+ eq("initial_data")
+ )
+ verify(qsTileAnalytics).trackUserAction(eq(tileConfig), eq(userAction))
+ }
+
+ @Test
+ fun disabledByPolicyUserInputIsSkipped() =
+ testScope.runTest {
+ underTest.state.launchIn(backgroundScope)
+ disabledByPolicyInteractor.policyResult =
+ DisabledByPolicyInteractor.PolicyResult.TileDisabled(
+ RestrictedLockUtils.EnforcedAdmin()
+ )
+ runCurrent()
+
+ underTest.onActionPerformed(userAction)
+ runCurrent()
+
+ assertThat(tileDataInteractor.triggers.last())
+ .isNotInstanceOf(DataUpdateTrigger.UserInput::class.java)
+ verify(qsTileLogger)
+ .logUserActionRejectedByPolicy(eq(userAction), eq(tileConfig.tileSpec))
+ verify(qsTileAnalytics, never()).trackUserAction(any(), any())
+ }
+
+ @Test
+ fun falsedUserInputIsSkipped() =
+ testScope.runTest {
+ underTest.state.launchIn(backgroundScope)
+ falsingManager.setFalseLongTap(true)
+ falsingManager.setFalseTap(true)
+ runCurrent()
+
+ underTest.onActionPerformed(userAction)
+ runCurrent()
+
+ assertThat(tileDataInteractor.triggers.last())
+ .isNotInstanceOf(DataUpdateTrigger.UserInput::class.java)
+ verify(qsTileLogger)
+ .logUserActionRejectedByFalsing(eq(userAction), eq(tileConfig.tileSpec))
+ verify(qsTileAnalytics, never()).trackUserAction(any(), any())
+ }
+
+ @Test
+ fun userInputIsThrottled() =
+ testScope.runTest {
+ val inputCount = 100
+ underTest.state.launchIn(backgroundScope)
+
+ repeat(inputCount) { underTest.onActionPerformed(userAction) }
+ runCurrent()
+
+ assertThat(tileDataInteractor.triggers.size).isLessThan(inputCount)
+ }
+
+ private fun createViewModel(scope: TestScope): QSTileViewModel =
+ QSTileViewModelImpl(
+ tileConfig,
+ { tileUserActionInteractor },
+ { tileDataInteractor },
+ {
+ object : QSTileDataToStateMapper<String> {
+ override fun map(config: QSTileConfig, data: String): QSTileState =
+ QSTileState.build(
+ { Icon.Resource(0, ContentDescription.Resource(0)) },
+ data
+ ) {}
+ }
+ },
+ disabledByPolicyInteractor,
+ userRepository,
+ falsingManager,
+ qsTileAnalytics,
+ qsTileLogger,
+ FakeSystemClock(),
+ testCoroutineDispatcher,
+ scope.backgroundScope,
+ )
+
+ companion object {
+
+ @JvmStatic
+ @Parameterized.Parameters
+ fun data(): Iterable<QSTileUserAction> =
+ listOf(
+ QSTileUserAction.Click(null),
+ QSTileUserAction.LongClick(null),
+ )
+ }
+}
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/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/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 7456e00..8c823b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -20,7 +20,6 @@
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN;
import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN;
import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
@@ -68,7 +67,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.VolumeDialogController;
@@ -149,14 +147,13 @@
}
};
- private FakeFeatureFlags mFeatureFlags;
private int mLongestHideShowAnimationDuration = 250;
private FakeSettings mSecureSettings;
@Rule
public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
- @Before
+ @Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -179,8 +176,6 @@
mConfigurationController = new FakeConfigurationController();
- mFeatureFlags = new FakeFeatureFlags();
-
mSecureSettings = new FakeSettings();
when(mLazySecureSettings.get()).thenReturn(mSecureSettings);
@@ -200,7 +195,6 @@
mPostureController,
mTestableLooper.getLooper(),
mDumpManager,
- mFeatureFlags,
mLazySecureSettings);
mDialog.init(0, null);
State state = createShellState();
@@ -328,7 +322,6 @@
@Test
public void testVibrateOnRingerChangedToVibrate() {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
final State initialSilentState = new State();
initialSilentState.ringerModeInternal = AudioManager.RINGER_MODE_SILENT;
@@ -349,30 +342,7 @@
}
@Test
- public void testControllerDoesNotVibrateOnRingerChangedToVibrate_OnewayAPI_On() {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- final State initialSilentState = new State();
- initialSilentState.ringerModeInternal = AudioManager.RINGER_MODE_SILENT;
-
- final State vibrateState = new State();
- vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
-
- // change ringer to silent
- mDialog.onStateChangedH(initialSilentState);
-
- // expected: shouldn't call vibrate yet
- verify(mVolumeDialogController, never()).vibrate(any());
-
- // changed ringer to vibrate
- mDialog.onStateChangedH(vibrateState);
-
- // expected: vibrate method of controller is not used
- verify(mVolumeDialogController, never()).vibrate(any());
- }
-
- @Test
public void testNoVibrateOnRingerInitialization() {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
final State initialUnsetState = new State();
initialUnsetState.ringerModeInternal = -1;
@@ -390,29 +360,9 @@
}
@Test
- public void testControllerDoesNotVibrateOnRingerInitialization_OnewayAPI_On() {
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = -1;
-
- // ringer not initialized yet:
- mDialog.onStateChangedH(initialUnsetState);
-
- final State vibrateState = new State();
- vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
-
- // changed ringer to vibrate
- mDialog.onStateChangedH(vibrateState);
-
- // shouldn't call vibrate on the controller either
- verify(mVolumeDialogController, never()).vibrate(any());
- }
-
- @Test
public void testSelectVibrateFromDrawer() {
assumeHasDrawer();
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
final State initialUnsetState = new State();
initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
mDialog.onStateChangedH(initialUnsetState);
@@ -426,27 +376,9 @@
}
@Test
- public void testSelectVibrateFromDrawer_OnewayAPI_On() {
- assumeHasDrawer();
-
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
- mDialog.onStateChangedH(initialUnsetState);
-
- mActiveRinger.performClick();
- mDrawerVibrate.performClick();
-
- // Make sure we've actually changed the ringer mode.
- verify(mVolumeDialogController, times(1)).setRingerMode(
- AudioManager.RINGER_MODE_VIBRATE, false);
- }
-
- @Test
public void testSelectMuteFromDrawer() {
assumeHasDrawer();
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
final State initialUnsetState = new State();
initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
mDialog.onStateChangedH(initialUnsetState);
@@ -460,27 +392,9 @@
}
@Test
- public void testSelectMuteFromDrawer_OnewayAPI_On() {
- assumeHasDrawer();
-
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
- mDialog.onStateChangedH(initialUnsetState);
-
- mActiveRinger.performClick();
- mDrawerMute.performClick();
-
- // Make sure we've actually changed the ringer mode.
- verify(mVolumeDialogController, times(1)).setRingerMode(
- AudioManager.RINGER_MODE_SILENT, false);
- }
-
- @Test
public void testSelectNormalFromDrawer() {
assumeHasDrawer();
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
final State initialUnsetState = new State();
initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
mDialog.onStateChangedH(initialUnsetState);
@@ -493,23 +407,6 @@
AudioManager.RINGER_MODE_NORMAL, false);
}
- @Test
- public void testSelectNormalFromDrawer_OnewayAPI_On() {
- assumeHasDrawer();
-
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
- mDialog.onStateChangedH(initialUnsetState);
-
- mActiveRinger.performClick();
- mDrawerNormal.performClick();
-
- // Make sure we've actually changed the ringer mode.
- verify(mVolumeDialogController, times(1)).setRingerMode(
- RINGER_MODE_NORMAL, false);
- }
-
/**
* Ideally we would look at the ringer ImageView and check its assigned drawable id, but that
* API does not exist. So we do the next best thing; we check the cached icon id.
@@ -682,7 +579,6 @@
State state = createShellState();
state.ringerModeInternal = ringerMode;
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
mDialog.onStateChangedH(state);
mDialog.show(SHOW_REASON_UNKNOWN);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
index 1efa74b..62765d1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
@@ -20,7 +20,6 @@
class FakeDisabledByPolicyInteractor : DisabledByPolicyInteractor {
- var handleResult: Boolean = false
var policyResult: DisabledByPolicyInteractor.PolicyResult =
DisabledByPolicyInteractor.PolicyResult.TileEnabled
@@ -31,5 +30,9 @@
override fun handlePolicyResult(
policyResult: DisabledByPolicyInteractor.PolicyResult
- ): Boolean = handleResult
+ ): Boolean =
+ when (policyResult) {
+ is DisabledByPolicyInteractor.PolicyResult.TileEnabled -> false
+ is DisabledByPolicyInteractor.PolicyResult.TileDisabled -> true
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
index 2b3330f..3fcf8a9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
@@ -17,16 +17,21 @@
package com.android.systemui.qs.tiles.base.interactor
import android.os.UserHandle
-import javax.annotation.CheckReturnValue
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.flatMapLatest
-class FakeQSTileDataInteractor<T>(
- private val dataFlow: MutableSharedFlow<T> = MutableSharedFlow(replay = Int.MAX_VALUE),
- private val availabilityFlow: MutableSharedFlow<Boolean> =
- MutableSharedFlow(replay = Int.MAX_VALUE),
-) : QSTileDataInteractor<T> {
+class FakeQSTileDataInteractor<T> : QSTileDataInteractor<T> {
+
+ private val dataFlow: MutableSharedFlow<T> = MutableSharedFlow(replay = 1)
+ val dataSubscriptionCount
+ get() = dataFlow.subscriptionCount
+ private val availabilityFlow: MutableSharedFlow<Boolean> = MutableSharedFlow(replay = 1)
+ val availabilitySubscriptionCount
+ get() = availabilityFlow.subscriptionCount
+
+ private val mutableTriggers = mutableListOf<DataUpdateTrigger>()
+ val triggers: List<DataUpdateTrigger> = mutableTriggers
private val mutableDataRequests = mutableListOf<DataRequest>()
val dataRequests: List<DataRequest> = mutableDataRequests
@@ -34,14 +39,17 @@
private val mutableAvailabilityRequests = mutableListOf<AvailabilityRequest>()
val availabilityRequests: List<AvailabilityRequest> = mutableAvailabilityRequests
- @CheckReturnValue fun emitData(data: T): Boolean = dataFlow.tryEmit(data)
+ suspend fun emitData(data: T): Unit = dataFlow.emit(data)
fun tryEmitAvailability(isAvailable: Boolean): Boolean = availabilityFlow.tryEmit(isAvailable)
suspend fun emitAvailability(isAvailable: Boolean) = availabilityFlow.emit(isAvailable)
override fun tileData(user: UserHandle, triggers: Flow<DataUpdateTrigger>): Flow<T> {
mutableDataRequests.add(DataRequest(user))
- return triggers.flatMapLatest { dataFlow }
+ return triggers.flatMapLatest {
+ mutableTriggers.add(it)
+ dataFlow
+ }
}
override fun availability(user: UserHandle): Flow<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/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/README-ravenwood+mockito.md b/ravenwood/README-ravenwood+mockito.md
new file mode 100644
index 0000000..6adb6144
--- /dev/null
+++ b/ravenwood/README-ravenwood+mockito.md
@@ -0,0 +1,24 @@
+# Ravenwood and Mockito
+
+Last update: 2023-11-13
+
+- As of 2023-11-13, `external/mockito` is based on version 2.x.
+- Mockito didn't support static mocking before 3.4.0.
+ See: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#48
+
+- Latest Mockito is 5.*. According to https://github.com/mockito/mockito:
+ `Mockito 3 does not introduce any breaking API changes, but now requires Java 8 over Java 6 for Mockito 2. Mockito 4 removes deprecated API. Mockito 5 switches the default mockmaker to mockito-inline, and now requires Java 11.`
+
+- Mockito now supports Android natively.
+ See: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#0.1
+ - But it's unclear at this point to omakoto@ how the `mockito-android` module is built.
+
+- Potential plan:
+ - Ideal option:
+ - If we can update `external/mockito`, that'd be great, but it may not work because
+ Mockito has removed the deprecated APIs.
+ - Second option:
+ - Import the latest mockito as `external/mockito-new`, and require ravenwood
+ to use this one.
+ - The latest mockito needs be exposed to all of 1) device tests, 2) host tests, and 3) ravenwood tests.
+ - This probably will require the latest `bytebuddy` and `objenesis`.
\ No newline at end of file
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..4135022
--- /dev/null
+++ b/ravenwood/mockito/Android.bp
@@ -0,0 +1,72 @@
+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,
+}
+
+android_test {
+ name: "RavenwoodMockitoTest_device",
+
+ srcs: [
+ "test/**/*.java",
+ ],
+ static_libs: [
+ "junit",
+ "truth",
+
+ "androidx.test.rules",
+
+ "ravenwood-junit",
+
+ "mockito-target-extended-minus-junit4",
+ ],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+ jni_libs: [
+ // Required by mockito
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+ test_suites: [
+ "device-tests",
+ ],
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/ravenwood/mockito/AndroidManifest.xml b/ravenwood/mockito/AndroidManifest.xml
new file mode 100644
index 0000000..15f0a29
--- /dev/null
+++ b/ravenwood/mockito/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.ravenwood.mockitotest">
+
+ <application android:debuggable="true" >
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.ravenwood.mockitotest"
+ />
+</manifest>
diff --git a/ravenwood/mockito/AndroidTest.xml b/ravenwood/mockito/AndroidTest.xml
new file mode 100644
index 0000000..96bc275
--- /dev/null
+++ b/ravenwood/mockito/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs Frameworks Services Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="RavenwoodMockitoTest_device.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="FrameworksMockingServicesTests" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.ravenwood.mockitotest" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java b/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java
new file mode 100644
index 0000000..36fa3dd
--- /dev/null
+++ b/ravenwood/mockito/test/com/android/ravenwood/mockito/RavenwoodMockitoTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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 android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+public class RavenwoodMockitoTest {
+ @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+
+// Use this to mock static methods, which isn't supported by mockito 2.
+// Mockito supports static mocking since 3.4.0:
+// See: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#48
+
+// private MockitoSession mMockingSession;
+//
+// @Before
+// public void setUp() {
+// mMockingSession = mockitoSession()
+// .strictness(Strictness.LENIENT)
+// .mockStatic(RavenwoodMockitoTest.class)
+// .startMocking();
+// }
+//
+// @After
+// public void tearDown() {
+// if (mMockingSession != null) {
+// mMockingSession.finishMocking();
+// }
+// }
+
+ @Test
+ public void testMockJdkClass() {
+ Process object = mock(Process.class);
+
+ when(object.exitValue()).thenReturn(42);
+
+ assertThat(object.exitValue()).isEqualTo(42);
+ }
+
+ /*
+ - Intent can't be mocked because of the dependency to `org.xmlpull.v1.XmlPullParser`.
+ (The error says "Mockito can only mock non-private & non-final classes", but that's likely a
+ red-herring.)
+
+STACKTRACE:
+org.mockito.exceptions.base.MockitoException:
+Mockito cannot mock this class: class android.content.Intent.
+
+ :
+
+Underlying exception : java.lang.IllegalArgumentException: Could not create type
+ at com.android.ravenwood.mockito.RavenwoodMockitoTest.testMockAndroidClass1
+ at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+
+ :
+
+Caused by: java.lang.ClassNotFoundException: org.xmlpull.v1.XmlPullParser
+ at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
+ at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
+ at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
+ ... 54 more
+ */
+ @Test
+ @IgnoreUnderRavenwood
+ public 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/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/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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 707e990..2ec3700 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -11449,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/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/proguard.flags b/services/proguard.flags
index 261bb7c..407505d 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -14,13 +14,20 @@
}
# APIs referenced by dependent JAR files and modules
--keep @interface android.annotation.SystemApi
+# TODO(b/300514883): Pull @SystemApi keep rules from system-api.pro.
+-keep interface android.annotation.SystemApi
-keep @android.annotation.SystemApi class * {
public protected *;
}
-keepclasseswithmembers class * {
@android.annotation.SystemApi *;
}
+# Also ensure nested classes are kept. This is overly conservative, but handles
+# cases where such classes aren't explicitly marked @SystemApi.
+-if @android.annotation.SystemApi class *
+-keep public class <1>$** {
+ public protected *;
+}
# Derivatives of SystemService and other services created via reflection
-keep,allowoptimization,allowaccessmodification class * extends com.android.server.SystemService {
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/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/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/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/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
index b133c2a..248121c 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
@@ -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
+}