Merge "Test permissions to launch activities on secondary displays"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 1871be7..c85c3542 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -795,9 +795,12 @@
<string name="snsr_device_admin_receiver">Sensor Tests Device Admin Receiver</string>
<string name="snsr_test_summary">Tests passed: %1$d, Tests skipped: %2$d, Tests failed: %3$d</string>
<string name="snsr_test_complete">Test completed without errors.</string>
- <string name="snsr_test_complete_with_errors">Test completed with errors: those errors
- might degrade the user experience and might cause some applications to misbehave. They are
- not required for Android Compatibility at this time, but will in a future release.</string>
+ <string name="snsr_test_complete_with_errors">Test completed with errors which indicates
+ the device does not meet Android compatibility requirement.
+ This will degrade user experience and cause applications to misbehave.
+ To help debugging, please take a bug report and back up contents under
+ /sdcard/sensorTests of the device under test.
+ </string>
<string name="snsr_test_pass">PASS</string>
<string name="snsr_test_skipped">SKIPPED</string>
<string name="snsr_test_fail">FAIL</string>
@@ -2313,6 +2316,13 @@
6) Unlock the device.\n
7) Repeat steps (1) through (6) for each screen lock type other than \"None\".
</string>
+ <string name="enterprise_privacy_add_account_negative">Add account disclosure</string>
+ <string name="enterprise_privacy_add_account_negative_info">
+ Please do the following:\n
+ 1) Press the Go button to open Settings.\n
+ 2) In the screen that opens, verify that you are not told that the device is managed.\n
+ 3) Use the Back button to return to this page.
+ </string>
<!-- Strings for DeviceOwnerPositiveTestActivity -->
<string name="positive_device_owner">Device Owner Tests</string>
@@ -2817,6 +2827,60 @@
<string name="enterprise_privacy_retrieve_security_logs">Retrieve Security Logs</string>
<string name="enterprise_privacy_clear_organization">Clear Org</string>
<string name="enterprise_privacy_set_organization">Set Org</string>
+ <string name="enterprise_privacy_enterprise_installed_apps">Enterprise-installed apps</string>
+ <string name="enterprise_privacy_enterprise_installed_apps_info">
+ Please do the following:\n
+ 1) You should have received NotificationBot.apk together with the CTS verifier. If you built the CTS verifier yourself, build the NotificationBot.apk by issuing the following command on the host:\n
+ make NotificationBot\n
+ 2) Upload the NotificationBot.apk to your device by issuing the following command on the host:\n
+ adb push /path/to/NotificationBot.apk /sdcard\n
+ 3) Press the Uninstall button.\n
+ 4) Press the Open Settings button.\n
+ 5) In the screen that opens, verify that you are not told that your administrator installed any apps.\n
+ 6) Use the Back button to return to this page.\n
+ 7) Press the Install button.\n
+ 8) Repeat steps (4) through (6), verifying that in step (5), you are told now that your administrator installed one app.\n
+ 9) Press the Uninstall button.\n
+ 10) Issue the following command on the host:\n
+ adb shell rm /sdcard/NotificationBot.apk
+ </string>
+ <string name="enterprise_privacy_install">Install</string>
+ <string name="enterprise_privacy_uninstall">Uninstall</string>
+ <string name="enterprise_privacy_admin_granted_location_access">Location access permission</string>
+ <string name="enterprise_privacy_admin_granted_location_access_info">
+ Please do the following:\n
+ 1) Press the Reset button.\n
+ 2) Press the Open Settings button.\n
+ 3) In the screen that opens, verify that you are not told that your administrator has allowed any apps to access your location.\n
+ 4) Use the Back button to return to this page.\n
+ 5) Press the Grant button.\n
+ 6) Repeat steps (2) through (4), verifying that in step (3), you are told now that your administrator has allowed one app to access your location.\n
+ 7) Press the Reset button.
+ </string>
+ <string name="enterprise_privacy_reset">Reset</string>
+ <string name="enterprise_privacy_grant">Grant</string>
+ <string name="enterprise_privacy_admin_granted_microphone_access">Microphone access permission</string>
+ <string name="enterprise_privacy_admin_granted_microphone_access_info">
+ Please do the following:\n
+ 1) Press the Reset button.\n
+ 2) Press the Open Settings button.\n
+ 3) In the screen that opens, verify that you are not told that your administrator has allowed any apps to access your microphone.\n
+ 4) Use the Back button to return to this page.\n
+ 5) Press the Grant button.\n
+ 6) Repeat steps (2) through (4), verifying that in step (3), you are told now that your administrator has allowed one app to access your microphone.\n
+ 7) Press the Reset button.
+ </string>
+ <string name="enterprise_privacy_admin_granted_camera_access">Camera access permission</string>
+ <string name="enterprise_privacy_admin_granted_camera_access_info">
+ Please do the following:\n
+ 1) Press the Reset button.\n
+ 2) Press the Open Settings button.\n
+ 3) In the screen that opens, verify that you are not told that your administrator has allowed any apps to access your camera.\n
+ 4) Use the Back button to return to this page.\n
+ 5) Press the Grant button.\n
+ 6) Repeat steps (2) through (4), verifying that in step (3), you are told now that your administrator has allowed one app to access your camera.\n
+ 7) Press the Reset button.
+ </string>
<string name="enterprise_privacy_always_on_vpn">Always-on VPN</string>
<string name="enterprise_privacy_always_on_vpn_info">
Please do the following:\n
@@ -2886,6 +2950,20 @@
11) Unlock the device.\n
12) Repeat steps (1) through (11) for each screen lock type other than \"None\".
</string>
+ <string name="enterprise_privacy_add_account">Add account disclosure</string>
+ <string name="enterprise_privacy_add_account_info">
+ Please do the following:\n
+ 1) Press the Clear Org button.\n
+ 2) Press the Open Settings button.\n
+ 3) In the screen that opens, verify that you are told that the device is managed.\n
+ 4) Use the Back button to return to this page.\n
+ 5) Press the Set Org button.\n
+ 6) Press the Open Settings button.\n
+ 7) In the screen that opens, verify that you are told that the device is managed by \"Foo, Inc.\".\n
+ 8) Tap the \"Learn more\" link.\n
+ 9) Verify that a screen informing you what your managing organization can do is shown.\n
+ 11) Use the Back button to return to this page.
+ </string>
<!-- Strings for Network Logging Tests -->
<string name="device_owner_network_logging_ui">Network Logging UI</string>
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 4de0251..028639d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -16,11 +16,14 @@
package com.android.cts.verifier.managedprovisioning;
+import android.Manifest;
import android.app.Activity;
+import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageInstaller;
import android.graphics.BitmapFactory;
import android.net.ProxyInfo;
import android.os.Bundle;
@@ -31,7 +34,10 @@
import com.android.cts.verifier.R;
import com.android.cts.verifier.managedprovisioning.Utils;
-import java.lang.reflect.Method;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -69,6 +75,9 @@
public static final String COMMAND_SET_ORGANIZATION_NAME = "set-organization-name";
public static final String COMMAND_ENABLE_NETWORK_LOGGING = "enable-network-logging";
public static final String COMMAND_DISABLE_NETWORK_LOGGING = "disable-network-logging";
+ public static final String COMMAND_INSTALL_HELPER_PACKAGE = "install-helper-package";
+ public static final String COMMAND_UNINSTALL_HELPER_PACKAGE = "uninstall-helper-package";
+ public static final String COMMAND_SET_PERMISSION_GRANT_STATE = "set-permission-grant-state";
public static final String COMMAND_CREATE_MANAGED_PROFILE = "create-managed-profile";
public static final String COMMAND_REMOVE_MANAGED_PROFILE = "remove-managed-profile";
public static final String COMMAND_SET_ALWAYS_ON_VPN = "set-always-on-vpn";
@@ -88,6 +97,20 @@
"com.android.cts.verifier.managedprovisioning.extra.VALUE";
public static final String EXTRA_ORGANIZATION_NAME =
"com.android.cts.verifier.managedprovisioning.extra.ORGANIZATION_NAME";
+ public static final String EXTRA_PERMISSION =
+ "com.android.cts.verifier.managedprovisioning.extra.PERMISSION";
+ public static final String EXTRA_GRANT_STATE =
+ "com.android.cts.verifier.managedprovisioning.extra.GRANT_STATE";
+
+ // We care about installing and uninstalling only. It does not matter what apk is used.
+ // NotificationBot.apk is a good choice because it comes bundled with the CTS verifier.
+ protected static final String HELPER_APP_LOCATION = "/sdcard/NotificationBot.apk";
+ protected static final String HELPER_APP_PKG = "com.android.cts.robot";
+
+ public static final String ACTION_INSTALL_COMPLETE =
+ "com.android.cts.verifier.managedprovisioning.action.ACTION_INSTALL_COMPLETE";
+ public static final String ACTION_UNINSTALL_COMPLETE =
+ "com.android.cts.verifier.managedprovisioning.action.ACTION_UNINSTALL_COMPLETE";
private ComponentName mAdmin;
private DevicePolicyManager mDpm;
@@ -203,17 +226,9 @@
if (!mDpm.isDeviceOwnerApp(getPackageName())) {
return;
}
- // STOPSHIP(b/33068581): Network logging will be un-hidden for O. Remove
- // reflection when the un-hiding happens.
- final Method setNetworkLoggingEnabledMethod =
- DevicePolicyManager.class.getDeclaredMethod(
- "setNetworkLoggingEnabled", ComponentName.class, boolean.class);
- final Method retrieveNetworkLogsMethod =
- DevicePolicyManager.class.getDeclaredMethod(
- "retrieveNetworkLogs", ComponentName.class, long.class);
- setNetworkLoggingEnabledMethod.invoke(mDpm, mAdmin, true);
- retrieveNetworkLogsMethod.invoke(mDpm, mAdmin, 0 /* batchToken */);
- setNetworkLoggingEnabledMethod.invoke(mDpm, mAdmin, false);
+ mDpm.setNetworkLoggingEnabled(mAdmin, true);
+ mDpm.retrieveNetworkLogs(mAdmin, 0 /* batchToken */);
+ mDpm.setNetworkLoggingEnabled(mAdmin, false);
} break;
case COMMAND_RETRIEVE_SECURITY_LOGS: {
if (!mDpm.isDeviceOwnerApp(getPackageName())) {
@@ -240,6 +255,18 @@
}
mDpm.setNetworkLoggingEnabled(mAdmin, false);
} break;
+ case COMMAND_INSTALL_HELPER_PACKAGE: {
+ installHelperPackage();
+ } break;
+ case COMMAND_UNINSTALL_HELPER_PACKAGE: {
+ uninstallHelperPackage();
+ } break;
+ case COMMAND_SET_PERMISSION_GRANT_STATE: {
+ mDpm.setPermissionGrantState(mAdmin, getPackageName(),
+ intent.getStringExtra(EXTRA_PERMISSION),
+ intent.getIntExtra(EXTRA_GRANT_STATE,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT));
+ } break;
case COMMAND_CREATE_MANAGED_PROFILE: {
if (!mDpm.isDeviceOwnerApp(getPackageName())) {
return;
@@ -289,7 +316,6 @@
}
mDpm.setRecommendedGlobalProxy(mAdmin, null);
}
-
}
} catch (Exception e) {
Log.e(TAG, "Failed to execute command: " + intent, e);
@@ -298,6 +324,32 @@
}
}
+ private void installHelperPackage() throws Exception {
+ final PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
+ final PackageInstaller.Session session = packageInstaller.openSession(
+ packageInstaller.createSession(new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL)));
+ final File file = new File(HELPER_APP_LOCATION);
+ final InputStream in = new FileInputStream(file);
+ final OutputStream out = session.openWrite("CommandReceiverActivity", 0, file.length());
+ final byte[] buffer = new byte[65536];
+ int count;
+ while ((count = in.read(buffer)) != -1) {
+ out.write(buffer, 0, count);
+ }
+ session.fsync(out);
+ in.close();
+ out.close();
+ session.commit(PendingIntent.getBroadcast(this, 0, new Intent(ACTION_INSTALL_COMPLETE), 0)
+ .getIntentSender());
+ }
+
+ private void uninstallHelperPackage() {
+ getPackageManager().getPackageInstaller().uninstall(HELPER_APP_PKG,
+ PendingIntent.getBroadcast(this, 0, new Intent(ACTION_UNINSTALL_COMPLETE), 0)
+ .getIntentSender());
+ }
+
private void clearAllPolicies() throws Exception {
clearProfileOwnerRelatedPolicies();
@@ -322,13 +374,20 @@
mDpm.setKeyguardDisabled(mAdmin, false);
mDpm.setAutoTimeRequired(mAdmin, false);
mDpm.setStatusBarDisabled(mAdmin, false);
- // STOPSHIP(b/33068581): Network logging will be un-hidden for O. Remove reflection when the
- // un-hiding happens.
- final Method setNetworkLoggingEnabledMethod = DevicePolicyManager.class.getDeclaredMethod(
- "setNetworkLoggingEnabled", ComponentName.class, boolean.class);
- setNetworkLoggingEnabledMethod.invoke(mDpm, mAdmin, false);
mDpm.setOrganizationName(mAdmin, null);
+ mDpm.setNetworkLoggingEnabled(mAdmin, false);
+ mDpm.setPermissionGrantState(mAdmin, getPackageName(),
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+ mDpm.setPermissionGrantState(mAdmin, getPackageName(), Manifest.permission.RECORD_AUDIO,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+ mDpm.setPermissionGrantState(mAdmin, getPackageName(), Manifest.permission.CAMERA,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+ mDpm.setAlwaysOnVpnPackage(mAdmin, null, false);
mDpm.setRecommendedGlobalProxy(mAdmin, null);
+
+ uninstallHelperPackage();
+ removeManagedProfile();
}
private void clearProfileOwnerRelatedPolicies() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerNegativeTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerNegativeTestActivity.java
index cee7307..078e9ce 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerNegativeTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerNegativeTestActivity.java
@@ -45,6 +45,8 @@
= "ENTERPRISE_PRIVACY_QUICK_SETTINGS_NEGATIVE";
private static final String ENTERPRISE_PRIVACY_KEYGUARD_NEGATIVE
= "ENTERPRISE_PRIVACY_KEYGUARD_NEGATIVE";
+ private static final String ENTERPRISE_PRIVACY_ADD_ACCOUNT_NEGATIVE
+ = "ENTERPRISE_PRIVACY_ADD_ACCOUNT_NEGATIVE";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -100,6 +102,10 @@
R.string.enterprise_privacy_keyguard_negative,
R.string.enterprise_privacy_keyguard_negative_info,
new ButtonInfo(R.string.go_button_text, new Intent(Settings.ACTION_SETTINGS))));
+ adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_ADD_ACCOUNT_NEGATIVE,
+ R.string.enterprise_privacy_add_account_negative,
+ R.string.enterprise_privacy_add_account_negative_info,
+ new ButtonInfo(R.string.go_button_text, new Intent(Settings.ACTION_ADD_ACCOUNT))));
}
/**
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
index 034e52e..54f1b79 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
@@ -16,6 +16,7 @@
package com.android.cts.verifier.managedprovisioning;
+import android.Manifest;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Intent;
@@ -43,6 +44,14 @@
private static final String ENTERPRISE_PRIVACY_BUG_REPORT = "ENTERPRISE_PRIVACY_BUG_REPORT";
private static final String ENTERPRISE_PRIVACY_SECURITY_LOGGING
= "ENTERPRISE_PRIVACY_SECURITY_LOGGING";
+ private static final String ENTERPRISE_PRIVACY_ENTERPRISE_INSTALLED_APPS
+ = "ENTERPRISE_PRIVACY_ENTERPRISE_INSTALLED_APPS";
+ private static final String ENTERPRISE_PRIVACY_LOCATION_ACCESS
+ = "ENTERPRISE_PRIVACY_LOCATION_ACCESS";
+ private static final String ENTERPRISE_PRIVACY_MICROPHONE_ACCESS
+ = "ENTERPRISE_PRIVACY_MICROPHONE_ACCESS";
+ private static final String ENTERPRISE_PRIVACY_CAMERA_ACCESS
+ = "ENTERPRISE_PRIVACY_CAMERA_ACCESS";
private static final String ENTERPRISE_PRIVACY_ALWAYS_ON_VPN
= "ENTERPRISE_PRIVACY_ALWAYS_ON_VPN";
private static final String ENTERPRISE_PRIVACY_COMP_ALWAYS_ON_VPN
@@ -52,6 +61,7 @@
private static final String ENTERPRISE_PRIVACY_QUICK_SETTINGS
= "ENTERPRISE_PRIVACY_QUICK_SETTINGS";
private static final String ENTERPRISE_PRIVACY_KEYGUARD = "ENTERPRISE_PRIVACY_KEYGUARD";
+ private static final String ENTERPRISE_PRIVACY_ADD_ACCOUNT = "ENTERPRISE_PRIVACY_ADD_ACCOUNT";
public static final String EXTRA_TEST_ID =
"com.android.cts.verifier.managedprovisioning.extra.TEST_ID";
@@ -95,6 +105,24 @@
new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS))});
}
+ private TestListItem buildAdminGrantedPermissionTest(String id, int titleRes, int infoRes,
+ String permission) {
+ return createInteractiveTestItem(this, id, titleRes, infoRes,
+ new ButtonInfo[] {
+ new ButtonInfo(R.string.enterprise_privacy_reset, buildCommandIntent(
+ CommandReceiverActivity.COMMAND_SET_PERMISSION_GRANT_STATE)
+ .putExtra(CommandReceiverActivity.EXTRA_PERMISSION, permission)
+ .putExtra(CommandReceiverActivity.EXTRA_GRANT_STATE,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT)),
+ new ButtonInfo(R.string.enterprise_privacy_grant, buildCommandIntent(
+ CommandReceiverActivity.COMMAND_SET_PERMISSION_GRANT_STATE)
+ .putExtra(CommandReceiverActivity.EXTRA_PERMISSION, permission)
+ .putExtra(CommandReceiverActivity.EXTRA_GRANT_STATE,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED)),
+ new ButtonInfo(R.string.enterprise_privacy_open_settings,
+ new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS))});
+ }
+
private void addTestsToAdapter(final ArrayTestListAdapter adapter) {
adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_PAGE,
R.string.enterprise_privacy_page,
@@ -115,6 +143,30 @@
R.string.enterprise_privacy_security_logging_info,
R.string.enterprise_privacy_retrieve_security_logs,
CommandReceiverActivity.COMMAND_RETRIEVE_SECURITY_LOGS));
+ adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_ENTERPRISE_INSTALLED_APPS,
+ R.string.enterprise_privacy_enterprise_installed_apps,
+ R.string.enterprise_privacy_enterprise_installed_apps_info,
+ new ButtonInfo[] {
+ new ButtonInfo(R.string.enterprise_privacy_install,
+ buildCommandIntent(
+ CommandReceiverActivity.COMMAND_INSTALL_HELPER_PACKAGE)),
+ new ButtonInfo(R.string.enterprise_privacy_uninstall,
+ buildCommandIntent(CommandReceiverActivity
+ .COMMAND_UNINSTALL_HELPER_PACKAGE)),
+ new ButtonInfo(R.string.enterprise_privacy_open_settings,
+ new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS))}));
+ adapter.add(buildAdminGrantedPermissionTest(ENTERPRISE_PRIVACY_LOCATION_ACCESS,
+ R.string.enterprise_privacy_admin_granted_location_access,
+ R.string.enterprise_privacy_admin_granted_location_access_info,
+ Manifest.permission.ACCESS_FINE_LOCATION));
+ adapter.add(buildAdminGrantedPermissionTest(ENTERPRISE_PRIVACY_MICROPHONE_ACCESS,
+ R.string.enterprise_privacy_admin_granted_microphone_access,
+ R.string.enterprise_privacy_admin_granted_microphone_access_info,
+ Manifest.permission.RECORD_AUDIO));
+ adapter.add(buildAdminGrantedPermissionTest(ENTERPRISE_PRIVACY_CAMERA_ACCESS,
+ R.string.enterprise_privacy_admin_granted_camera_access,
+ R.string.enterprise_privacy_admin_granted_camera_access_info,
+ Manifest.permission.CAMERA));
adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_ALWAYS_ON_VPN,
R.string.enterprise_privacy_always_on_vpn,
R.string.enterprise_privacy_always_on_vpn_info,
@@ -179,6 +231,20 @@
CommandReceiverActivity.COMMAND_SET_ORGANIZATION_NAME)
.putExtra(CommandReceiverActivity.EXTRA_ORGANIZATION_NAME,
"Foo, Inc."))}));
+ adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_ADD_ACCOUNT,
+ R.string.enterprise_privacy_add_account,
+ R.string.enterprise_privacy_add_account_info,
+ new ButtonInfo[] {
+ new ButtonInfo(R.string.enterprise_privacy_open_settings,
+ new Intent(Settings.ACTION_ADD_ACCOUNT)),
+ new ButtonInfo(R.string.enterprise_privacy_clear_organization,
+ buildCommandIntent(
+ CommandReceiverActivity.COMMAND_SET_ORGANIZATION_NAME)),
+ new ButtonInfo(R.string.enterprise_privacy_set_organization,
+ buildCommandIntent(
+ CommandReceiverActivity.COMMAND_SET_ORGANIZATION_NAME)
+ .putExtra(CommandReceiverActivity.EXTRA_ORGANIZATION_NAME,
+ "Foo, Inc."))}));
}
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java
index 2f7619c..e767f7f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java
@@ -22,6 +22,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -52,6 +53,7 @@
private static final String NFC_BEAM_ACTIVITY = "com.android.nfc.BeamShareActivity";
private static final String SAMPLE_IMAGE_FILENAME = "image_to_share.jpg";
private static final String SAMPLE_IMAGE_CONTENT = "sample image";
+ private static final String SAMPLE_TEXT = "sample text";
private static final int MARGIN = 80;
private static final int TEXT_SIZE = 200;
@@ -76,11 +78,28 @@
UserManager.DISALLOW_OUTGOING_BEAM);
}
- final Uri uri = createUriForImage(SAMPLE_IMAGE_FILENAME, SAMPLE_IMAGE_CONTENT);
- Uri[] uris = new Uri[] { uri };
-
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
- mNfcAdapter.setBeamPushUris(uris, this);
+
+ final Intent shareIntent = new Intent(Intent.ACTION_SEND);
+
+ // Sending a large amount of data requires hand-over to bluetooth, so determine here
+ // if supported by the device. If bluetooth is not supported, a simple text message
+ // will be transferred instead.
+ final boolean hasBluetooth =
+ getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+
+ if (hasBluetooth) {
+ final Uri uri = createUriForImage(SAMPLE_IMAGE_FILENAME, SAMPLE_IMAGE_CONTENT);
+ Uri[] uris = new Uri[]{uri};
+
+ mNfcAdapter.setBeamPushUris(uris, this);
+
+ shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
+ shareIntent.setType("image/jpg");
+ shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } else {
+ shareIntent.putExtra(Intent.EXTRA_TEXT, SAMPLE_TEXT);
+ }
findViewById(R.id.manual_beam_button).setOnClickListener(new OnClickListener() {
@Override
@@ -91,10 +110,6 @@
findViewById(R.id.intent_share_button).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
- Intent shareIntent = new Intent(Intent.ACTION_SEND);
- shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
- shareIntent.setType("image/jpg");
- shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Specify the package name of NfcBeamActivity so that the tester don't need to
// select the activity manually.
shareIntent.setClassName(NFC_BEAM_PACKAGE, NFC_BEAM_ACTIVITY);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
index ad2ead6..1c92fd7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
@@ -510,10 +510,6 @@
if (resultCode == SensorTestDetails.ResultCode.FAIL) {
mTestLogger.logInstructions(R.string.snsr_test_complete_with_errors);
enableTestResultButton(
- mPassButton,
- R.string.snsr_pass_on_error,
- testDetails.cloneAndChangeResultCode(SensorTestDetails.ResultCode.PASS));
- enableTestResultButton(
mFailButton,
R.string.fail_button_text,
testDetails.cloneAndChangeResultCode(SensorTestDetails.ResultCode.FAIL));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/usb/accessory/UsbAccessoryTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/usb/accessory/UsbAccessoryTestActivity.java
index 8ac009b..936775e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/usb/accessory/UsbAccessoryTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/usb/accessory/UsbAccessoryTestActivity.java
@@ -27,6 +27,7 @@
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
@@ -34,6 +35,8 @@
import android.widget.ProgressBar;
import android.widget.TextView;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
@@ -54,6 +57,8 @@
private static final String LOG_TAG = UsbAccessoryTestActivity.class.getSimpleName();
private static final int MAX_BUFFER_SIZE = 16384;
+ private static final int TEST_DATA_SIZE_THRESHOLD = 100 * 1024 * 1024; // 100MB
+
private TextView mStatus;
private ProgressBar mProgress;
@@ -187,6 +192,26 @@
assertEquals(MAX_BUFFER_SIZE, numRead);
assertArrayEquals(origBufferMax, bufferMax);
+ // Send two transfers in a row
+ nextTest(is, os, "measure transfer speed");
+
+ byte[] result = new byte[1];
+ long bytesSent = 0;
+ long timeStart = SystemClock.elapsedRealtime();
+ while (bytesSent < TEST_DATA_SIZE_THRESHOLD) {
+ os.write(origBufferMax);
+ bytesSent += MAX_BUFFER_SIZE;
+ }
+ numRead = is.read(result);
+ double speedKBPS = (bytesSent * 8 * 1000. / 1024.)
+ / (SystemClock.elapsedRealtime() - timeStart);
+ assertEquals(1, numRead);
+ assertEquals(1, result[0]);
+ // We don't mandate min speed for now, let's collect data on what it is.
+ getReportLog().setSummary(
+ "Speed", speedKBPS, ResultType.HIGHER_BETTER, ResultUnit.KBPS);
+ Log.i(LOG_TAG, "Data transfer speed is " + speedKBPS + "KBPS");
+
nextTest(is, os, "done");
}
}
diff --git a/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/AccessoryTestCompanion.java b/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/AccessoryTestCompanion.java
index dc5cc3d..5999d3a 100644
--- a/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/AccessoryTestCompanion.java
+++ b/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/AccessoryTestCompanion.java
@@ -39,6 +39,7 @@
*/
class AccessoryTestCompanion extends TestCompanion {
private static final int MAX_BUFFER_SIZE = 16384;
+ private static final int TEST_DATA_SIZE_THRESHOLD = 100 * 1024 * 1024; // 100MB
private static final String ACTION_USB_PERMISSION =
"com.android.cts.verifierusbcompanion.USB_PERMISSION";
@@ -163,13 +164,38 @@
}
break;
+ case "measure transfer speed": {
+ byte[] buffer = new byte[MAX_BUFFER_SIZE];
+
+ long bytesRead = 0;
+ while (bytesRead < TEST_DATA_SIZE_THRESHOLD) {
+ int numTransferred = connection.bulkTransfer(
+ in, buffer, MAX_BUFFER_SIZE, 0);
+ bytesRead += numTransferred;
+ }
+
+ // If the data length is a multple of the maxpacket size read zero packet.
+ int numTransferred = connection.bulkTransfer(in, buffer, 1, 0);
+ assertEquals(0, numTransferred);
+
+ byte[] confirm = new byte[] {1};
+ numTransferred = connection.bulkTransfer(out, confirm, 1, 0);
+ assertEquals(1, numTransferred);
+ }
+ break;
+
case "echo max bytes": {
byte[] buffer = new byte[MAX_BUFFER_SIZE];
+ byte[] empty = new byte[1];
int numTransferred = connection.bulkTransfer(in, buffer, MAX_BUFFER_SIZE,
0);
assertEquals(MAX_BUFFER_SIZE, numTransferred);
+ // If the data length is a multple of the maxpacket size read zero packet.
+ numTransferred = connection.bulkTransfer(in, empty, 1, 0);
+ assertEquals(0, numTransferred);
+
numTransferred = connection.bulkTransfer(out, buffer, MAX_BUFFER_SIZE, 0);
assertEquals(MAX_BUFFER_SIZE, numTransferred);
}
@@ -177,6 +203,7 @@
case "echo max*2 bytes": {
byte[] buffer = new byte[MAX_BUFFER_SIZE * 2];
+ byte[] empty = new byte[1];
int numTransferred = connection.bulkTransfer(in, buffer, MAX_BUFFER_SIZE,
0);
@@ -187,6 +214,10 @@
MAX_BUFFER_SIZE, 0);
assertEquals(MAX_BUFFER_SIZE, numTransferred);
+ // If the data length is a multple of the maxpacket size read zero packet.
+ numTransferred = connection.bulkTransfer(in, empty, 1, 0);
+ assertEquals(0, numTransferred);
+
numTransferred = connection.bulkTransfer(out, buffer, MAX_BUFFER_SIZE, 0);
assertEquals(MAX_BUFFER_SIZE, numTransferred);
diff --git a/common/device-side/device-info/Android.mk b/common/device-side/device-info/Android.mk
index 1fde079..bf6e122 100644
--- a/common/device-side/device-info/Android.mk
+++ b/common/device-side/device-info/Android.mk
@@ -20,7 +20,11 @@
LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ compatibility-device-util \
+ android-support-test \
+ junit \
+ legacy-android-test
LOCAL_MODULE := compatibility-device-info
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GlesStubActivity.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GlesStubActivity.java
index 9e0d3c5..5edf531 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GlesStubActivity.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GlesStubActivity.java
@@ -24,6 +24,8 @@
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
+import android.opengl.EGL14;
+import android.opengl.EGLDisplay;
import android.opengl.GLES20;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
@@ -32,6 +34,7 @@
import java.lang.reflect.Field;
import java.nio.FloatBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.HashSet;
@@ -50,10 +53,11 @@
private int mVersion = -1;
private GraphicsDeviceInfo mGraphicsDeviceInfo;
private CountDownLatch mDone = new CountDownLatch(1);
- private HashSet<String> mOpenGlExtensions = new HashSet<String>();
- private HashSet<String> mFormats = new HashSet<String>();
- private HashMap<String, Object> mImplVariables = new HashMap<String, Object>();
- private HashSet<String> mDynamicArrayVariables = new HashSet<String>();
+ private HashSet<String> mOpenGlExtensions = new HashSet<>();
+ private HashSet<String> mEglExtensions = new HashSet<>();
+ private HashSet<String> mFormats = new HashSet<>();
+ private HashMap<String, Object> mImplVariables = new HashMap<>();
+ private HashSet<String> mDynamicArrayVariables = new HashSet<>();
private String mGraphicsVendor;
private String mGraphicsRenderer;
@@ -126,6 +130,15 @@
mOpenGlExtensions.add(openGlExtension);
}
+ List<String> getEglExtensions() {
+ return new ArrayList<>(mEglExtensions);
+ }
+
+ void addEglExtensions(String[] eglExtensions) {
+ // NOTE: We may end up here multiple times, using set to avoid dupes.
+ mEglExtensions.addAll(Arrays.asList(eglExtensions));
+ }
+
List<String> getCompressedTextureFormats() {
return new ArrayList<>(mFormats);
}
@@ -429,6 +442,8 @@
}
scanner.close();
+ collectEglExtensions(mParent);
+
mDone.countDown();
}
@@ -446,5 +461,22 @@
mParent.addImplementationVariable(name, value, dynamicArray);
}
}
+
+ private static void collectEglExtensions(GlesStubActivity collector) {
+ EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+ if (display == EGL14.EGL_NO_DISPLAY) {
+ Log.e(LOG_TAG, "Failed to init EGL default display: 0x" +
+ Integer.toHexString(EGL14.eglGetError()));
+ return;
+ }
+ String extensions = EGL14.eglQueryString(display, EGL14.EGL_EXTENSIONS);
+ int error = EGL14.eglGetError();
+ if (error != EGL14.EGL_SUCCESS) {
+ Log.e(LOG_TAG, "Failed to query extension string: 0x" + Integer.toHexString(error));
+ return;
+ }
+ // Fingers crossed for no extra white space in the extension string.
+ collector.addEglExtensions(extensions.split(" "));
+ }
}
}
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GraphicsDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GraphicsDeviceInfo.java
index f1832b3..e1d0cef 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GraphicsDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GraphicsDeviceInfo.java
@@ -95,5 +95,7 @@
}
}
}
+
+ store.addListResult("egl_extension", stubActivity.getEglExtensions());
}
}
diff --git a/common/device-side/device-info/tests/Android.mk b/common/device-side/device-info/tests/Android.mk
index a8d9e038..d40614c 100644
--- a/common/device-side/device-info/tests/Android.mk
+++ b/common/device-side/device-info/tests/Android.mk
@@ -18,7 +18,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-info
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-info junit legacy-android-test
LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceReportLog.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceReportLog.java
index 3944f70..1d872a8 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceReportLog.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/DeviceReportLog.java
@@ -72,7 +72,7 @@
super.addValue(source, message, value, type, unit);
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
Log.e(TAG, "Could not log metric.", e);
}
}
@@ -85,7 +85,7 @@
super.addValue(message, value, type, unit);
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
Log.e(TAG, "Could not log metric.", e);
}
}
@@ -99,7 +99,7 @@
super.addValues(source, message, values, type, unit);
try {
store.addArrayResult(message, values);
- } catch (IOException e) {
+ } catch (Exception e) {
Log.e(TAG, "Could not log metric.", e);
}
}
@@ -112,7 +112,7 @@
super.addValues(message, values, type, unit);
try {
store.addArrayResult(message, values);
- } catch (IOException e) {
+ } catch (Exception e) {
Log.e(TAG, "Could not log metric.", e);
}
}
@@ -124,7 +124,7 @@
public void addValue(String message, int value, ResultType type, ResultUnit unit) {
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
Log.e(TAG, "Could not log metric.", e);
}
}
@@ -136,7 +136,7 @@
public void addValue(String message, long value, ResultType type, ResultUnit unit) {
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
Log.e(TAG, "Could not log metric.", e);
}
}
@@ -148,7 +148,7 @@
public void addValue(String message, float value, ResultType type, ResultUnit unit) {
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
Log.e(TAG, "Could not log metric.", e);
}
}
@@ -160,7 +160,7 @@
public void addValue(String message, boolean value, ResultType type, ResultUnit unit) {
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
Log.e(TAG, "Could not log metric.", e);
}
}
@@ -172,7 +172,7 @@
public void addValue(String message, String value, ResultType type, ResultUnit unit) {
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
Log.e(TAG, "Could not log metric.", e);
}
}
@@ -184,7 +184,7 @@
public void addValues(String message, int[] values, ResultType type, ResultUnit unit) {
try {
store.addArrayResult(message, values);
- } catch (IOException e) {
+ } catch (Exception e) {
Log.e(TAG, "Could not log metric.", e);
}
}
@@ -196,7 +196,7 @@
public void addValues(String message, long[] values, ResultType type, ResultUnit unit) {
try {
store.addArrayResult(message, values);
- } catch (IOException e) {
+ } catch (Exception e) {
Log.e(TAG, "Could not log metric.", e);
}
}
@@ -208,7 +208,7 @@
public void addValues(String message, float[] values, ResultType type, ResultUnit unit) {
try {
store.addArrayResult(message, values);
- } catch (IOException e) {
+ } catch (Exception e) {
Log.e(TAG, "Could not log metric.", e);
}
}
@@ -220,7 +220,7 @@
public void addValues(String message, boolean[] values, ResultType type, ResultUnit unit) {
try {
store.addArrayResult(message, values);
- } catch (IOException e) {
+ } catch (Exception e) {
Log.e(TAG, "Could not log metric.", e);
}
}
@@ -232,7 +232,7 @@
public void addValues(String message, List<String> values, ResultType type, ResultUnit unit) {
try {
store.addListResult(message, values);
- } catch (IOException e) {
+ } catch (Exception e) {
Log.e(TAG, "Could not log metric.", e);
}
}
@@ -247,7 +247,7 @@
super.setSummary(message, value, type, unit);
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
Log.e(TAG, "Could not log metric.", e);
}
}
@@ -256,15 +256,13 @@
* Closes report file and submits report to instrumentation.
*/
public void submit(Instrumentation instrumentation) {
- Log.i(TAG, "Submit");
try {
store.close();
Bundle output = new Bundle();
output.putString(RESULT, serialize(this));
instrumentation.sendStatus(INST_STATUS_IN_PROGRESS, output);
- } catch (IllegalArgumentException | IllegalStateException | XmlPullParserException
- | IOException e) {
- Log.e(TAG, "Submit Failed", e);
+ } catch (Exception e) {
+ Log.e(TAG, "ReportLog Submit Failed", e);
instrumentation.sendStatus(INST_STATUS_ERROR, null);
}
}
@@ -275,11 +273,10 @@
* does not appear in the result XML.
*/
public void submit() {
- Log.i(TAG, "Submit");
try {
store.close();
- } catch (IOException e) {
- Log.e(TAG, "Submit Failed", e);
+ } catch (Exception e) {
+ Log.e(TAG, "ReportLog Submit Failed", e);
}
}
}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ReportLogDeviceInfoStore.java b/common/device-side/util/src/com/android/compatibility/common/util/ReportLogDeviceInfoStore.java
index 504a02d..538881d 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/ReportLogDeviceInfoStore.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/ReportLogDeviceInfoStore.java
@@ -27,12 +27,11 @@
public class ReportLogDeviceInfoStore extends DeviceInfoStore {
private final String mStreamName;
- private final File tempJsonFile;
+ private File tempJsonFile;
public ReportLogDeviceInfoStore(File jsonFile, String streamName) throws Exception {
mJsonFile = jsonFile;
mStreamName = streamName;
- tempJsonFile = File.createTempFile(streamName, "-temp-report-log");
}
/**
@@ -42,7 +41,7 @@
public void open() throws IOException {
// Write new metrics to a temp file to avoid invalid JSON files due to failed tests.
BufferedWriter formatWriter;
- tempJsonFile.createNewFile();
+ tempJsonFile = File.createTempFile(mStreamName, "-temp-report-log");
formatWriter = new BufferedWriter(new FileWriter(tempJsonFile));
if (mJsonFile.exists()) {
BufferedReader jsonReader = new BufferedReader(new FileReader(mJsonFile));
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/WifiConfigCreator.java b/common/device-side/util/src/com/android/compatibility/common/util/WifiConfigCreator.java
index ac30fe1..19d843b 100755
--- a/common/device-side/util/src/com/android/compatibility/common/util/WifiConfigCreator.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/WifiConfigCreator.java
@@ -16,21 +16,23 @@
package com.android.compatibility.common.util;
+import static android.net.wifi.WifiManager.EXTRA_WIFI_STATE;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.ProxyInfo;
+import android.net.Uri;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.text.TextUtils;
import android.util.Log;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import java.util.List;
-
-import static android.net.wifi.WifiManager.EXTRA_WIFI_STATE;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
/**
* A simple activity to create and manage wifi configurations.
@@ -84,6 +86,49 @@
}
/**
+ * Adds a new wifiConfiguration with OPEN security type, and the given pacProxy
+ * verifies that the proxy is added by getting the configuration back, and checking it.
+ * @return returns the PAC proxy URL after adding the network and getting it from WifiManager
+ * @throws IllegalStateException if any of the WifiManager operations fail
+ */
+ public String addHttpProxyNetworkVerifyAndRemove(String ssid, String pacProxyUrl)
+ throws IllegalStateException {
+ String retrievedPacProxyUrl = null;
+ int netId = -1;
+ try {
+ WifiConfiguration conf = createConfig(ssid, false, SECURITY_TYPE_NONE, null);
+ if (pacProxyUrl != null) {
+ conf.setHttpProxy(ProxyInfo.buildPacProxy(Uri.parse(pacProxyUrl)));
+ }
+ netId = mWifiManager.addNetwork(conf);
+ if (netId == -1) {
+ throw new IllegalStateException("Failed to addNetwork: " + ssid);
+ }
+ for (final WifiConfiguration w : mWifiManager.getConfiguredNetworks()) {
+ if (w.SSID.equals(ssid)) {
+ conf = w;
+ break;
+ }
+ }
+ if (conf == null) {
+ throw new IllegalStateException("Failed to get WifiConfiguration for: " + ssid);
+ }
+ Uri pacProxyFileUri = null;
+ ProxyInfo httpProxy = conf.getHttpProxy();
+ if (httpProxy != null) pacProxyFileUri = httpProxy.getPacFileUrl();
+ if (pacProxyFileUri != null) {
+ retrievedPacProxyUrl = conf.getHttpProxy().getPacFileUrl().toString();
+ }
+ if (!mWifiManager.removeNetwork(netId)) {
+ throw new IllegalStateException("Failed to remove WifiConfiguration: " + ssid);
+ }
+ } finally {
+ mWifiManager.removeNetwork(netId);
+ }
+ return retrievedPacProxyUrl;
+ }
+
+ /**
* Updates a new WiFi network.
* @return network id (may differ from original) or -1 in case of error
*/
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java b/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
index 3b4b408..5b83357 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
@@ -15,9 +15,10 @@
*/
package com.android.compatibility.common.util.devicepolicy.provisioning;
+import static android.app.admin.DevicePolicyManager.ACTION_MANAGED_PROFILE_PROVISIONED;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
+import static android.content.Intent.ACTION_MANAGED_PROFILE_ADDED;
-import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -50,11 +51,9 @@
};
private final Context mContext;
- private final ManagedProfileAddedReceiver mManagedProfileAddedReceiver;
public SilentProvisioningTestManager(Context context) {
mContext = context.getApplicationContext();
- mManagedProfileAddedReceiver = new ManagedProfileAddedReceiver(mContext);
}
public boolean startProvisioningAndWait(Intent provisioningIntent) throws InterruptedException {
@@ -70,6 +69,35 @@
}
private boolean waitDeviceOwnerProvisioning() throws InterruptedException {
+ return pollProvisioningResult();
+ }
+
+ private boolean waitManagedProfileProvisioning() throws InterruptedException {
+ BlockingReceiver managedProfileProvisionedReceiver = new BlockingReceiver(mContext,
+ ACTION_MANAGED_PROFILE_PROVISIONED);
+ managedProfileProvisionedReceiver.register();
+ BlockingReceiver managedProfileAddedReceiver = new BlockingReceiver(mContext,
+ ACTION_MANAGED_PROFILE_ADDED);
+ managedProfileAddedReceiver.register();
+
+ if (!pollProvisioningResult()) {
+ return false;
+ }
+
+ if (!managedProfileProvisionedReceiver.await()) {
+ Log.i(TAG, "mManagedProfileProvisionedReceiver.await(): false");
+ return false;
+ }
+
+ if (!managedProfileAddedReceiver.await()) {
+ Log.i(TAG, "mManagedProfileAddedReceiver.await(): false");
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean pollProvisioningResult() throws InterruptedException {
Boolean result = mProvisioningResults.poll(TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (result == null) {
Log.i(TAG, "ManagedProvisioning doesn't return result within "
@@ -84,22 +112,6 @@
return true;
}
- private boolean waitManagedProfileProvisioning() throws InterruptedException {
- mManagedProfileAddedReceiver.register();
- Boolean result = mProvisioningResults.poll(TIMEOUT_SECONDS, TimeUnit.SECONDS);
- if (result == null) {
- Log.i(TAG, "ManagedProvisioning doesn't return result within "
- + TIMEOUT_SECONDS + " seconds ");
- return false;
- }
-
- if (!result) {
- Log.i(TAG, "Failed to provision");
- return false;
- }
- return mManagedProfileAddedReceiver.await();
- }
-
private Intent getStartIntent(Intent intent) {
final Bundle bundle = new Bundle();
bundle.putParcelable(Intent.EXTRA_INTENT, intent);
@@ -118,22 +130,23 @@
}
}
- private static class ManagedProfileAddedReceiver extends BroadcastReceiver {
+ private static class BlockingReceiver extends BroadcastReceiver {
private static final CountDownLatch mLatch = new CountDownLatch(1);
private final Context mContext;
+ private final String mAction;
- private ManagedProfileAddedReceiver(Context context) {
+ private BlockingReceiver(Context context, String action) {
mContext = context;
+ mAction = action;
}
- private void register() {
- mContext.registerReceiver(this, new IntentFilter(
- DevicePolicyManager.ACTION_MANAGED_PROFILE_PROVISIONED));
+ public void register() {
+ mContext.registerReceiver(this, new IntentFilter(mAction));
}
- private boolean await() throws InterruptedException {
+ public boolean await() throws InterruptedException {
return mLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
diff --git a/common/device-side/util/tests/Android.mk b/common/device-side/util/tests/Android.mk
index fa7424d..12baeb7 100644
--- a/common/device-side/util/tests/Android.mk
+++ b/common/device-side/util/tests/Android.mk
@@ -18,7 +18,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util junit
LOCAL_MODULE_TAGS := optional
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
index c1087a1..16bf486 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
@@ -71,6 +71,10 @@
}
}
+ public String getRecentCommandLineArgs() {
+ return mBuildInfo.getBuildAttributes().get(COMMAND_LINE_ARGS);
+ }
+
public String getSuiteBuild() {
return mBuildInfo.getBuildAttributes().get(SUITE_BUILD);
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
index 08b7c17..ec41baf 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
@@ -18,6 +18,7 @@
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.compatibility.common.tradefed.result.InvocationFailureHandler;
import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.compatibility.common.tradefed.testtype.CompatibilityTest.RetryType;
import com.android.compatibility.common.util.ICaseResult;
import com.android.compatibility.common.util.IInvocationResult;
import com.android.compatibility.common.util.IModuleResult;
@@ -82,6 +83,13 @@
importance = Importance.IF_UNSET)
private Integer mRetrySessionId = null;
+ @Option(name = CompatibilityTest.RETRY_TYPE_OPTION,
+ description = "used with " + CompatibilityTest.RETRY_OPTION
+ + ", retry tests of a certain status. Possible values include \"failed\" and "
+ + "\"not_executed\".",
+ importance = Importance.IF_UNSET)
+ private RetryType mRetryType = null;
+
@Option(name = "result-server", description = "Server to publish test results.")
private String mResultServer;
@@ -116,6 +124,13 @@
private int mCurrentTestNum;
private int mTotalTestsInModule;
+
+ // Whether modules can be marked done for this invocation. Initialized in invocationStarted()
+ // Visible for unit testing
+ protected boolean mCanMarkDone;
+ // Whether the current module has previously been marked done
+ private boolean mModuleWasDone;
+
// Nullable. If null, "this" is considered the master and must handle
// result aggregation and reporting. When not null, it should forward events
// to the master.
@@ -148,6 +163,7 @@
if (mDeviceSerial == null && buildInfo.getDeviceSerial() != null) {
mDeviceSerial = buildInfo.getDeviceSerial();
}
+ mCanMarkDone = canMarkDone(mBuildHelper.getRecentCommandLineArgs());
}
if (isShardResultReporter()) {
@@ -244,13 +260,14 @@
mTotalTestsInModule +=
Math.max(0, numTests - mCurrentModuleResult.getNotExecuted());
}
- mCurrentModuleResult.setDone(false);
} else {
mCurrentModuleResult = mResult.getOrCreateModule(id);
mTotalTestsInModule = numTests;
// Reset counters
mCurrentTestNum = 0;
}
+ mModuleWasDone = mCurrentModuleResult.isDone();
+ mCurrentModuleResult.setDone(false);
}
/**
@@ -335,8 +352,12 @@
@Override
public void testRunEnded(long elapsedTime, Map<String, String> metrics) {
mCurrentModuleResult.addRuntime(elapsedTime);
- // Expect them to be equal, but greater than to be safe.
- mCurrentModuleResult.setDone(mCurrentTestNum >= mTotalTestsInModule);
+ if (mCanMarkDone || mModuleWasDone) {
+ // Only mark module done if status of the invocation allows it (mCanMarkDone) or module
+ // was previously marked done (mModuleWasDone) and all expected tests are collected.
+ // Expect mCurrentTestNum = mTotalTestsInModule, but use >= to be safe
+ mCurrentModuleResult.setDone(mCurrentTestNum >= mTotalTestsInModule);
+ }
mCurrentModuleResult.setNotExecuted(Math.max(mTotalTestsInModule - mCurrentTestNum, 0));
if (isShardResultReporter()) {
// Forward module results to the master.
@@ -604,6 +625,28 @@
}
/**
+ * Returns whether it is safe to mark modules as "done", given the invocation command-line
+ * arguments. Returns true unless this is a retry and specific filtering techniques are applied
+ * on the command-line, such as:
+ * --retry-type failed
+ * --include-filter
+ * --exclude-filter
+ * -t/--test
+ * --subplan
+ */
+ private boolean canMarkDone(String args) {
+ if (mRetrySessionId == null) {
+ return true; // always allow modules to be marked done if not retry
+ }
+ return !(RetryType.FAILED.equals(mRetryType)
+ || args.contains(CompatibilityTest.INCLUDE_FILTER_OPTION)
+ || args.contains(CompatibilityTest.EXCLUDE_FILTER_OPTION)
+ || args.contains(CompatibilityTest.SUBPLAN_OPTION)
+ || args.matches(String.format(".* (-%s|--%s) .*",
+ CompatibilityTest.TEST_OPTION_SHORT_NAME, CompatibilityTest.TEST_OPTION)));
+ }
+
+ /**
* Copy the xml formatting files stored in this jar to the results directory
*
* @param resultsDir
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index 70bdafd..50ee9d0 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -92,6 +92,7 @@
public static final String PRECONDITION_ARG_OPTION = "precondition-arg";
public static final String MODULE_ARG_OPTION = "module-arg";
public static final String TEST_ARG_OPTION = "test-arg";
+ public static final char TEST_OPTION_SHORT_NAME = 't';
public static final String RETRY_OPTION = "retry";
public static final String RETRY_TYPE_OPTION = "retry-type";
public static final String ABI_OPTION = "abi";
@@ -129,7 +130,7 @@
private String mModuleName = null;
@Option(name = TEST_OPTION,
- shortName = 't',
+ shortName = TEST_OPTION_SHORT_NAME,
description = "the test run.",
importance = Importance.IF_UNSET)
private String mTestName = null;
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java
index 84f9faf..09885ed 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java
@@ -288,6 +288,84 @@
finalTestResult2.getResultStatus());
}
+ public void testRetryCanSetDone() throws Exception {
+ mReporter.invocationStarted(mBuildInfo);
+ // Set mCanMarkDone directly (otherwise we must build result directory, write XML, and
+ // perform actual retry)
+ mReporter.mCanMarkDone = true;
+ // Set up IInvocationResult with existing results from previous session
+ IInvocationResult invocationResult = mReporter.getResult();
+ IModuleResult moduleResult = invocationResult.getOrCreateModule(ID);
+ moduleResult.setDone(false);
+ ICaseResult caseResult = moduleResult.getOrCreateResult(CLASS);
+ ITestResult testResult1 = caseResult.getOrCreateResult(METHOD_1);
+ testResult1.setResultStatus(TestStatus.PASS);
+ testResult1.setRetry(true);
+ ITestResult testResult2 = caseResult.getOrCreateResult(METHOD_2);
+ testResult2.setResultStatus(TestStatus.FAIL);
+ testResult2.setStackTrace(STACK_TRACE);
+ testResult2.setRetry(true);
+
+ // Assume no additional filtering is applied to retry, and all tests for the module have
+ // been collected. Thus, module "done" value should switch.
+ mReporter.testRunStarted(ID, 1);
+
+ TestIdentifier test2 = new TestIdentifier(CLASS, METHOD_2);
+ mReporter.testStarted(test2);
+ mReporter.testEnded(test2, new HashMap<String, String>());
+
+ mReporter.testRunEnded(10, new HashMap<String, String>());
+ mReporter.invocationEnded(10);
+
+ // Verification that results have been overwritten.
+ IInvocationResult result = mReporter.getResult();
+ assertEquals("Expected 2 pass", 2, result.countResults(TestStatus.PASS));
+ assertEquals("Expected 0 failures", 0, result.countResults(TestStatus.FAIL));
+ List<IModuleResult> modules = result.getModules();
+ assertEquals("Expected 1 module", 1, modules.size());
+ IModuleResult module = modules.get(0);
+ assertTrue("Module should be marked done", module.isDone());
+ }
+
+ public void testRetryCannotSetDone() throws Exception {
+ mReporter.invocationStarted(mBuildInfo);
+ // Set mCanMarkDone directly (otherwise we must build result directory, write XML, and
+ // perform actual retry)
+ mReporter.mCanMarkDone = false;
+ // Set up IInvocationResult with existing results from previous session
+ IInvocationResult invocationResult = mReporter.getResult();
+ IModuleResult moduleResult = invocationResult.getOrCreateModule(ID);
+ moduleResult.setDone(false);
+ ICaseResult caseResult = moduleResult.getOrCreateResult(CLASS);
+ ITestResult testResult1 = caseResult.getOrCreateResult(METHOD_1);
+ testResult1.setResultStatus(TestStatus.PASS);
+ testResult1.setRetry(true);
+ ITestResult testResult2 = caseResult.getOrCreateResult(METHOD_2);
+ testResult2.setResultStatus(TestStatus.FAIL);
+ testResult2.setStackTrace(STACK_TRACE);
+ testResult2.setRetry(true);
+
+ // Since using retry-type failed option, we only run previously failed test
+ // and don't run any non-executed tests, so module "done" value should not switch.
+ mReporter.testRunStarted(ID, 1);
+
+ TestIdentifier test2 = new TestIdentifier(CLASS, METHOD_2);
+ mReporter.testStarted(test2);
+ mReporter.testEnded(test2, new HashMap<String, String>());
+
+ mReporter.testRunEnded(10, new HashMap<String, String>());
+ mReporter.invocationEnded(10);
+
+ // Verification that results have been overwritten.
+ IInvocationResult result = mReporter.getResult();
+ assertEquals("Expected 2 pass", 2, result.countResults(TestStatus.PASS));
+ assertEquals("Expected 0 failures", 0, result.countResults(TestStatus.FAIL));
+ List<IModuleResult> modules = result.getModules();
+ assertEquals("Expected 1 module", 1, modules.size());
+ IModuleResult module = modules.get(0);
+ assertFalse("Module should not be marked done", module.isDone());
+ }
+
public void testResultReporting_moduleNotDone() throws Exception {
mReporter.invocationStarted(mBuildInfo);
mReporter.testRunStarted(ID, 2);
diff --git a/common/host-side/util/src/com/android/compatibility/common/util/MetricsReportLog.java b/common/host-side/util/src/com/android/compatibility/common/util/MetricsReportLog.java
index b87b86c..5ea9928 100644
--- a/common/host-side/util/src/com/android/compatibility/common/util/MetricsReportLog.java
+++ b/common/host-side/util/src/com/android/compatibility/common/util/MetricsReportLog.java
@@ -20,7 +20,6 @@
import com.android.tradefed.util.FileUtil;
import java.io.File;
-import java.io.IOException;
import java.util.List;
/**
@@ -69,7 +68,7 @@
super.addValue(source, message, value, type, unit);
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -82,7 +81,7 @@
super.addValue(message, value, type, unit);
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
@@ -97,7 +96,7 @@
super.addValues(source, message, values, type, unit);
try {
store.addArrayResult(message, values);
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -110,7 +109,7 @@
super.addValues(message, values, type, unit);
try {
store.addArrayResult(message, values);
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -122,7 +121,7 @@
public void addValue(String message, int value, ResultType type, ResultUnit unit) {
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -134,7 +133,7 @@
public void addValue(String message, long value, ResultType type, ResultUnit unit) {
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -146,7 +145,7 @@
public void addValue(String message, float value, ResultType type, ResultUnit unit) {
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -158,7 +157,7 @@
public void addValue(String message, boolean value, ResultType type, ResultUnit unit) {
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -170,7 +169,7 @@
public void addValue(String message, String value, ResultType type, ResultUnit unit) {
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -182,7 +181,7 @@
public void addValues(String message, int[] values, ResultType type, ResultUnit unit) {
try {
store.addArrayResult(message, values);
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -194,7 +193,7 @@
public void addValues(String message, long[] values, ResultType type, ResultUnit unit) {
try {
store.addArrayResult(message, values);
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -206,7 +205,7 @@
public void addValues(String message, float[] values, ResultType type, ResultUnit unit) {
try {
store.addArrayResult(message, values);
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -218,7 +217,7 @@
public void addValues(String message, boolean[] values, ResultType type, ResultUnit unit) {
try {
store.addArrayResult(message, values);
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -230,7 +229,7 @@
public void addValues(String message, List<String> values, ResultType type, ResultUnit unit) {
try {
store.addListResult(message, values);
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -245,7 +244,7 @@
super.setSummary(message, value, type, unit);
try {
store.addResult(message, value);
- } catch (IOException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
}
@@ -256,9 +255,9 @@
public void submit() {
try {
store.close();
- } catch (IOException e) {
+ MetricsStore.storeResult(mBuildInfo, mAbi, mClassMethodName, this);
+ } catch (Exception e) {
e.printStackTrace();
}
- MetricsStore.storeResult(mBuildInfo, mAbi, mClassMethodName, this);
}
}
diff --git a/common/host-side/util/src/com/android/compatibility/common/util/ReportLogHostInfoStore.java b/common/host-side/util/src/com/android/compatibility/common/util/ReportLogHostInfoStore.java
index 38b742b..f501010 100644
--- a/common/host-side/util/src/com/android/compatibility/common/util/ReportLogHostInfoStore.java
+++ b/common/host-side/util/src/com/android/compatibility/common/util/ReportLogHostInfoStore.java
@@ -27,12 +27,11 @@
public class ReportLogHostInfoStore extends HostInfoStore {
private final String mStreamName;
- private final File tempJsonFile;
+ private File tempJsonFile;
public ReportLogHostInfoStore(File jsonFile, String streamName) throws Exception {
mJsonFile = jsonFile;
mStreamName = streamName;
- tempJsonFile = File.createTempFile(streamName, "-temp-report-log");
}
/**
@@ -42,7 +41,7 @@
public void open() throws IOException {
// Write new metrics to a temp file to avoid invalid JSON files due to failed tests.
BufferedWriter formatWriter;
- tempJsonFile.createNewFile();
+ tempJsonFile = File.createTempFile(mStreamName, "-temp-report-log");
formatWriter = new BufferedWriter(new FileWriter(tempJsonFile));
if (mJsonFile.exists()) {
BufferedReader jsonReader = new BufferedReader(new FileReader(mJsonFile));
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java
index a064287..7d8c884 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java
@@ -85,4 +85,8 @@
public void testCreateDocumentAtInitialLocation() throws Exception {
runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testCreateDocumentAtInitialLocation");
}
+
+ public void testCreateWebLink() throws Exception {
+ runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testCreateWebLink");
+ }
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
index 7a6ad8e..0249509 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
@@ -107,6 +107,10 @@
runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartEphemeral");
}
+ public void testExposedSystemActivities() throws Exception {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testExposedSystemActivities");
+ }
+
private void runDeviceTests(String packageName, String testClassName, String testMethodName)
throws DeviceNotAvailableException {
Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
index 1566066..bc8f904 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
@@ -16,10 +16,13 @@
package com.android.cts.documentclient;
+import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
+import android.content.IntentSender;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Bundle;
import android.os.SystemClock;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
@@ -524,4 +527,54 @@
assertTrue(findDocument("FILE1").exists());
}
+
+ public void testCreateWebLink() throws Exception {
+ if (!supportedHardware()) return;
+
+ final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.setType("*/*");
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+
+ // Pick a virtual file from the local root.
+ mDevice.waitForIdle();
+ findRoot("CtsLocal").click();
+
+ mDevice.waitForIdle();
+ findDocument("WEB_LINKABLE_FILE").click();
+
+ // Confirm that the returned file is actually the selected one.
+ final Result result = mActivity.getResult();
+ final Uri uri = result.data.getData();
+ assertEquals("doc:web-linkable-file", DocumentsContract.getDocumentId(uri));
+
+ final ContentResolver resolver = getInstrumentation().getContext().getContentResolver();
+ final String streamTypes[] = resolver.getStreamTypes(uri, "*/*");
+ assertEquals(1, streamTypes.length);
+ assertEquals("text/plain", streamTypes[0]);
+
+ Bundle bundle = new Bundle();
+ bundle.putString(Intent.EXTRA_EMAIL, "x@x.com");
+ final IntentSender intentSender = DocumentsContract.createWebLinkIntent(resolver,
+ uri, bundle);
+
+ final int WEB_LINK_REQUEST_CODE = 1;
+ mActivity.startIntentSenderForResult(intentSender, WEB_LINK_REQUEST_CODE,
+ null, 0, 0, 0);
+ mDevice.waitForIdle();
+
+ // Confirm the permissions dialog. The dialog is provided by the stub
+ // provider.
+ UiObject okButton = new UiObject(new UiSelector().text("OK").clickable(true));
+ assertNotNull(okButton);
+ assertTrue(okButton.waitForExists(TIMEOUT));
+ okButton.click();
+
+ final Result webLinkResult = mActivity.getResult();
+ assertEquals(WEB_LINK_REQUEST_CODE, webLinkResult.requestCode);
+ assertEquals(Activity.RESULT_OK, webLinkResult.resultCode);
+
+ final Uri webLinkUri = webLinkResult.data.getData();
+ assertEquals("http://www.foobar.com/shared/SW33TCH3RR13S", webLinkUri.toString());
+ }
+
}
diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/DocumentProvider/AndroidManifest.xml
index c0fc6cc..894eff1 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentProvider/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/AndroidManifest.xml
@@ -36,5 +36,8 @@
<data android:mimeType="image/*" />
</intent-filter>
</activity>
+
+ <activity android:name=".WebLinkActivity">
+ </activity>
</application>
</manifest>
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 eabf4ba..afcaefe 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
@@ -16,6 +16,9 @@
package com.android.cts.documentprovider;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.IntentSender;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.MatrixCursor;
@@ -46,6 +49,8 @@
public class MyDocumentsProvider extends DocumentsProvider {
private static final String TAG = "TestDocumentsProvider";
+ private static final int WEB_LINK_REQUEST_CODE = 321;
+
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES,
@@ -141,6 +146,15 @@
mCreateRoot.children.add(virtualFile);
}
+ {
+ Doc webLinkableFile = buildDoc("doc:web-linkable-file", "WEB_LINKABLE_FILE",
+ "application/icecream", new String[] { "text/plain" });
+ webLinkableFile.flags = Document.FLAG_VIRTUAL_DOCUMENT | Document.FLAG_WEB_LINKABLE;
+ webLinkableFile.contents = "Fake contents.".getBytes();
+ mLocalRoot.children.add(webLinkableFile);
+ mCreateRoot.children.add(webLinkableFile);
+ }
+
Doc dir1 = buildDoc("doc:dir1", "DIR1", Document.MIME_TYPE_DIR, null);
mLocalRoot.children.add(dir1);
@@ -428,6 +442,29 @@
throw new UnsupportedOperationException("Unsupported MIME type filter for tests.");
}
+ @Override
+ public IntentSender createWebLinkIntent(String documentId, Bundle options)
+ throws FileNotFoundException {
+ final Doc doc = mDocs.get(documentId);
+ if (doc == null) {
+ throw new FileNotFoundException();
+ }
+ if ((doc.flags & Document.FLAG_WEB_LINKABLE) == 0) {
+ throw new IllegalArgumentException("The file is not web linkable");
+ }
+
+ final Intent intent = new Intent(getContext(), WebLinkActivity.class);
+ intent.putExtra(WebLinkActivity.EXTRA_DOCUMENT_ID, documentId);
+ if (options != null) {
+ intent.putExtras(options);
+ }
+
+ final PendingIntent pendingIntent = PendingIntent.getActivity(
+ getContext(), WEB_LINK_REQUEST_CODE, intent,
+ PendingIntent.FLAG_ONE_SHOT);
+ return pendingIntent.getIntentSender();
+ }
+
private static byte[] readFullyNoClose(InputStream in) throws IOException {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/WebLinkActivity.java b/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/WebLinkActivity.java
new file mode 100644
index 0000000..76deee3
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/WebLinkActivity.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 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.documentprovider;
+
+import android.app.Activity;
+import android.app.AlertDialog.Builder;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+
+public class WebLinkActivity extends Activity {
+ public static final String EXTRA_DOCUMENT_ID =
+ "com.android.cts.documentprovider.EXTRA_DOCUMENT_ID";
+ private static final Uri FAKE_WEB_LINK = Uri.parse(
+ "http://www.foobar.com/shared/SW33TCH3RR13S");
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final String documentId = getIntent().getStringExtra(EXTRA_DOCUMENT_ID);
+ final String email = getIntent().getStringExtra(Intent.EXTRA_EMAIL);
+
+ new AlertDialog.Builder(this)
+ .setTitle("Grant permissions to this file to " + email + "?")
+ .setMessage(documentId)
+ .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ final Intent intent = new Intent();
+ intent.setData(FAKE_WEB_LINK);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+ })
+ .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ setResult(RESULT_CANCELED, null);
+ finish();
+ }
+ })
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .show();
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk
index 88501bf..c50dbd9 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/Android.mk
@@ -18,7 +18,9 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ legacy-android-test
# tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
index 2b03efa..b480791 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
@@ -59,6 +59,22 @@
"com.android.cts.ephemeraltest.QUERY";
private static final String EXTRA_ACTIVITY_NAME =
"com.android.cts.ephemeraltest.EXTRA_ACTIVITY_NAME";
+ private static final String EXTRA_ACTIVITY_RESULT =
+ "com.android.cts.ephemeraltest.EXTRA_ACTIVITY_RESULT";
+
+ /**
+ * Intents that we expect the system to expose activities to ephemeral apps to handle.
+ */
+ private static final Intent[] EXPECTED_EXPOSED_SYSTEM_INTENTS = new Intent[] {
+ makeIntent(Intent.ACTION_OPEN_DOCUMENT, Intent.CATEGORY_OPENABLE, "*/*"),
+ makeIntent(Intent.ACTION_OPEN_DOCUMENT, null, "*/*"),
+ makeIntent(Intent.ACTION_GET_CONTENT, Intent.CATEGORY_OPENABLE, "*/*"),
+ makeIntent(Intent.ACTION_GET_CONTENT, null, "*/*"),
+ makeIntent(Intent.ACTION_OPEN_DOCUMENT_TREE, null, null),
+ makeIntent(Intent.ACTION_CREATE_DOCUMENT, Intent.CATEGORY_OPENABLE, "text/plain"),
+ makeIntent(Intent.ACTION_CREATE_DOCUMENT, null, "text/plain"),
+
+ };
private BroadcastReceiver mReceiver;
private final SynchronousQueue<BroadcastResult> mResultQueue = new SynchronousQueue<>();
@@ -156,6 +172,8 @@
is("com.android.cts.normalapp"));
assertThat(testResult.activityName,
is("ExposedActivity"));
+ assertThat(testResult.result,
+ is("PASS"));
}
// start the exposed activity; directed package
@@ -169,6 +187,8 @@
is("com.android.cts.normalapp"));
assertThat(testResult.activityName,
is("ExposedActivity"));
+ assertThat(testResult.result,
+ is("PASS"));
}
// start the exposed activity; directed component
@@ -183,6 +203,8 @@
is("com.android.cts.normalapp"));
assertThat(testResult.activityName,
is("ExposedActivity"));
+ assertThat(testResult.result,
+ is("PASS"));
}
}
@@ -229,6 +251,21 @@
}
}
+ @Test
+ public void testExposedSystemActivities() throws Exception {
+ for (Intent queryIntent : EXPECTED_EXPOSED_SYSTEM_INTENTS) {
+ assertIntentHasExposedActivities(queryIntent);
+ }
+ }
+
+ private void assertIntentHasExposedActivities(Intent queryIntent) throws Exception {
+ final List<ResolveInfo> resolveInfo = InstrumentationRegistry
+ .getContext().getPackageManager().queryIntentActivities(queryIntent, 0 /*flags*/);
+ if (resolveInfo == null || resolveInfo.size() == 0) {
+ fail("No activies found for Intent: " + queryIntent);
+ }
+ }
+
private BroadcastResult getResult() {
final BroadcastResult result;
try {
@@ -242,17 +279,33 @@
return result;
}
+ private static Intent makeIntent(String action, String category, String mimeType) {
+ Intent intent = new Intent(action);
+ if (category != null) {
+ intent.addCategory(category);
+ }
+ if (mimeType != null) {
+ intent.setType(mimeType);
+ }
+ return intent;
+ }
+
private static class BroadcastResult {
final String packageName;
final String activityName;
- public BroadcastResult(String packageName, String activityName) {
+ final String result;
+
+ public BroadcastResult(String packageName, String activityName, String result) {
this.packageName = packageName;
this.activityName = activityName;
+ this.result = result;
}
@Override
public String toString() {
- return "[pkg=" + packageName + ", activity=" + activityName + "]";
+ return "[pkg=" + packageName
+ + ", activity=" + activityName
+ + ", result=" + result + "]";
}
}
@@ -268,7 +321,9 @@
mQueue.offer(
new BroadcastResult(
intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME),
- intent.getStringExtra(EXTRA_ACTIVITY_NAME)),
+ intent.getStringExtra(EXTRA_ACTIVITY_NAME),
+ intent.getStringExtra(EXTRA_ACTIVITY_RESULT)
+ ),
5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/Android.mk b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/Android.mk
index c2db091..945422e 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/Android.mk
@@ -18,7 +18,9 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ legacy-android-test
# tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/AndroidManifest.xml
index c2e91c4..e38ab54 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/AndroidManifest.xml
@@ -16,6 +16,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.normalapp">
+ <uses-sdk
+ android:minSdkVersion="24" />
<application
android:label="@string/app_name">
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java
index 8bd6a68..1f1c292 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ClientTest.java
@@ -58,6 +58,8 @@
"com.android.cts.ephemeraltest.QUERY";
private static final String EXTRA_ACTIVITY_NAME =
"com.android.cts.ephemeraltest.EXTRA_ACTIVITY_NAME";
+ private static final String EXTRA_ACTIVITY_RESULT =
+ "com.android.cts.ephemeraltest.EXTRA_ACTIVITY_RESULT";
private BroadcastReceiver mReceiver;
private final SynchronousQueue<BroadcastResult> mResultQueue = new SynchronousQueue<>();
@@ -129,6 +131,8 @@
is("com.android.cts.normalapp"));
assertThat(testResult.activityName,
is("NormalActivity"));
+ assertThat(testResult.result,
+ is("android.content.pm.PackageManager$NameNotFoundException"));
}
// start the normal activity; directed package
@@ -141,6 +145,8 @@
is("com.android.cts.normalapp"));
assertThat(testResult.activityName,
is("NormalActivity"));
+ assertThat(testResult.result,
+ is("android.content.pm.PackageManager$NameNotFoundException"));
}
// start the normal activity; directed component
@@ -154,6 +160,8 @@
is("com.android.cts.normalapp"));
assertThat(testResult.activityName,
is("NormalActivity"));
+ assertThat(testResult.result,
+ is("android.content.pm.PackageManager$NameNotFoundException"));
}
}
@@ -219,14 +227,19 @@
private static class BroadcastResult {
final String packageName;
final String activityName;
- public BroadcastResult(String packageName, String activityName) {
+ final String result;
+
+ public BroadcastResult(String packageName, String activityName, String result) {
this.packageName = packageName;
this.activityName = activityName;
+ this.result = result;
}
@Override
public String toString() {
- return "[pkg=" + packageName + ", activity=" + activityName + "]";
+ return "[pkg=" + packageName
+ + ", activity=" + activityName
+ + ", result=" + result + "]";
}
}
@@ -242,7 +255,9 @@
mQueue.offer(
new BroadcastResult(
intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME),
- intent.getStringExtra(EXTRA_ACTIVITY_NAME)),
+ intent.getStringExtra(EXTRA_ACTIVITY_NAME),
+ intent.getStringExtra(EXTRA_ACTIVITY_RESULT)
+ ),
5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ExposedActivity.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ExposedActivity.java
index 0295085..de597c8 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ExposedActivity.java
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/ExposedActivity.java
@@ -19,10 +19,14 @@
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.List;
public class ExposedActivity extends Activity {
@@ -30,6 +34,8 @@
"com.android.cts.ephemeraltest.START_ACTIVITY";
private static final String EXTRA_ACTIVITY_NAME =
"com.android.cts.ephemeraltest.EXTRA_ACTIVITY_NAME";
+ private static final String EXTRA_ACTIVITY_RESULT =
+ "com.android.cts.ephemeraltest.EXTRA_ACTIVITY_RESULT";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -39,8 +45,22 @@
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, "com.android.cts.normalapp");
broadcastIntent.putExtra(EXTRA_ACTIVITY_NAME, "ExposedActivity");
+ String result = "FAIL";
+ try {
+ tryAccessingEphemeral();
+ result = "PASS";
+ } catch (Throwable t) {
+ result = t.getClass().getName();
+ }
+ broadcastIntent.putExtra(EXTRA_ACTIVITY_RESULT, result);
sendBroadcast(broadcastIntent);
finish();
}
+
+ private void tryAccessingEphemeral() throws NameNotFoundException {
+ final PackageInfo info =
+ getPackageManager().getPackageInfo("com.android.cts.ephemeralapp1", 0 /*flags*/);
+ if (info == null) throw new IllegalStateException("No PackageInfo object found");
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/NormalActivity.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/NormalActivity.java
index d279506..1807971 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/NormalActivity.java
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/NormalActivity.java
@@ -18,11 +18,15 @@
import android.app.Activity;
import android.content.Intent;
+import android.content.pm.PackageInfo;
import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.List;
public class NormalActivity extends Activity {
@@ -30,6 +34,8 @@
"com.android.cts.ephemeraltest.START_ACTIVITY";
private static final String EXTRA_ACTIVITY_NAME =
"com.android.cts.ephemeraltest.EXTRA_ACTIVITY_NAME";
+ private static final String EXTRA_ACTIVITY_RESULT =
+ "com.android.cts.ephemeraltest.EXTRA_ACTIVITY_RESULT";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -39,8 +45,22 @@
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, "com.android.cts.normalapp");
broadcastIntent.putExtra(EXTRA_ACTIVITY_NAME, "NormalActivity");
+ String result = "FAIL";
+ try {
+ tryAccessingEphemeral();
+ result = "PASS";
+ } catch (Throwable t) {
+ result = t.getClass().getName();
+ }
+ broadcastIntent.putExtra(EXTRA_ACTIVITY_RESULT, result);
sendBroadcast(broadcastIntent);
finish();
}
+
+ private void tryAccessingEphemeral() throws NameNotFoundException {
+ final PackageInfo info =
+ getPackageManager().getPackageInfo("com.android.cts.ephemeralapp1", 0 /*flags*/);
+ if (info == null) throw new IllegalStateException("No PackageInfo object found");
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/NormalWebActivity.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/NormalWebActivity.java
index 3283cea..a1a50b9 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/NormalWebActivity.java
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/NormalApp/src/com/android/cts/normalapp/NormalWebActivity.java
@@ -18,11 +18,15 @@
import android.app.Activity;
import android.content.Intent;
+import android.content.pm.PackageInfo;
import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.List;
public class NormalWebActivity extends Activity {
@@ -30,6 +34,8 @@
"com.android.cts.ephemeraltest.START_ACTIVITY";
private static final String EXTRA_ACTIVITY_NAME =
"com.android.cts.ephemeraltest.EXTRA_ACTIVITY_NAME";
+ private static final String EXTRA_ACTIVITY_RESULT =
+ "com.android.cts.ephemeraltest.EXTRA_ACTIVITY_RESULT";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -39,8 +45,22 @@
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, "com.android.cts.normalapp");
broadcastIntent.putExtra(EXTRA_ACTIVITY_NAME, "NormalWebActivity");
+ String result = "FAIL";
+ try {
+ tryAccessingEphemeral();
+ result = "PASS";
+ } catch (Throwable t) {
+ result = t.getClass().getName();
+ }
+ broadcastIntent.putExtra(EXTRA_ACTIVITY_RESULT, result);
sendBroadcast(broadcastIntent);
finish();
}
+
+ private void tryAccessingEphemeral() throws NameNotFoundException {
+ final PackageInfo info =
+ getPackageManager().getPackageInfo("com.android.cts.ephemeralapp1", 0 /*flags*/);
+ if (info == null) throw new IllegalStateException("No PackageInfo object found");
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk
index 34f4170..f8db5aa 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk
@@ -19,7 +19,12 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test compatibility-device-util ctstestrunner ub-uiautomator
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java \
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk
index e8aefb9..9e6ea3e 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk
@@ -19,7 +19,12 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test compatibility-device-util ctstestrunner ub-uiautomator
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
diff --git a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
index 3ab7d95..25c0b89 100644
--- a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
+++ b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
@@ -20,6 +20,7 @@
import com.android.ddmlib.Log;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IBuildReceiver;
@@ -102,7 +103,7 @@
/*details*/ m.group(6));
return;
}
- System.err.println("line doesn't match: " + line);
+ CLog.i("line doesn't match: " + line);
}
private static void parse(Reader reader, FtraceEntryCallback callback) throws Exception {
diff --git a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/AdminReceiver.java b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/AdminReceiver.java
index 957cbf5..bf18ebb 100644
--- a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/AdminReceiver.java
+++ b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/AdminReceiver.java
@@ -37,6 +37,7 @@
super.onProfileProvisioningComplete(context, intent);
Log.i(TAG, "onProfileProvisioningComplete");
// Enabled profile
+ getManager(context).setProfileEnabled(getComponentName(context));
getManager(context).setProfileName(getComponentName(context), "Corp owned Managed Profile");
}
}
diff --git a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/ManagementTest.java b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/ManagementTest.java
index 1daa496..230859d 100644
--- a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/ManagementTest.java
+++ b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/ManagementTest.java
@@ -16,10 +16,11 @@
package com.android.cts.comp;
import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
import android.content.Context;
import android.os.UserHandle;
import android.test.AndroidTestCase;
-
+import android.test.MoreAsserts;
import java.util.List;
public class ManagementTest extends AndroidTestCase {
@@ -84,4 +85,15 @@
assertTrue(mDevicePolicyManager.removeUser(AdminReceiver.getComponentName(mContext),
profileUserHandle));
}
+
+ public void testCreateSecondaryUser() throws Exception {
+ ComponentName admin = AdminReceiver.getComponentName(mContext);
+ assertNotNull(mDevicePolicyManager.createAndManageUser(admin, "secondary-user",
+ admin, null, DevicePolicyManager.SKIP_SETUP_WIZARD));
+ }
+
+ public void testNoBindDeviceAdminTargetUsers() {
+ MoreAsserts.assertEmpty(mDevicePolicyManager.getBindDeviceAdminTargetUsers(
+ AdminReceiver.getComponentName(mContext)));
+ }
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/WipeDataTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/WipeDataTest.java
new file mode 100644
index 0000000..ea010f4
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/WipeDataTest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 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.deviceadmin;
+
+public class WipeDataTest extends BaseDeviceAdminTest {
+
+ // Caution: this test will wipe the device's data if it fails
+ public void testWipeDataThrowsSecurityException() {
+ try {
+ dpm.wipeData(0);
+ fail("wipeData didn't throw expected SecurityException. Managed to kick off factory"
+ + " reset process");
+ } catch (SecurityException expected) {
+ }
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/ProfileGlobalRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/ProfileGlobalRestrictionsTest.java
new file mode 100644
index 0000000..d343401
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/ProfileGlobalRestrictionsTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 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.deviceandprofileowner.userrestrictions;
+
+import static android.os.UserManager.ENSURE_VERIFY_APPS;
+
+import com.android.cts.deviceandprofileowner.BaseDeviceAdminTest;
+
+public class ProfileGlobalRestrictionsTest extends BaseDeviceAdminTest {
+ private void assertRestriction(String restriction, boolean expected) {
+ assertEquals("Wrong restriction value",
+ expected, mUserManager.hasUserRestriction(restriction));
+ }
+
+ public void testSetProfileGlobalRestrictions() throws Exception {
+ mDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT, ENSURE_VERIFY_APPS);
+ }
+
+ public void testClearProfileGlobalRestrictions() throws Exception {
+ mDevicePolicyManager.clearUserRestriction(ADMIN_RECEIVER_COMPONENT, ENSURE_VERIFY_APPS);
+ }
+
+ public void testProfileGlobalRestrictionsEnforced() {
+ assertRestriction(ENSURE_VERIFY_APPS, true);
+ }
+
+ public void testProfileGlobalRestrictionsNotEnforced() {
+ assertRestriction(ENSURE_VERIFY_APPS, false);
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
index 9acb94e..51a8fd3 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
@@ -69,6 +69,15 @@
android:taskAffinity="com.android.cts.deviceowner.LockTaskTest.IntentReceivingActivity"
/>
+ <activity
+ android:name=".SetPolicyActivity"
+ android:launchMode="singleTop">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="com.android.compatibility.common.util.devicepolicy.provisioning.StartProvisioningActivity"/>
</application>
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BackupServiceEnabledTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BackupServiceEnabledTest.java
new file mode 100644
index 0000000..fbf1ec7
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BackupServiceEnabledTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 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;
+
+public class BackupServiceEnabledTest extends BaseDeviceOwnerTest {
+
+ /**
+ * Test: Test enabling backup service. This test should be executed after installing a device
+ * owner so that we check that backup service is not enabled by default.
+ * This test will keep backup service disabled after its execution.
+ */
+ public void testEnablingAndDisablingBackupService() {
+ assertFalse(mDevicePolicyManager.isBackupServiceEnabled(getWho()));
+ mDevicePolicyManager.setBackupServiceEnabled(getWho(), true);
+ assertTrue(mDevicePolicyManager.isBackupServiceEnabled(getWho()));
+ mDevicePolicyManager.setBackupServiceEnabled(getWho(), false);
+ assertFalse(mDevicePolicyManager.isBackupServiceEnabled(getWho()));
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetPolicyActivity.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetPolicyActivity.java
new file mode 100644
index 0000000..028bf2ea
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetPolicyActivity.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 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 android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Process;
+import android.util.Log;
+
+/**
+ * Simple activity that adds or clears a user restriction depending on the value of the extras.
+ */
+public class SetPolicyActivity extends Activity {
+
+ private static final String TAG = SetPolicyActivity.class.getName();
+
+ private static final String EXTRA_RESTRICTION_KEY = "extra-restriction-key";
+ private static final String EXTRA_COMMAND = "extra-command";
+
+ private static final String ADD_RESTRICTION_COMMAND = "add-restriction";
+ private static final String CLEAR_RESTRICTION_COMMAND = "clear-restriction";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ handleIntent(getIntent());
+ }
+
+ // Overriding this method in case another intent is sent to this activity before finish()
+ @Override
+ public void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ handleIntent(intent);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ // Calling finish() here because doing it in onCreate(), onStart() or onResume() makes
+ // "adb shell am start" timeout if using the -W option.
+ finish();
+ }
+
+ private void handleIntent(Intent intent) {
+ DevicePolicyManager dpm = (DevicePolicyManager)
+ getSystemService(Context.DEVICE_POLICY_SERVICE);
+ String command = intent.getStringExtra(EXTRA_COMMAND);
+ Log.i(TAG, "Command: \"" + command);
+ ComponentName admin = BaseDeviceOwnerTest.getWho();
+ if (ADD_RESTRICTION_COMMAND.equals(command)) {
+ String restrictionKey = intent.getStringExtra(EXTRA_RESTRICTION_KEY);
+ dpm.addUserRestriction(admin, restrictionKey);
+ Log.i(TAG, "Added user restriction " + restrictionKey
+ + " for user " + Process.myUserHandle());
+ } else if (CLEAR_RESTRICTION_COMMAND.equals(command)) {
+ String restrictionKey = intent.getStringExtra(EXTRA_RESTRICTION_KEY);
+ dpm.clearUserRestriction(admin, restrictionKey);
+ Log.i(TAG, "Cleared user restriction " + restrictionKey
+ + " for user " + Process.myUserHandle());
+ } else {
+ Log.e(TAG, "Invalid command: " + command);
+ }
+ }
+
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiSetHttpProxyTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiSetHttpProxyTest.java
new file mode 100644
index 0000000..bd651f2
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiSetHttpProxyTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 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 android.content.pm.PackageManager;
+
+import com.android.compatibility.common.util.WifiConfigCreator;
+
+/**
+ * Tests that DeviceOwner can add WifiConfigurations containing a HttpProxy
+ */
+public class WifiSetHttpProxyTest extends BaseDeviceOwnerTest {
+
+ private static final String TAG = "WifiSetHttpProxyTest";
+ private static final String TEST_PAC_URL = "http://www.example.com/proxy.pac";
+ private static final String TEST_SSID = "SomeProxyApSsid";
+ private static final int FAILURE_NETWORK_ID = -1;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Test WifiManager.addNetwork() succeeds for a DeviceOwner adding a WifiConfiguraiton
+ * containing a HttpProxy.
+ * 2. Creates a new WifiConfiguration with ssid TEST_SSID
+ * 3. Adds a PAC proxy file URL to the WifiConfiguraiton
+ * 4. Adds the WifiConfiguration via WifiManager.addNetwork(), expects success
+ * 5. Verifies the added WifiConfiguration has the same proxy
+ */
+ public void testSetHttpProxy() throws Exception {
+ PackageManager packageManager = getContext().getPackageManager();
+ if (!packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ // skip the test if WiFi is not supported
+ return;
+ }
+ WifiConfigCreator configCreator = new WifiConfigCreator(getContext());
+ String retreievedPacProxyUrl = configCreator.addHttpProxyNetworkVerifyAndRemove(
+ TEST_SSID, TEST_PAC_URL);
+ assertEquals(TEST_PAC_URL, retreievedPacProxyUrl);
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
index 35c16ea..e4af64c 100644
--- a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
+++ b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.ServiceConnection;
@@ -120,6 +121,12 @@
assertTrue(activity.getUser().equals(mUser));
}
assertTrue(foundSimpleApp);
+
+ // Also make sure getApplicationInfo works too.
+ final ApplicationInfo ai =
+ mLauncherApps.getApplicationInfo(SIMPLE_APP_PACKAGE, /* flags= */ 0, mUser);
+ assertEquals(SIMPLE_APP_PACKAGE, ai.packageName);
+ assertEquals(mUser, UserHandle.getUserHandleForUid(ai.uid));
}
public void testPackageAddedCallbackForUser() throws Throwable {
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
index e5786dd..90ed617 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
@@ -44,6 +44,15 @@
</intent-filter>
</receiver>
<receiver
+ android:name="com.android.cts.managedprofile.ProvisioningTest$ProvisioningAdminReceiver"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/device_admin" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
+ </receiver>
+ <receiver
android:name="com.android.cts.managedprofile.PrimaryUserDeviceAdmin"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
@@ -112,6 +121,7 @@
<meta-data android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
+ <activity android:name="com.android.compatibility.common.util.devicepolicy.provisioning.StartProvisioningActivity"/>
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProvisioningTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProvisioningTest.java
new file mode 100644
index 0000000..b31851a
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProvisioningTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 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.managedprofile;
+
+import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_SKIP_ENCRYPTION;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.os.PersistableBundle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.util.Log;
+
+import com.android.compatibility.common.util.devicepolicy.provisioning.SilentProvisioningTestManager;
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+public class ProvisioningTest {
+ private static final String TAG = ProvisioningTest.class.getSimpleName();
+
+ private static final String ADMIN_EXTRAS_BUNDLE_FILENAME = "admin_extras_bundle.txt";
+ private static final PersistableBundle ADMIN_EXTRAS_BUNDLE = new PersistableBundle();
+ private static final String ADMIN_EXTRAS_BUNDLE_KEY_1 = "KEY_1";
+ private static final String ADMIN_EXTRAS_BUNDLE_VALUE_1 = "VALUE_1";
+ static {
+ ADMIN_EXTRAS_BUNDLE.putString(ADMIN_EXTRAS_BUNDLE_KEY_1, ADMIN_EXTRAS_BUNDLE_VALUE_1);
+ }
+
+ private static final ComponentName ADMIN_RECEIVER_COMPONENT = new ComponentName(
+ ProvisioningAdminReceiver.class.getPackage().getName(),
+ ProvisioningAdminReceiver.class.getName());
+
+ public static class ProvisioningAdminReceiver extends DeviceAdminReceiver {
+ @Override
+ public void onProfileProvisioningComplete(Context context, Intent intent) {
+ super.onProfileProvisioningComplete(context, intent);
+ // Enabled profile
+ getManager(context).setProfileName(ADMIN_RECEIVER_COMPONENT, "Managed Profile");
+ getManager(context).setProfileEnabled(ADMIN_RECEIVER_COMPONENT);
+ Log.i(TAG, "onProfileProvisioningComplete");
+
+ saveBundle(context, intent.getParcelableExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE));
+ }
+ }
+
+ private Context mContext;
+ private DevicePolicyManager mDpm;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mDpm = mContext.getSystemService(DevicePolicyManager.class);
+ }
+
+ @Test
+ public void testIsManagedProfile() {
+ assertTrue(mDpm.isManagedProfile(ADMIN_RECEIVER_COMPONENT));
+ Log.i(TAG, "managed profile app: " + ADMIN_RECEIVER_COMPONENT.getPackageName());
+ }
+
+ @Test
+ public void testProvisionManagedProfile() throws InterruptedException {
+ provisionManagedProfile();
+ }
+
+ @Test
+ public void testVerifyAdminExtraBundle() {
+ PersistableBundle bundle = loadBundle(mContext);
+ assertNotNull(bundle);
+ assertEquals(ADMIN_EXTRAS_BUNDLE_VALUE_1, bundle.getString(ADMIN_EXTRAS_BUNDLE_KEY_1));
+ }
+
+ private void provisionManagedProfile() throws InterruptedException {
+ Intent intent = new Intent(ACTION_PROVISION_MANAGED_PROFILE)
+ .putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME, ADMIN_RECEIVER_COMPONENT)
+ .putExtra(EXTRA_PROVISIONING_SKIP_ENCRYPTION, true)
+ .putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE, ADMIN_EXTRAS_BUNDLE);
+ SilentProvisioningTestManager provisioningManager =
+ new SilentProvisioningTestManager(mContext);
+ assertTrue(provisioningManager.startProvisioningAndWait(intent));
+ Log.i(TAG, "managed profile provisioning successful");
+ }
+
+ private static void saveBundle(Context context, PersistableBundle bundle) {
+ if (bundle == null) {
+ Log.e(TAG, "null saveBundle");
+ return;
+ }
+
+ getAdminExtraSharedPreferences(context).edit()
+ .putString(ADMIN_EXTRAS_BUNDLE_KEY_1, bundle.getString(ADMIN_EXTRAS_BUNDLE_KEY_1))
+ .commit();
+ }
+
+ private static PersistableBundle loadBundle(Context context) {
+ SharedPreferences pref = getAdminExtraSharedPreferences(context);
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(ADMIN_EXTRAS_BUNDLE_KEY_1,
+ pref.getString(ADMIN_EXTRAS_BUNDLE_KEY_1, null));
+ return bundle;
+ }
+
+ private static SharedPreferences getAdminExtraSharedPreferences(Context context) {
+ return context.getSharedPreferences(ADMIN_EXTRAS_BUNDLE_FILENAME, 0);
+ }
+
+}
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk b/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk
index 5f2a64e..3c0a2a1 100644
--- a/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk
@@ -28,7 +28,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 ctstestrunner ub-uiautomator
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
# tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
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 f994e9e..33d4427 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
@@ -26,6 +26,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
+import android.os.Process;
import android.support.test.uiautomator.UiDevice;
import android.test.InstrumentationTestCase;
@@ -195,4 +196,8 @@
return false;
}
}
+
+ protected int getInstallReason(String packageName) {
+ return mPackageManager.getInstallReason(packageName, Process.myUserHandle());
+ }
}
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/InstallReasonTest.java b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/InstallReasonTest.java
new file mode 100644
index 0000000..d6cf39b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/InstallReasonTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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.packageinstaller;
+
+import android.content.pm.PackageManager;
+
+/**
+ * This class tests that the install reason is correctly recorded for packages.
+ */
+public class InstallReasonTest extends BasePackageInstallTest {
+ public void testInstallReason() throws Exception {
+ // Verify that since the Device Owner was sideloaded, its install reason is unknown.
+ assertEquals(PackageManager.INSTALL_REASON_UNKNOWN, getInstallReason(PACKAGE_NAME));
+
+ // Verify that when the Device Owner installs another package, its install reason is
+ // recorded as enterprise policy.
+ assertInstallPackage();
+ assertEquals(PackageManager.INSTALL_REASON_POLICY, getInstallReason(TEST_APP_PKG));
+ tryUninstallPackage();
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminHostSideTest.java
index c5e022b..591a5be 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminHostSideTest.java
@@ -27,47 +27,24 @@
*/
public abstract class BaseDeviceAdminHostSideTest extends BaseDevicePolicyTest {
- private static final String ADMIN_RECEIVER_TEST_CLASS = "BaseDeviceAdminTest$AdminReceiver";
-
- private static final String UNPROTECTED_ADMIN_RECEIVER_TEST_CLASS =
- "DeviceAdminReceiverWithNoProtection";
-
protected int mUserId;
- /** returns "com.android.cts.deviceadmin" */
- protected final String getDeviceAdminJavaPackage() {
- return "com.android.cts.deviceadmin";
- }
-
- /** e.g. 23, 24, etc. */
protected abstract int getTargetApiVersion();
- /** e.g. CtsDeviceAdminApp24.apk */
protected final String getDeviceAdminApkFileName() {
- return "CtsDeviceAdminApp" + getTargetApiVersion() + ".apk";
+ return DeviceAdminHelper.getDeviceAdminApkFileName(getTargetApiVersion());
}
- /** e.g. "com.android.cts.deviceadmin24" */
protected final String getDeviceAdminApkPackage() {
- return getDeviceAdminJavaPackage() + getTargetApiVersion();
+ return DeviceAdminHelper.getDeviceAdminApkPackage(getTargetApiVersion());
}
- /**
- * e.g.
- * "com.android.cts.deviceadmin24/com.android.cts.deviceadmin.BaseDeviceAdminTest$AdminReceiver"
- */
protected final String getAdminReceiverComponent() {
- return getDeviceAdminApkPackage() + "/" + getDeviceAdminJavaPackage() + "." +
- ADMIN_RECEIVER_TEST_CLASS;
+ return DeviceAdminHelper.getAdminReceiverComponent(getTargetApiVersion());
}
- /**
- * e.g.
- * "com.android.cts.deviceadmin24/com.android.cts.deviceadmin.DeviceAdminReceiverWithNoProtection"
- */
protected final String getUnprotectedAdminReceiverComponent() {
- return getDeviceAdminApkPackage() + "/" + getDeviceAdminJavaPackage() + "." +
- UNPROTECTED_ADMIN_RECEIVER_TEST_CLASS;
+ return DeviceAdminHelper.getUnprotectedAdminReceiverComponent(getTargetApiVersion());
}
@Override
@@ -85,8 +62,7 @@
@Override
protected void tearDown() throws Exception {
if (mHasFeature) {
- assertTrue("Failed to remove admin",
- removeAdmin(getAdminReceiverComponent(), mUserId));
+ assertTrue("Failed to remove admin", removeAdmin(getAdminReceiverComponent(), mUserId));
getDevice().uninstallPackage(getDeviceAdminApkPackage());
}
@@ -96,7 +72,7 @@
protected void runTests(@Nonnull String apk, @Nonnull String className,
@Nullable String method) throws DeviceNotAvailableException {
runDeviceTestsAsUser(apk,
- getDeviceAdminJavaPackage() + "." + className, method, mUserId);
+ DeviceAdminHelper.getDeviceAdminJavaPackage() + "." + className, method, mUserId);
}
protected void runTests(@Nonnull String apk, @Nonnull String className)
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index d2b8d25..d9eb2e6 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -34,6 +34,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -228,13 +229,20 @@
}
protected void removeTestUsers() throws Exception {
- for (int userId : listUsers()) {
- if (!mFixedUsers.contains(userId)) {
- removeUser(userId);
- }
+ for (int userId : getUsersCreatedByTests()) {
+ removeUser(userId);
}
}
+ /**
+ * Returns the users that have been created since running this class' setUp() method.
+ */
+ protected List<Integer> getUsersCreatedByTests() throws Exception {
+ List<Integer> result = listUsers();
+ result.removeAll(mFixedUsers);
+ return result;
+ }
+
/** Removes any packages that were installed during the test. */
protected void removeTestPackages() throws Exception {
for (String packageName : getDevice().getUninstallablePackageNames()) {
@@ -645,4 +653,71 @@
}
fail(failureMessage);
}
+
+ /**
+ * Sets a user restriction via SetPolicyActivity.
+ * <p>IMPORTANT: The package that contains SetPolicyActivity must have been installed prior to
+ * calling this method.
+ * @param key user restriction key
+ * @param value true if we should set the restriction, false if we should clear it
+ * @param userId userId to set/clear the user restriction on
+ * @param packageName package where SetPolicyActivity is installed
+ * @return The output of the command
+ * @throws DeviceNotAvailableException
+ */
+ protected String changeUserRestriction(String key, boolean value, int userId,
+ String packageName) throws DeviceNotAvailableException {
+ return changePolicy(getUserRestrictionCommand(value),
+ " --es extra-restriction-key " + key, userId, packageName);
+ }
+
+ /**
+ * Same as {@link #changeUserRestriction(String, boolean, int, String)} but asserts that it
+ * succeeds.
+ */
+ protected void changeUserRestrictionOrFail(String key, boolean value, int userId,
+ String packageName) throws DeviceNotAvailableException {
+ changePolicyOrFail(getUserRestrictionCommand(value), " --es extra-restriction-key " + key,
+ userId, packageName);
+ }
+
+ /**
+ * Sets some policy via SetPolicyActivity.
+ * <p>IMPORTANT: The package that contains SetPolicyActivity must have been installed prior to
+ * calling this method.
+ * @param command command to pass to SetPolicyActivity
+ * @param extras extras to pass to SetPolicyActivity
+ * @param userId the userId where we invoke SetPolicyActivity
+ * @param packageName where SetPolicyActivity is installed
+ * @return The output of the command
+ * @throws DeviceNotAvailableException
+ */
+ protected String changePolicy(String command, String extras, int userId, String packageName)
+ throws DeviceNotAvailableException {
+ String adbCommand = "am start -W --user " + userId
+ + " -c android.intent.category.DEFAULT "
+ + " --es extra-command " + command
+ + " " + extras
+ + " " + packageName + "/.SetPolicyActivity";
+ String commandOutput = getDevice().executeShellCommand(adbCommand);
+ CLog.d("Output for command " + adbCommand + ": " + commandOutput);
+ return commandOutput;
+ }
+
+ /**
+ * Same as {@link #changePolicy(String, String, int, String)} but asserts that it succeeds.
+ */
+ protected void changePolicyOrFail(String command, String extras, int userId,
+ String packageName) throws DeviceNotAvailableException {
+ String commandOutput = changePolicy(command, extras, userId, packageName);
+ assertTrue("Command was expected to succeed " + commandOutput,
+ commandOutput.contains("Status: ok"));
+ }
+
+ private String getUserRestrictionCommand(boolean setRestriction) {
+ if (setRestriction) {
+ return "add-restriction";
+ }
+ return "clear-restriction";
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
index bb1811f..e690a41 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
@@ -170,4 +170,30 @@
getDevice().uninstallPackage(DEVICE_OWNER_PKG);
}
}
+
+ public void testInstallReason() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
+ final File apk = buildHelper.getTestFile(TEST_APP_APK);
+ try {
+ // Install the test and prepare the test apk.
+ installAppAsUser(PACKAGE_INSTALLER_APK, mPrimaryUserId);
+ assertTrue(setDeviceOwner(PACKAGE_INSTALLER_ADMIN_COMPONENT, mPrimaryUserId,
+ /*expectFailure*/ false));
+
+ getDevice().uninstallPackage(TEST_APP_PKG);
+ assertTrue(getDevice().pushFile(apk, TEST_APP_LOCATION + apk.getName()));
+ runDeviceTestsAsUser(PACKAGE_INSTALLER_PKG,
+ PACKAGE_INSTALLER_PKG + ".InstallReasonTest", mPrimaryUserId);
+ } finally {
+ assertTrue("Failed to remove device owner.",
+ removeAdmin(PACKAGE_INSTALLER_ADMIN_COMPONENT, mPrimaryUserId));
+ final String command = "rm " + TEST_APP_LOCATION + apk.getName();
+ final String commandOutput = getDevice().executeShellCommand(command);
+ getDevice().uninstallPackage(TEST_APP_PKG);
+ getDevice().uninstallPackage(PACKAGE_INSTALLER_PKG);
+ }
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHelper.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHelper.java
new file mode 100644
index 0000000..4955c42
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHelper.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 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.devicepolicy;
+
+public class DeviceAdminHelper {
+
+ private static final String ADMIN_RECEIVER_TEST_CLASS = "BaseDeviceAdminTest$AdminReceiver";
+
+ private static final String UNPROTECTED_ADMIN_RECEIVER_TEST_CLASS =
+ "DeviceAdminReceiverWithNoProtection";
+
+ /** returns "com.android.cts.deviceadmin" */
+ static String getDeviceAdminJavaPackage() {
+ return "com.android.cts.deviceadmin";
+ }
+
+ /** e.g. CtsDeviceAdminApp24.apk */
+ static String getDeviceAdminApkFileName(int targetApiVersion) {
+ return "CtsDeviceAdminApp" + targetApiVersion + ".apk";
+ }
+
+ /** e.g. "com.android.cts.deviceadmin24" */
+ static String getDeviceAdminApkPackage(int targetApiVersion) {
+ return getDeviceAdminJavaPackage() + targetApiVersion;
+ }
+
+ /**
+ * e.g.
+ * "com.android.cts.deviceadmin24/com.android.cts.deviceadmin.BaseDeviceAdminTest$AdminReceiver"
+ */
+ static String getAdminReceiverComponent(int targetApiVersion) {
+ return getDeviceAdminApkPackage(targetApiVersion) + "/" + getDeviceAdminJavaPackage() + "."
+ + ADMIN_RECEIVER_TEST_CLASS;
+ }
+
+ /**
+ * e.g.
+ * "com.android.cts.deviceadmin24/com.android.cts.deviceadmin.DeviceAdminReceiverWithNoProtection"
+ */
+ static String getUnprotectedAdminReceiverComponent(int targetApiVersion) {
+ return getDeviceAdminApkPackage(targetApiVersion) + "/" + getDeviceAdminJavaPackage()
+ + "." + UNPROTECTED_ADMIN_RECEIVER_TEST_CLASS;
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index a6e8e5f..c2ee3f7 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -83,8 +83,6 @@
private static final String VPN_APP_PKG = "com.android.cts.vpnfirewall";
private static final String VPN_APP_APK = "CtsVpnFirewallApp.apk";
- private static final String COMMAND_ADD_USER_RESTRICTION = "add-restriction";
- private static final String COMMAND_CLEAR_USER_RESTRICTION = "clear-restriction";
private static final String COMMAND_BLOCK_ACCOUNT_TYPE = "block-accounttype";
private static final String COMMAND_UNBLOCK_ACCOUNT_TYPE = "unblock-accounttype";
@@ -365,13 +363,11 @@
installAppAsUser(ACCOUNT_MANAGEMENT_APK, mUserId);
try {
- changeUserRestrictionForUser(DISALLOW_MODIFY_ACCOUNTS, COMMAND_ADD_USER_RESTRICTION,
- mUserId);
+ changeUserRestrictionOrFail(DISALLOW_MODIFY_ACCOUNTS, true, mUserId);
executeAccountTest("testAddAccount_blocked");
} finally {
// Ensure we clear the user restriction
- changeUserRestrictionForUser(DISALLOW_MODIFY_ACCOUNTS, COMMAND_CLEAR_USER_RESTRICTION,
- mUserId);
+ changeUserRestrictionOrFail(DISALLOW_MODIFY_ACCOUNTS, false, mUserId);
}
executeAccountTest("testAddAccount_allowed");
}
@@ -383,13 +379,11 @@
installAppAsUser(ACCOUNT_MANAGEMENT_APK, mUserId);
try {
- changeUserRestrictionForUser(DISALLOW_MODIFY_ACCOUNTS, COMMAND_ADD_USER_RESTRICTION,
- mUserId);
+ changeUserRestrictionOrFail(DISALLOW_MODIFY_ACCOUNTS, true, mUserId);
executeAccountTest("testRemoveAccount_blocked");
} finally {
// Ensure we clear the user restriction
- changeUserRestrictionForUser(DISALLOW_MODIFY_ACCOUNTS, COMMAND_CLEAR_USER_RESTRICTION,
- mUserId);
+ changeUserRestrictionOrFail(DISALLOW_MODIFY_ACCOUNTS, false, mUserId);
}
executeAccountTest("testRemoveAccount_allowed");
}
@@ -461,13 +455,11 @@
installAppAsUser(CUSTOMIZATION_APP_APK, mUserId);
try {
- changeUserRestrictionForUser(DISALLOW_SET_WALLPAPER, COMMAND_ADD_USER_RESTRICTION,
- mUserId);
+ changeUserRestrictionOrFail(DISALLOW_SET_WALLPAPER, true, mUserId);
runDeviceTestsAsUser(CUSTOMIZATION_APP_PKG, ".CustomizationTest",
"testSetWallpaper_disallowed", mUserId);
} finally {
- changeUserRestrictionForUser(DISALLOW_SET_WALLPAPER, COMMAND_CLEAR_USER_RESTRICTION,
- mUserId);
+ changeUserRestrictionOrFail(DISALLOW_SET_WALLPAPER, false, mUserId);
}
}
@@ -516,14 +508,12 @@
// Add restrictions and test if we can install the apk.
getDevice().uninstallPackage(TEST_APP_PKG);
- changeUserRestrictionForUser(DISALLOW_INSTALL_UNKNOWN_SOURCES,
- COMMAND_ADD_USER_RESTRICTION, mUserId);
+ changeUserRestrictionOrFail(DISALLOW_INSTALL_UNKNOWN_SOURCES, true, mUserId);
runDeviceTestsAsUser(PACKAGE_INSTALLER_PKG, ".ManualPackageInstallTest",
"testManualInstallBlocked", mUserId);
// Clear restrictions and test if we can install the apk.
- changeUserRestrictionForUser(DISALLOW_INSTALL_UNKNOWN_SOURCES,
- COMMAND_CLEAR_USER_RESTRICTION, mUserId);
+ changeUserRestrictionOrFail(DISALLOW_INSTALL_UNKNOWN_SOURCES, false, mUserId);
// Enable Unknown sources in Settings.
unknownSourceSetting =
@@ -605,12 +595,10 @@
}
final int userId = createUser();
try {
- changeUserRestrictionForUser(DISALLOW_REMOVE_USER, COMMAND_ADD_USER_RESTRICTION,
- mUserId);
+ changeUserRestrictionOrFail(DISALLOW_REMOVE_USER, true, mUserId);
assertFalse(getDevice().removeUser(userId));
} finally {
- changeUserRestrictionForUser(DISALLOW_REMOVE_USER, COMMAND_CLEAR_USER_RESTRICTION,
- mUserId);
+ changeUserRestrictionOrFail(DISALLOW_REMOVE_USER, false, mUserId);
assertTrue(getDevice().removeUser(userId));
}
}
@@ -672,34 +660,26 @@
".ApplicationRestrictionsManagerTest", testName, mUserId);
}
- private void changeUserRestrictionForUser(String key, String command, int userId)
+ private void changeUserRestrictionOrFail(String key, boolean value, int userId)
throws DeviceNotAvailableException {
- changePolicy(command, "--es extra-restriction-key " + key, userId);
+ changeUserRestrictionOrFail(key, value, userId, DEVICE_ADMIN_PKG);
}
private void changeAccountManagement(String command, String accountType, int userId)
throws DeviceNotAvailableException {
- changePolicy(command, "--es extra-account-type " + accountType, userId);
+ changePolicyOrFail(command, "--es extra-account-type " + accountType, userId);
}
private void changeApplicationRestrictionsManagingPackage(String packageName)
throws DeviceNotAvailableException {
String packageNameExtra = (packageName != null)
? "--es extra-package-name " + packageName : "";
- changePolicy("set-app-restrictions-manager", packageNameExtra, mUserId);
+ changePolicyOrFail("set-app-restrictions-manager", packageNameExtra, mUserId);
}
- private void changePolicy(String command, String extras, int userId)
+ private void changePolicyOrFail(String command, String extras, int userId)
throws DeviceNotAvailableException {
- String adbCommand = "am start -W --user " + userId
- + " -c android.intent.category.DEFAULT "
- + " --es extra-command " + command
- + " " + extras
- + " " + DEVICE_ADMIN_PKG + "/.SetPolicyActivity";
- String commandOutput = getDevice().executeShellCommand(adbCommand);
- CLog.d("Output for command " + adbCommand + ": " + commandOutput);
- assertTrue("Command was expected to succeed " + commandOutput,
- commandOutput.contains("Status: ok"));
+ changePolicyOrFail(command, extras, userId, DEVICE_ADMIN_PKG);
}
/**
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusManagedProfileTest.java
index 76c1f21..c37b2e2 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusManagedProfileTest.java
@@ -16,6 +16,8 @@
package com.android.cts.devicepolicy;
+import java.util.List;
+
/**
* Tests for having both device owner and profile owner. Device owner is setup for you in
* {@link #setUp()} and it is always the {@link #COMP_DPC_PKG}. You are required to call
@@ -388,6 +390,45 @@
assertUserGetsRemoved(mProfileUserId);
}
+ public void testCannotBindToSecondaryUser() throws Exception {
+ if (!mHasFeature || !canCreateAdditionalUsers(1)) {
+ return;
+ }
+ runDeviceTestsAsUser(
+ COMP_DPC_PKG,
+ MANAGEMENT_TEST,
+ "testCreateSecondaryUser",
+ mPrimaryUserId);
+ List<Integer> newUsers = getUsersCreatedByTests();
+ assertEquals(1, newUsers.size());
+ int secondaryUserId = newUsers.get(0);
+ getDevice().startUser(secondaryUserId);
+
+ // Set the same affiliation ids on both users.
+ runDeviceTestsAsUser(
+ COMP_DPC_PKG,
+ AFFILIATION_TEST,
+ "testSetAffiliationId1",
+ mPrimaryUserId);
+ runDeviceTestsAsUser(
+ COMP_DPC_PKG,
+ AFFILIATION_TEST,
+ "testSetAffiliationId1",
+ secondaryUserId);
+
+ // But check that we still can't bind to the other user.
+ runDeviceTestsAsUser(
+ COMP_DPC_PKG,
+ MANAGEMENT_TEST,
+ "testNoBindDeviceAdminTargetUsers",
+ mPrimaryUserId);
+ runDeviceTestsAsUser(
+ COMP_DPC_PKG,
+ MANAGEMENT_TEST,
+ "testNoBindDeviceAdminTargetUsers",
+ secondaryUserId);
+ }
+
protected void setupManagedProfile(String apkName, String packageName,
String adminReceiverClassName) throws Exception {
// Temporary disable the DISALLOW_ADD_MANAGED_PROFILE, so that we can create profile
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 8be8f38..676f455 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -16,7 +16,10 @@
package com.android.cts.devicepolicy;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.TimeUnit;
/**
@@ -217,23 +220,11 @@
return;
}
- ArrayList<Integer> originalUsers = listUsers();
executeDeviceTestMethod(".CreateAndManageUserTest", "testCreateAndManageEphemeralUser");
- ArrayList<Integer> newUsers = listUsers();
-
- // Check that exactly one new user was created.
- assertEquals(
- "One user should have been created", originalUsers.size() + 1, newUsers.size());
-
- // Get the id of the newly created user.
- int newUserId = -1;
- for (int userId : newUsers) {
- if (!originalUsers.contains(userId)) {
- newUserId = userId;
- break;
- }
- }
+ List<Integer> newUsers = getUsersCreatedByTests();
+ assertEquals(1, newUsers.size());
+ int newUserId = newUsers.get(0);
// Get the flags of the new user and check the user is ephemeral.
int flags = getUserFlags(newUserId);
@@ -389,6 +380,16 @@
}
}
+ /**
+ * Execute WifiSetHttpProxyTest as device owner.
+ */
+ public void testWifiSetHttpProxyTest() throws Exception {
+ final boolean hasWifi = hasDeviceFeature("android.hardware.wifi");
+ if (hasWifi && mHasFeature) {
+ executeDeviceOwnerTest("WifiSetHttpProxyTest");
+ }
+ }
+
public void testCannotSetDeviceOwnerAgain() throws Exception {
if (!mHasFeature) {
return;
@@ -463,6 +464,35 @@
executeDeviceOwnerTest("DeviceOwnerProvisioningTest");
}
+ public void testDisallowFactoryReset() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ int adminVersion = 24;
+ changeUserRestrictionOrFail("no_factory_reset", true, mPrimaryUserId,
+ DEVICE_OWNER_PKG);
+ try {
+ installAppAsUser(DeviceAdminHelper.getDeviceAdminApkFileName(adminVersion),
+ mPrimaryUserId);
+ setDeviceAdmin(DeviceAdminHelper.getAdminReceiverComponent(adminVersion),
+ mPrimaryUserId);
+ runDeviceTestsAsUser(
+ DeviceAdminHelper.getDeviceAdminApkPackage(adminVersion),
+ DeviceAdminHelper.getDeviceAdminJavaPackage() + ".WipeDataTest",
+ "testWipeDataThrowsSecurityException", mPrimaryUserId);
+ } finally {
+ removeAdmin(DeviceAdminHelper.getAdminReceiverComponent(adminVersion), mPrimaryUserId);
+ getDevice().uninstallPackage(DeviceAdminHelper.getDeviceAdminApkPackage(adminVersion));
+ }
+ }
+
+ public void testBackupServiceEnabling() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ executeDeviceOwnerTest("BackupServiceEnabledTest");
+ }
+
private void executeDeviceOwnerTest(String testClassName) throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
new file mode 100644
index 0000000..08a35d6
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 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.devicepolicy;
+
+public class ManagedProfileProvisioningTest extends BaseDevicePolicyTest {
+ private static final String MANAGED_PROFILE_PKG = "com.android.cts.managedprofile";
+ private static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
+
+ private int mProfileUserId;
+ private int mParentUserId;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // We need multi user to be supported in order to create a profile of the user owner.
+ mHasFeature = mHasFeature && hasDeviceFeature(
+ "android.software.managed_users");
+
+ if (mHasFeature) {
+ removeTestUsers();
+ mParentUserId = mPrimaryUserId;
+ installAppAsUser(MANAGED_PROFILE_APK, mParentUserId);
+
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ProvisioningTest",
+ "testProvisionManagedProfile", mParentUserId);
+
+ mProfileUserId = getFirstManagedProfileUserId();
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mHasFeature) {
+ removeUser(mProfileUserId);
+ getDevice().uninstallPackage(MANAGED_PROFILE_PKG);
+ }
+ super.tearDown();
+ }
+
+ public void testManagedProfileProvisioning() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ProvisioningTest",
+ "testIsManagedProfile", mProfileUserId);
+ }
+
+ public void testEXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ProvisioningTest",
+ "testVerifyAdminExtraBundle", mProfileUserId);
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 5f5b319..1ec9c0ab 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -71,7 +71,6 @@
private static final String FEATURE_TELEPHONY = "android.hardware.telephony";
private static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
- private static final String ADD_RESTRICTION_COMMAND = "add-restriction";
private static final long TIMEOUT_USER_LOCKED_MILLIS = TimeUnit.SECONDS.toMillis(15);
@@ -244,8 +243,7 @@
// managed profile
assertAppLinkResult("testTwoReceivers");
- changeUserRestrictionForUser("allow_parent_profile_app_linking", ADD_RESTRICTION_COMMAND,
- mProfileUserId);
+ changeUserRestrictionOrFail("allow_parent_profile_app_linking", true, mProfileUserId);
// Now we should also have one receiver in the primary user, so three receivers in total.
assertAppLinkResult("testThreeReceivers");
@@ -300,8 +298,7 @@
// We now set allow_parent_profile_app_linking, and hence we should have the app handler
// in parent user if it is enabled.
- changeUserRestrictionForUser("allow_parent_profile_app_linking", ADD_RESTRICTION_COMMAND,
- mProfileUserId);
+ changeUserRestrictionOrFail("allow_parent_profile_app_linking", true, mProfileUserId);
disableComponentOrPackage(mParentUserId, APP_HANDLER_COMPONENT);
disableComponentOrPackage(mProfileUserId, APP_HANDLER_COMPONENT);
@@ -431,15 +428,13 @@
}
String restriction = "no_debugging_features"; // UserManager.DISALLOW_DEBUGGING_FEATURES
- String addRestrictionCommandOutput =
- changeUserRestrictionForUser(restriction, ADD_RESTRICTION_COMMAND, mProfileUserId);
- assertTrue("Command was expected to succeed " + addRestrictionCommandOutput,
- addRestrictionCommandOutput.contains("Status: ok"));
+ changeUserRestrictionOrFail(restriction, true, mProfileUserId);
+
// This should now fail, as the shell is not available to start activities under a different
// user once the restriction is in place.
- addRestrictionCommandOutput =
- changeUserRestrictionForUser(restriction, ADD_RESTRICTION_COMMAND, mProfileUserId);
+ String addRestrictionCommandOutput =
+ changeUserRestriction(restriction, true, mProfileUserId);
assertTrue(
"Expected SecurityException when starting the activity "
+ addRestrictionCommandOutput,
@@ -640,13 +635,8 @@
runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NfcTest",
"testNfcShareEnabled", mParentUserId);
- String restriction = "no_outgoing_beam"; // UserManager.DISALLOW_OUTGOING_BEAM
- String command = "add-restriction";
-
- String addRestrictionCommandOutput =
- changeUserRestrictionForUser(restriction, command, mProfileUserId);
- assertTrue("Command was expected to succeed " + addRestrictionCommandOutput,
- addRestrictionCommandOutput.contains("Status: ok"));
+ changeUserRestrictionOrFail("no_outgoing_beam" /* UserManager.DISALLOW_OUTGOING_BEAM */,
+ true, mProfileUserId);
runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NfcTest",
"testNfcShareDisabled", mProfileUserId);
@@ -840,17 +830,14 @@
+ getDevice().executeShellCommand(command));
}
- private String changeUserRestrictionForUser(String key, String command, int userId)
+ private void changeUserRestrictionOrFail(String key, boolean value, int userId)
throws DeviceNotAvailableException {
- String adbCommand = "am start -W --user " + userId
- + " -c android.intent.category.DEFAULT "
- + " --es extra-command " + command
- + " --es extra-restriction-key " + key
- + " " + MANAGED_PROFILE_PKG + "/.SetPolicyActivity";
- // Don't log output because sometimes used expecting failures.
- CLog.d("Running command " + adbCommand);
- String commandOutput = getDevice().executeShellCommand(adbCommand);
- return commandOutput;
+ changeUserRestrictionOrFail(key, value, userId, MANAGED_PROFILE_PKG);
+ }
+
+ private String changeUserRestriction(String key, boolean value, int userId)
+ throws DeviceNotAvailableException {
+ return changeUserRestriction(key, value, userId, MANAGED_PROFILE_PKG);
}
private String changeCrossProfileWidgetForUser(String packageName, String command, int userId)
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
index 0c2f575..e8c6c57 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
@@ -26,6 +26,17 @@
private static final String ADMIN_RECEIVER_TEST_CLASS
= ".BaseDeviceAdminTest$BasicAdminReceiver";
+ private static final String GLOBAL_RESTRICTIONS_TEST_CLASS =
+ "userrestrictions.ProfileGlobalRestrictionsTest";
+ private static final String SET_GLOBAL_RESTRICTIONS_TEST =
+ "testSetProfileGlobalRestrictions";
+ private static final String CLEAR_GLOBAL_RESTRICTIONS_TEST =
+ "testClearProfileGlobalRestrictions";
+ private static final String ENSURE_GLOBAL_RESTRICTIONS_TEST =
+ "testProfileGlobalRestrictionsEnforced";
+ private static final String ENSURE_NO_GLOBAL_RESTRICTIONS_TEST =
+ "testProfileGlobalRestrictionsNotEnforced";
+
private boolean mRemoveOwnerInTearDown;
private int mDeviceOwnerUserId;
@@ -69,11 +80,7 @@
if (!mHasFeature) {
return;
}
- installAppAsUser(DEVICE_ADMIN_APK, mDeviceOwnerUserId);
- assertTrue("Failed to set device owner",
- setDeviceOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
- mDeviceOwnerUserId, /*expectFailure*/ false));
- mRemoveOwnerInTearDown = true;
+ setDo();
runTests("userrestrictions.DeviceOwnerUserRestrictionsTest",
"testDefaultRestrictions", mDeviceOwnerUserId);
@@ -90,11 +97,7 @@
return;
}
- installAppAsUser(DEVICE_ADMIN_APK, mDeviceOwnerUserId);
- assertTrue("Failed to set profile owner",
- setProfileOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
- mDeviceOwnerUserId, /* expectFailure */ false));
- mRemoveOwnerInTearDown = true;
+ setPoAsUser(mDeviceOwnerUserId);
runTests("userrestrictions.PrimaryProfileOwnerUserRestrictionsTest",
"testDefaultRestrictions", mDeviceOwnerUserId);
@@ -107,11 +110,7 @@
return;
}
final int secondaryUserId = createUser();
-
- installAppAsUser(DEVICE_ADMIN_APK, secondaryUserId);
- assertTrue("Failed to set profile owner",
- setProfileOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
- secondaryUserId, /* expectFailure */ false));
+ setPoAsUser(secondaryUserId);
runTests("userrestrictions.SecondaryProfileOwnerUserRestrictionsTest",
"testDefaultRestrictions", secondaryUserId);
@@ -126,20 +125,11 @@
if (!mHasFeature || !mSupportsMultiUser) {
return;
}
- // Set DO
- installAppAsUser(DEVICE_ADMIN_APK, mDeviceOwnerUserId);
- assertTrue("Failed to set device owner",
- setDeviceOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
- mDeviceOwnerUserId, /*expectFailure*/ false));
- mRemoveOwnerInTearDown = true;
+ setDo();
// Create another user and set PO.
final int secondaryUserId = createUser();
-
- installAppAsUser(DEVICE_ADMIN_APK, secondaryUserId);
- assertTrue("Failed to set profile owner",
- setProfileOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
- secondaryUserId, /* expectFailure */ false));
+ setPoAsUser(secondaryUserId);
// Let DO set all restrictions.
runTests("userrestrictions.DeviceOwnerUserRestrictionsTest",
@@ -177,20 +167,12 @@
// Can't set PO on user-0 in this mode.
return;
}
- // Set DO on user 0
- installAppAsUser(DEVICE_ADMIN_APK, mDeviceOwnerUserId);
- assertTrue("Failed to set profile owner",
- setProfileOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
- mDeviceOwnerUserId, /* expectFailure */ false));
- mRemoveOwnerInTearDown = true;
+ // Set PO on user 0
+ setPoAsUser(mDeviceOwnerUserId);
// Create another user and set PO.
final int secondaryUserId = createUser();
-
- installAppAsUser(DEVICE_ADMIN_APK, secondaryUserId);
- assertTrue("Failed to set profile owner",
- setProfileOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
- secondaryUserId, /* expectFailure */ false));
+ setPoAsUser(secondaryUserId);
// Let user-0 PO sets all restrictions.
runTests("userrestrictions.PrimaryProfileOwnerUserRestrictionsTest",
@@ -200,4 +182,100 @@
runTests("userrestrictions.SecondaryProfileOwnerUserRestrictionsTest",
"testDefaultRestrictionsOnly", secondaryUserId);
}
+
+ /**
+ * DO sets profile global restrictions (only ENSURE_VERIFY_APPS), should affect all
+ * users (not a particularly special case but to be sure).
+ */
+ public void testUserRestrictions_profileGlobalRestrictionsAsDo() throws Exception {
+ if (!mHasFeature || !mSupportsMultiUser) {
+ return;
+ }
+ setDo();
+
+ // Create another user with PO.
+ final int secondaryUserId = createUser();
+ setPoAsUser(secondaryUserId);
+
+ final int[] usersToCheck = {mDeviceOwnerUserId, secondaryUserId};
+
+ // Do sets the restriction.
+ setAndCheckProfileGlobalRestriction(mDeviceOwnerUserId, usersToCheck);
+ }
+
+ /**
+ * Managed profile owner sets profile global restrictions (only ENSURE_VERIFY_APPS), should
+ * affect all users.
+ */
+ public void testUserRestrictions_ProfileGlobalRestrictionsAsPo() throws Exception {
+ if (!mHasFeature || !mSupportsMultiUser) {
+ return;
+ }
+ // Set PO on user 0
+ setPoAsUser(mDeviceOwnerUserId);
+
+ // Create another user with PO.
+ final int secondaryUserId = createManagedProfile(mDeviceOwnerUserId /* parentUserId */);
+ setPoAsUser(secondaryUserId);
+
+ final int[] usersToCheck = {mDeviceOwnerUserId, secondaryUserId};
+
+ // Check the case when primary user's PO sets the restriction.
+ setAndCheckProfileGlobalRestriction(mDeviceOwnerUserId, usersToCheck);
+
+ // Check the case when managed profile owner sets the restriction.
+ setAndCheckProfileGlobalRestriction(secondaryUserId, usersToCheck);
+ }
+
+ /** Installs admin package and makes it a profile owner for a given user. */
+ private void setPoAsUser(int userId) throws Exception {
+ installAppAsUser(DEVICE_ADMIN_APK, userId);
+ assertTrue("Failed to set profile owner",
+ setProfileOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
+ userId, /* expectFailure */ false));
+ // If PO is not in primary user, it will be removed with the user.
+ if (userId == mDeviceOwnerUserId) {
+ mRemoveOwnerInTearDown = true;
+ }
+ }
+
+ /** Installs admin package and makes it a device owner. */
+ private void setDo() throws Exception {
+ installAppAsUser(DEVICE_ADMIN_APK, mDeviceOwnerUserId);
+ assertTrue("Failed to set device owner",
+ setDeviceOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
+ mDeviceOwnerUserId, /*expectFailure*/ false));
+ mRemoveOwnerInTearDown = true;
+ }
+
+ /**
+ * Sets user restriction and checks that it applies to all users.
+ * @param enforcingUserId user who should set/clear the restriction, should be either
+ * primary or secondary user id and should have device or profile owner active.
+ * @param usersToCheck users that should have this restriction enforced.
+ */
+ private void setAndCheckProfileGlobalRestriction(int enforcingUserId, int usersToCheck[])
+ throws Exception {
+ // Always try to clear the restriction to avoid undesirable side effects.
+ try {
+ // Set the restriction.
+ runGlobalRestrictionsTest(SET_GLOBAL_RESTRICTIONS_TEST, enforcingUserId);
+ // Check that the restriction is in power.
+ for (int userId : usersToCheck) {
+ runGlobalRestrictionsTest(ENSURE_GLOBAL_RESTRICTIONS_TEST, userId);
+ }
+ } finally {
+ // Clear the restriction.
+ runGlobalRestrictionsTest(CLEAR_GLOBAL_RESTRICTIONS_TEST, enforcingUserId);
+ // Check that the restriction is not in power anymore.
+ for (int userId : usersToCheck) {
+ runGlobalRestrictionsTest(ENSURE_NO_GLOBAL_RESTRICTIONS_TEST, userId);
+ }
+ }
+ }
+
+ /** Convenience method to run global user restrictions tests. */
+ private void runGlobalRestrictionsTest(String testMethodName, int userId) throws Exception {
+ runTests(GLOBAL_RESTRICTIONS_TEST_CLASS, testMethodName, userId);
+ }
}
diff --git a/hostsidetests/incident/apps/Android.mk b/hostsidetests/incident/apps/Android.mk
new file mode 100644
index 0000000..4a74e80
--- /dev/null
+++ b/hostsidetests/incident/apps/Android.mk
@@ -0,0 +1,20 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Build the test APKs using their own makefiles
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/incident/apps/netstatsapp/Android.mk b/hostsidetests/incident/apps/netstatsapp/Android.mk
new file mode 100644
index 0000000..682ec73
--- /dev/null
+++ b/hostsidetests/incident/apps/netstatsapp/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsNetStatsApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctstestrunner \
+ compatibility-device-util \
+ android-support-v4
+
+LOCAL_SDK_VERSION := test_current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/incident/apps/netstatsapp/AndroidManifest.xml b/hostsidetests/incident/apps/netstatsapp/AndroidManifest.xml
new file mode 100644
index 0000000..b9d4d17
--- /dev/null
+++ b/hostsidetests/incident/apps/netstatsapp/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.server.cts.netstats" >
+
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.cts.netstats" />
+</manifest>
diff --git a/hostsidetests/incident/apps/netstatsapp/src/com/android/server/cts/netstats/NetstatsDeviceTest.java b/hostsidetests/incident/apps/netstatsapp/src/com/android/server/cts/netstats/NetstatsDeviceTest.java
new file mode 100644
index 0000000..075a393
--- /dev/null
+++ b/hostsidetests/incident/apps/netstatsapp/src/com/android/server/cts/netstats/NetstatsDeviceTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.cts.netstats;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * Used by NetstatsIncidentTest. Makes some network requests so "dumpsys netstats" will have
+ * something to show.
+ */
+@RunWith(AndroidJUnit4.class)
+public class NetstatsDeviceTest {
+ private static final String TAG = "NetstatsDeviceTest";
+
+ @Test
+ public void testDoNetwork() throws Exception {
+ Log.i(TAG, "Making network requests...");
+
+ final URL url = new URL("http://www.android.com/");
+ final HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
+ try {
+ final int status = urlConnection.getResponseCode();
+
+ Log.i(TAG, "Response code from " + url + ": " + status);
+
+ // Doesn't matter what response code we got. We touched the network, which is enough.
+ } finally {
+ urlConnection.disconnect();
+ }
+ }
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/DiskStatsProtoTest.java b/hostsidetests/incident/src/com/android/server/cts/DiskStatsProtoTest.java
new file mode 100644
index 0000000..19a6a89
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/DiskStatsProtoTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cts;
+
+import android.service.diskstats.DiskStatsFreeSpaceProto;
+import android.service.diskstats.DiskStatsServiceDumpProto;
+
+/**
+ * Test proto dump of diskstats
+ */
+public class DiskStatsProtoTest extends ProtoDumpTestCase {
+ /**
+ * Test that diskstats dump is reasonable
+ *
+ * @throws Exception
+ */
+ public void testDump() throws Exception {
+ final DiskStatsServiceDumpProto dump = getDump(DiskStatsServiceDumpProto.parser(),
+ "dumpsys diskstats --proto");
+
+ // At least one partition listed
+ assertTrue(dump.getPartitionsFreeSpaceCount() > 0);
+ // Test latency
+ boolean testError = dump.getHasTestError();
+ if (testError) {
+ assertNotNull(dump.getErrorMessage());
+ } else {
+ assertTrue(dump.getWrite512BLatencyMillis() < 100); // Less than 100ms
+ }
+ DiskStatsServiceDumpProto.EncryptionType encryptionType = dump.getEncryption();
+ if ("file".equals(getDevice().getProperty("ro.crypto.type"))) {
+ assertEquals(DiskStatsServiceDumpProto.EncryptionType.ENCRYPTION_FILE_BASED,
+ encryptionType);
+ }
+ }
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java
new file mode 100644
index 0000000..7ca4a5a
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.cts;
+
+import android.service.NetworkStatsServiceDumpProto;
+
+/**
+ * Test for "dumpsys netstats --proto"
+ * Usage:
+
+ cts-tradefed run cts --skip-device-info --skip-preconditions \
+ --skip-system-status-check \
+ com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker \
+ -a armeabi-v7a -m CtsIncidentHostTestCases -t com.android.server.cts.NetstatsIncidentTest
+
+ */
+public class NetstatsIncidentTest extends ProtoDumpTestCase {
+ private static final String DEVICE_SIDE_TEST_APK = "CtsNetStatsApp.apk";
+ private static final String DEVICE_SIDE_TEST_PACKAGE = "com.android.server.cts.netstats";
+
+ @Override
+ protected void tearDown() throws Exception {
+ getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
+
+ super.tearDown();
+ }
+
+ /**
+ * Parse the output of "dumpsys netstats --proto" and make sure all the values are probable.
+ */
+ public void testSanityCheck() throws Exception {
+ installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true);
+
+ // Run the device side test which makes some network requests.
+ runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, null, null);
+
+ // Also does ping for more network activity.
+ getDevice().executeShellCommand("ping -c 8 -i 0 8.8.8.8");
+
+ // Force refresh the output.
+ getDevice().executeShellCommand("dumpsys netstats --poll");
+
+ final NetworkStatsServiceDumpProto dump = getDump(NetworkStatsServiceDumpProto.parser(),
+ "dumpsys netstats --proto");
+
+ // TODO Actually check the output.
+ }
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/ProtoDumpTestCase.java b/hostsidetests/incident/src/com/android/server/cts/ProtoDumpTestCase.java
index 4bd9c60..0ec59c2 100644
--- a/hostsidetests/incident/src/com/android/server/cts/ProtoDumpTestCase.java
+++ b/hostsidetests/incident/src/com/android/server/cts/ProtoDumpTestCase.java
@@ -16,17 +16,47 @@
package com.android.server.cts;
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.ddmlib.testrunner.TestResult;
+import com.android.ddmlib.testrunner.TestResult.TestStatus;
+import com.android.ddmlib.testrunner.TestRunResult;
+import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.CollectingByteOutputReceiver;
-import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.CollectingTestListener;
import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import com.google.protobuf.Parser;
-import java.util.Scanner;
+import java.io.FileNotFoundException;
+import java.util.Map;
-public class ProtoDumpTestCase extends DeviceTestCase {
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public class ProtoDumpTestCase extends DeviceTestCase implements IBuildReceiver {
+
+ protected IBuildInfo mCtsBuild;
+
+ private static final String TEST_RUNNER = "android.support.test.runner.AndroidJUnitRunner";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ assertNotNull(mCtsBuild);
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
/**
* Call onto the device with an adb shell command and get the results of
@@ -46,4 +76,69 @@
getDevice().executeShellCommand(command, receiver);
return parser.parseFrom(receiver.getOutput());
}
+
+ /**
+ * Install a device side test package.
+ *
+ * @param appFileName Apk file name, such as "CtsNetStatsApp.apk".
+ * @param grantPermissions whether to give runtime permissions.
+ */
+ protected void installPackage(String appFileName, boolean grantPermissions)
+ throws FileNotFoundException, DeviceNotAvailableException {
+ CLog.d("Installing app " + appFileName);
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
+ final String result = getDevice().installPackage(
+ buildHelper.getTestFile(appFileName), true, grantPermissions);
+ assertNull("Failed to install " + appFileName + ": " + result, result);
+ }
+
+ /**
+ * Run a device side test.
+ *
+ * @param pkgName Test package name, such as "com.android.server.cts.netstats".
+ * @param testClassName Test class name; either a fully qualified name, or "." + a class name.
+ * @param testMethodName Test method name.
+ * @throws DeviceNotAvailableException
+ */
+ protected void runDeviceTests(@Nonnull String pkgName,
+ @Nullable String testClassName, @Nullable String testMethodName)
+ throws DeviceNotAvailableException {
+ if (testClassName != null && testClassName.startsWith(".")) {
+ testClassName = pkgName + testClassName;
+ }
+
+ RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(
+ pkgName, TEST_RUNNER, getDevice().getIDevice());
+ if (testClassName != null && testMethodName != null) {
+ testRunner.setMethodName(testClassName, testMethodName);
+ } else if (testClassName != null) {
+ testRunner.setClassName(testClassName);
+ }
+
+ CollectingTestListener listener = new CollectingTestListener();
+ assertTrue(getDevice().runInstrumentationTests(testRunner, listener));
+
+ final TestRunResult result = listener.getCurrentRunResults();
+ if (result.isRunFailure()) {
+ throw new AssertionError("Failed to successfully run device tests for "
+ + result.getName() + ": " + result.getRunFailureMessage());
+ }
+ if (result.getNumTests() == 0) {
+ throw new AssertionError("No tests were run on the device");
+ }
+
+ if (result.hasFailedTests()) {
+ // build a meaningful error message
+ StringBuilder errorBuilder = new StringBuilder("On-device tests failed:\n");
+ for (Map.Entry<TestIdentifier, TestResult> resultEntry :
+ result.getTestResults().entrySet()) {
+ if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
+ errorBuilder.append(resultEntry.getKey().toString());
+ errorBuilder.append(":\n");
+ errorBuilder.append(resultEntry.getValue().getStackTrace());
+ }
+ }
+ throw new AssertionError(errorBuilder.toString());
+ }
+ }
}
diff --git a/hostsidetests/security/Android.mk b/hostsidetests/security/Android.mk
index 334ce7e..2704850 100644
--- a/hostsidetests/security/Android.mk
+++ b/hostsidetests/security/Android.mk
@@ -42,7 +42,7 @@
selinux_general_property_contexts := $(call intermediates-dir-for,ETC,general_property_contexts)/general_property_contexts
-selinux_general_service_contexts := $(call intermediates-dir-for,ETC,general_service_contexts)/general_service_contexts
+selinux_plat_service_contexts := $(call intermediates-dir-for,ETC,plat_service_contexts)/plat_service_contexts
LOCAL_JAVA_RESOURCE_FILES := \
$(HOST_OUT_EXECUTABLES)/checkseapp \
@@ -51,7 +51,7 @@
$(selinux_plat_seapp_neverallows) \
$(selinux_general_file_contexts) \
$(selinux_general_property_contexts) \
- $(selinux_general_service_contexts)
+ $(selinux_plat_service_contexts)
selinux_general_policy := $(call intermediates-dir-for,ETC,general_sepolicy.conf)/general_sepolicy.conf
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index 7d386d2..b3f4ad7 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -367,12 +367,12 @@
/* obtain service_contexts file from running device */
deviceSvcFile = File.createTempFile("service_contexts", ".tmp");
deviceSvcFile.deleteOnExit();
- mDevice.pullFile("/service_contexts", deviceSvcFile);
+ mDevice.pullFile("/plat_service_contexts", deviceSvcFile);
/* retrieve the AOSP service_contexts file from jar */
- aospSvcFile = copyResourceToTempFile("/general_service_contexts");
+ aospSvcFile = copyResourceToTempFile("/plat_service_contexts");
- assertFileStartsWith(aospSvcFile, deviceSvcFile);
+ assertFileEquals(aospSvcFile, deviceSvcFile);
}
/**
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
index a022625..d604daf 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
@@ -233,6 +233,33 @@
}
/**
+ * Test that move-task works when moving between displays.
+ */
+ public void testMoveTaskBetweenDisplays() throws Exception {
+ // Create new virtual display.
+ final DisplayState newDisplay = createVirtualDisplay(CUSTOM_DENSITY_DPI,
+ false /* launchInSplitScreen */);
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+ mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
+ VIRTUAL_DISPLAY_ACTIVITY);
+ mAmWmState.assertFocusedStack("Focus must remain on primary display",
+ FULLSCREEN_WORKSPACE_STACK_ID);
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
+ mAmWmState.assertFocusedActivity("Focus must be on secondary display", TEST_ACTIVITY_NAME);
+ mAmWmState.assertNotFocusedStack("Focused stack must be on secondary display",
+ FULLSCREEN_WORKSPACE_STACK_ID);
+
+ // Launch other activity with different uid and check it is launched on primary display.
+ moveActivityToStack(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.waitForFocusedStack(mDevice, FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY_NAME);
+ mAmWmState.assertFocusedStack("Focus must return to primary display",
+ FULLSCREEN_WORKSPACE_STACK_ID);
+ }
+
+ /**
* Tests launching activities on secondary display and then removing it to see if stack focus
* is moved correctly.
*/
diff --git a/libs/deviceutillegacy/Android.mk b/libs/deviceutillegacy/Android.mk
index 3784394..f169ca8 100644
--- a/libs/deviceutillegacy/Android.mk
+++ b/libs/deviceutillegacy/Android.mk
@@ -16,7 +16,10 @@
include $(CLEAR_VARS)
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ compatibility-device-util \
+ junit \
+ legacy-android-test
LOCAL_SRC_FILES := \
$(call all-java-files-under, src)
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index a3aa96a..413ff7d 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -36,7 +36,7 @@
public class AccessibilityNodeInfoTest extends AndroidTestCase {
/** The number of properties of the {@link AccessibilityNodeInfo} class that are marshalled. */
- private static final int NUM_MARSHALLED_PROPERTIES = 31;
+ private static final int NUM_MARSHALLED_PROPERTIES = 32;
/** The number of properties that are purposely not marshalled */
private static final int NUM_NONMARSHALLED_PROPERTIES = 1;
@@ -204,6 +204,7 @@
info.setContentDescription("content description");
info.setPackageName("foo.bar.baz");
info.setText("text");
+ info.setHintText("hint");
info.setCheckable(true);
info.setChecked(true);
info.setClickable(true);
@@ -250,6 +251,8 @@
assertEquals("packageName has incorrect value", expectedInfo.getPackageName(),
receivedInfo.getPackageName());
assertEquals("text has incorrect value", expectedInfo.getText(), receivedInfo.getText());
+ assertEquals("Hint text has incorrect value",
+ expectedInfo.getHintText(), receivedInfo.getHintText());
assertSame("checkable has incorrect value", expectedInfo.isCheckable(),
receivedInfo.isCheckable());
assertSame("checked has incorrect value", expectedInfo.isChecked(),
@@ -310,6 +313,7 @@
assertNull("contentDescription not properly recycled", info.getContentDescription());
assertNull("packageName not properly recycled", info.getPackageName());
assertNull("text not properly recycled", info.getText());
+ assertNull("Hint text not properly recycled", info.getHintText());
assertFalse("checkable not properly recycled", info.isCheckable());
assertFalse("checked not properly recycled", info.isChecked());
assertFalse("clickable not properly recycled", info.isClickable());
diff --git a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index 9697ce2..454a2ab 100644
--- a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -797,4 +797,29 @@
}
}
+ public void testSetBackupServiceEnabled_failIfNotDeviceOwner() {
+ if (!mDeviceAdmin) {
+ Log.w(TAG, "Skipping testSetBackupServiceEnabled");
+ return;
+ }
+ try {
+ mDevicePolicyManager.setBackupServiceEnabled(mComponent, false);
+ fail("did not throw expected SecurityException");
+ } catch (SecurityException e) {
+ assertDeviceOwnerMessage(e.getMessage());
+ }
+ }
+
+ public void testIsBackupServiceEnabled_failIfNotDeviceOwner() {
+ if (!mDeviceAdmin) {
+ Log.w(TAG, "Skipping testIsBackupServiceEnabled");
+ return;
+ }
+ try {
+ mDevicePolicyManager.isBackupServiceEnabled(mComponent);
+ fail("did not throw expected SecurityException");
+ } catch (SecurityException e) {
+ assertDeviceOwnerMessage(e.getMessage());
+ }
+ }
}
diff --git a/tests/app/app/Android.mk b/tests/app/app/Android.mk
index 8463a56..2e4bdf0 100644
--- a/tests/app/app/Android.mk
+++ b/tests/app/app/Android.mk
@@ -25,7 +25,12 @@
LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common voip-common org.apache.http.legacy
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner ctstestserver mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ compatibility-device-util \
+ ctstestrunner \
+ ctstestserver \
+ mockito-target-minus-junit4 \
+ legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
src/android/app/stubs/ISecondary.aidl
diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index a03edf4..086aff9 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -88,8 +88,14 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.dir/person" />
</intent-filter>
+ <intent-filter>
+ <action android:name="android.app.stubs.activity.INSTRUMENTATION_TEST"/>
+ </intent-filter>
</activity>
+ <activity android:name="android.app.stubs.ActivityMonitorTestActivity"
+ android:label="ActivityMonitorTestActivity" />
+
<activity android:name="android.app.stubs.AliasActivityStub">
<meta-data android:name="android.app.alias"
android:resource="@xml/alias" />
diff --git a/tests/app/app/src/android/app/stubs/ActivityMonitorTestActivity.java b/tests/app/app/src/android/app/stubs/ActivityMonitorTestActivity.java
new file mode 100644
index 0000000..a850b16
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/ActivityMonitorTestActivity.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 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.app.stubs;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class ActivityMonitorTestActivity extends Activity {
+ private OnActivityResultListener mListener;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mListener != null) {
+ mListener.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ public void startInstrumentationTestActivity(boolean startExplicit) {
+ Intent intent;
+ if (startExplicit) {
+ intent = new Intent(this, InstrumentationTestActivity.class);
+ } else {
+ intent = new Intent(InstrumentationTestActivity.START_INTENT);
+ }
+ startActivityForResult(intent, 42);
+ }
+
+ public void setOnActivityResultListener(OnActivityResultListener listener) {
+ mListener = listener;
+ }
+
+ public interface OnActivityResultListener {
+ void onActivityResult(int requestCode, int resultCode, Intent data);
+ }
+}
\ No newline at end of file
diff --git a/tests/app/app/src/android/app/stubs/InstrumentationTestActivity.java b/tests/app/app/src/android/app/stubs/InstrumentationTestActivity.java
index 495bae2..82f7b71 100644
--- a/tests/app/app/src/android/app/stubs/InstrumentationTestActivity.java
+++ b/tests/app/app/src/android/app/stubs/InstrumentationTestActivity.java
@@ -33,6 +33,8 @@
public class InstrumentationTestActivity extends Activity {
+ public static final String START_INTENT = "android.app.stubs.activity.INSTRUMENTATION_TEST";
+
private boolean mOnCreateCalled;
private boolean mOnDestroyCalled ;
private boolean mOnNewIntentCalled;
diff --git a/tests/app/src/android/app/cts/ActivityManagerTest.java b/tests/app/src/android/app/cts/ActivityManagerTest.java
index 8c04dad..07dc354 100644
--- a/tests/app/src/android/app/cts/ActivityManagerTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerTest.java
@@ -452,6 +452,7 @@
static final class ServiceConnectionHandler implements ServiceConnection {
final Context mContext;
final Intent mIntent;
+ boolean mMonitoring;
boolean mBound;
IBinder mService;
@@ -470,25 +471,21 @@
mIntent = intent;
}
- public void bind(long timeout) {
+ public void startMonitoring() {
+ if (mMonitoring) {
+ throw new IllegalStateException("Already monitoring");
+ }
+ if (!mContext.bindService(mIntent, this, Context.BIND_WAIVE_PRIORITY)) {
+ throw new IllegalStateException("Failed to bind " + mIntent);
+ }
+ mMonitoring = true;
+ mService = null;
+ }
+
+ public void waitForConnect(long timeout) {
+ final long endTime = SystemClock.uptimeMillis() + timeout;
+
synchronized (this) {
- if (mBound) {
- throw new IllegalStateException("Already bound");
- }
- // Here's the trick: the first binding allows us to to see the service come
- // up and go down but doesn't actually cause it to run or impact process management.
- // The second binding actually brings it up.
- if (!mContext.bindService(mIntent, this, Context.BIND_WAIVE_PRIORITY)) {
- throw new IllegalStateException("Failed to bind " + mIntent);
- }
- if (!mContext.bindService(mIntent, mMainBinding, Context.BIND_AUTO_CREATE)) {
- throw new IllegalStateException("Failed to bind " + mIntent);
- }
- mBound = true;
- mService = null;
-
- final long endTime = SystemClock.uptimeMillis() + timeout;
-
while (mService == null) {
final long now = SystemClock.uptimeMillis();
if (now >= endTime) {
@@ -502,6 +499,48 @@
}
}
+ public void waitForDisconnect(long timeout) {
+ final long endTime = SystemClock.uptimeMillis() + timeout;
+
+ synchronized (this) {
+ while (mService != null) {
+ final long now = SystemClock.uptimeMillis();
+ if (now >= endTime) {
+ throw new IllegalStateException("Timed out unbinding from " + mIntent);
+ }
+ try {
+ wait(endTime - now);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+ public void stopMonitoring() {
+ if (!mMonitoring) {
+ throw new IllegalStateException("Not monitoring");
+ }
+ mContext.unbindService(this);
+ mMonitoring = false;
+ }
+
+ public void bind(long timeout) {
+ synchronized (this) {
+ if (mBound) {
+ throw new IllegalStateException("Already bound");
+ }
+ // Here's the trick: the first binding allows us to to see the service come
+ // up and go down but doesn't actually cause it to run or impact process management.
+ // The second binding actually brings it up.
+ startMonitoring();
+ if (!mContext.bindService(mIntent, mMainBinding, Context.BIND_AUTO_CREATE)) {
+ throw new IllegalStateException("Failed to bind " + mIntent);
+ }
+ mBound = true;
+ waitForConnect(timeout);
+ }
+ }
+
public void unbind(long timeout) {
synchronized (this) {
if (!mBound) {
@@ -513,20 +552,9 @@
mBound = false;
try {
- final long endTime = SystemClock.uptimeMillis() + timeout;
-
- while (mService != null) {
- final long now = SystemClock.uptimeMillis();
- if (now >= endTime) {
- throw new IllegalStateException("Timed out unbinding from " + mIntent);
- }
- try {
- wait(endTime - now);
- } catch (InterruptedException e) {
- }
- }
+ waitForDisconnect(timeout);
} finally {
- mContext.unbindService(this);
+ stopMonitoring();
}
}
}
@@ -642,6 +670,127 @@
am.removeOnUidImportanceListener(uidGoneListener);
}
+ public void testBackgroundCheckService() throws Exception {
+ Intent serviceIntent = new Intent();
+ Parcel data = Parcel.obtain();
+ serviceIntent.setClassName(SIMPLE_PACKAGE_NAME,
+ SIMPLE_PACKAGE_NAME + SIMPLE_SERVICE);
+ ServiceConnectionHandler conn = new ServiceConnectionHandler(mContext, serviceIntent);
+
+ ActivityManager am = mContext.getSystemService(ActivityManager.class);
+
+ String cmd = "pm grant " + STUB_PACKAGE_NAME + " "
+ + Manifest.permission.PACKAGE_USAGE_STATS;
+ String result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ /*
+ Log.d("XXXX", "Invoke: " + cmd);
+ Log.d("XXXX", "Result: " + result);
+ Log.d("XXXX", SystemUtil.runShellCommand(getInstrumentation(), "dumpsys package "
+ + STUB_PACKAGE_NAME));
+ */
+
+ ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
+ SIMPLE_PACKAGE_NAME, 0);
+
+ UidImportanceListener uidForegroundListener = new UidImportanceListener(appInfo.uid);
+ am.addOnUidImportanceListener(uidForegroundListener,
+ RunningAppProcessInfo.IMPORTANCE_SERVICE);
+ UidImportanceListener uidGoneListener = new UidImportanceListener(appInfo.uid);
+ am.addOnUidImportanceListener(uidGoneListener,
+ RunningAppProcessInfo.IMPORTANCE_EMPTY);
+
+ // First kill the process to start out in a stable state.
+ conn.bind(WAIT_TIME);
+ try {
+ conn.mService.transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0);
+ } catch (RemoteException e) {
+ }
+ conn.unbind(WAIT_TIME);
+
+ //cmd = "am kill " + STUB_PACKAGE_NAME;
+ //result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+
+ // Wait for uid's process to go away.
+ uidGoneListener.waitForValue(RunningAppProcessInfo.IMPORTANCE_GONE,
+ RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_TIME);
+ assertEquals(RunningAppProcessInfo.IMPORTANCE_GONE,
+ am.getPackageImportance(SIMPLE_PACKAGE_NAME));
+
+ cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND deny";
+ result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+
+ // We will use this to monitor when the service is running.
+ conn.startMonitoring();
+
+ try {
+ // Try starting the service. Should fail!
+ boolean failed = false;
+ try {
+ mContext.startService(serviceIntent);
+ } catch (IllegalStateException e) {
+ failed = true;
+ }
+ if (!failed) {
+ fail("Service was allowed to start while in the background");
+ }
+
+ // Put app on temporary whitelist to see if this allows the service start.
+ cmd = "cmd deviceidle tempwhitelist -d 2000 " + SIMPLE_PACKAGE_NAME;
+ result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+
+ // Try starting the service now that the app is whitelisted... should work!
+ mContext.startService(serviceIntent);
+ conn.waitForConnect(WAIT_TIME);
+
+ // Good, now stop the service and give enough time to get off the temp whitelist.
+ mContext.stopService(serviceIntent);
+ conn.waitForDisconnect(WAIT_TIME);
+ Thread.sleep(3000);
+
+ // We don't want to wait for the uid to actually go idle, we can force it now.
+ cmd = "am make-uid-idle " + SIMPLE_PACKAGE_NAME;
+ result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+
+ // Now that we should be off the temp whitelist, make sure we again can't start.
+ failed = false;
+ try {
+ mContext.startService(serviceIntent);
+ } catch (IllegalStateException e) {
+ failed = true;
+ }
+ if (!failed) {
+ fail("Service was allowed to start while in the background");
+ }
+
+ // Work around bug in the platform.
+ conn.stopMonitoring();
+ conn.startMonitoring();
+
+ // Now put app on whitelist, should allow service to run.
+ cmd = "cmd deviceidle whitelist +" + SIMPLE_PACKAGE_NAME;
+ result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+
+ // Try starting the service now that the app is whitelisted... should work!
+ mContext.startService(serviceIntent);
+ conn.waitForConnect(WAIT_TIME);
+
+ // Okay, bring down the service.
+ mContext.stopService(serviceIntent);
+ conn.waitForDisconnect(WAIT_TIME);
+
+ } finally {
+ conn.stopMonitoring();
+
+ cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND normal";
+ result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+
+ am.removeOnUidImportanceListener(uidGoneListener);
+ am.removeOnUidImportanceListener(uidForegroundListener);
+
+ data.recycle();
+ }
+ }
+
/**
* Verify that the TimeTrackingAPI works properly when starting and ending an activity.
*/
diff --git a/tests/app/src/android/app/cts/Instrumentation_ActivityMonitorTest.java b/tests/app/src/android/app/cts/Instrumentation_ActivityMonitorTest.java
index 719f74b..8ffb136 100644
--- a/tests/app/src/android/app/cts/Instrumentation_ActivityMonitorTest.java
+++ b/tests/app/src/android/app/cts/Instrumentation_ActivityMonitorTest.java
@@ -20,15 +20,22 @@
import android.app.Instrumentation;
import android.app.Instrumentation.ActivityMonitor;
import android.app.Instrumentation.ActivityResult;
+import android.app.stubs.ActivityMonitorTestActivity;
import android.app.stubs.InstrumentationTestActivity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
public class Instrumentation_ActivityMonitorTest extends InstrumentationTestCase {
+ private static final String TAG = "ActivityMonitorTest";
- private static final long WAIT_TIMEOUT = 100;
+ private static final long TIMEOUT_FOR_ACTIVITY_LAUNCH_MS = 5000; // 5 sec
+ private static final long CHECK_INTERVAL_FOR_ACTIVITY_LAUNCH_MS = 100; // 0.1 sec
/**
* check points:
@@ -46,10 +53,9 @@
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
Activity lastActivity = am.getLastActivity();
- final long TIMEOUT_MSEC = 5000;
- long timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
+ long timeout = System.currentTimeMillis() + TIMEOUT_FOR_ACTIVITY_LAUNCH_MS;
while (lastActivity == null && System.currentTimeMillis() < timeout) {
- Thread.sleep(WAIT_TIMEOUT);
+ Thread.sleep(CHECK_INTERVAL_FOR_ACTIVITY_LAUNCH_MS);
lastActivity = am.getLastActivity();
}
Activity activity = am.waitForActivity();
@@ -59,11 +65,11 @@
activity.finish();
instrumentation.waitForIdleSync();
context.startActivity(intent);
- timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
+ timeout = System.currentTimeMillis() + TIMEOUT_FOR_ACTIVITY_LAUNCH_MS;
activity = null;
while (activity == null && System.currentTimeMillis() < timeout) {
- Thread.sleep(WAIT_TIMEOUT);
- activity = am.waitForActivityWithTimeout(WAIT_TIMEOUT);
+ Thread.sleep(CHECK_INTERVAL_FOR_ACTIVITY_LAUNCH_MS);
+ activity = am.waitForActivityWithTimeout(CHECK_INTERVAL_FOR_ACTIVITY_LAUNCH_MS);
}
assertNotNull(activity);
activity.finish();
@@ -77,4 +83,232 @@
assertSame(which, am.getFilter());
assertFalse(am.isBlocking());
}
+
+ /**
+ * Verifies that
+ * - when ActivityMonitor.onMatchIntent returs non-null, then there is monitor hit.
+ * - when ActivityMonitor.onMatchIntent returns null, then the activity start is not blocked.
+ */
+ public void testActivityMonitor_onMatchIntent() throws Exception {
+ final ActivityResult result = new ActivityResult(Activity.RESULT_OK, new Intent());
+ final Instrumentation instrumentation = getInstrumentation();
+ final Context context = instrumentation.getTargetContext();
+ final Intent intent = new Intent(context, InstrumentationTestActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ // Verify when ActivityMonitor.onMatchIntent returns non-null, then there is a monitor hit.
+ final CustomActivityMonitor cam1 = new CustomActivityMonitor(result);
+ instrumentation.addMonitor(cam1);
+ context.startActivity(intent);
+ final Activity activity1 = cam1.waitForActivityWithTimeout(
+ CHECK_INTERVAL_FOR_ACTIVITY_LAUNCH_MS * 2);
+ try {
+ assertNull("Activity should not have been started", activity1);
+ assertEquals("There should be 1 monitor hit", 1, cam1.getHits());
+ } finally {
+ instrumentation.removeMonitor(cam1);
+ }
+
+ // Verify when ActivityMonitor.onMatchIntent returns null, then activity start is not
+ // blocked and there is no monitor hit.
+ final CustomActivityMonitor cam2 = new CustomActivityMonitor(null);
+ instrumentation.addMonitor(cam2);
+ Activity activity2 = instrumentation.startActivitySync(intent);
+ try {
+ assertNotNull("Activity should not be null", activity2);
+ assertTrue("Activity returned should be of instance InstrumentationTestActivity",
+ activity2 instanceof InstrumentationTestActivity);
+ assertTrue("InstrumentationTestActivity should have been started",
+ ((InstrumentationTestActivity) activity2).isOnCreateCalled());
+ assertEquals("There should be no monitor hits", 0, cam2.getHits());
+ } finally {
+ activity2.finish();
+ instrumentation.removeMonitor(cam2);
+ }
+ }
+
+ /**
+ * Verifies that when ActivityMonitor.onMatchIntent returns non-null, activity start is blocked.
+ */
+ public void testActivityMonitor_onMatchIntentBlocks() throws Exception {
+ final Instrumentation instrumentation = getInstrumentation();
+ final Context context = instrumentation.getTargetContext();
+
+ // Start ActivityMonitorTestActivity
+ final Intent intent = new Intent(context, ActivityMonitorTestActivity.class);
+ ActivityMonitorTestActivity amTestActivity =
+ (ActivityMonitorTestActivity) instrumentation.startActivitySync(intent);
+
+ // Initialize and set activity monitor.
+ final int expectedResultCode = 1111;
+ final String expectedAction = "matched_using_onMatchIntent";
+ final CustomActivityMonitor cam = new CustomActivityMonitor(
+ new ActivityResult(expectedResultCode, new Intent(expectedAction)));
+ instrumentation.addMonitor(cam);
+
+ // Start InstrumentationTestActivity from ActivityMonitorTestActivity and verify
+ // it is intercepted using onMatchIntent as expected.
+ try {
+ final CountDownLatch latch = new CountDownLatch(1);
+ amTestActivity.setOnActivityResultListener(
+ new ActivityMonitorTestActivity.OnActivityResultListener() {
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ assertEquals("Result code is not same as expected",
+ expectedResultCode, resultCode);
+ assertNotNull("Data from activity result is null", data);
+ assertEquals("Data action is not same as expected",
+ expectedAction, data.getAction());
+ latch.countDown();
+ }
+ });
+ amTestActivity.startInstrumentationTestActivity(false);
+ if (!latch.await(TIMEOUT_FOR_ACTIVITY_LAUNCH_MS, TimeUnit.MILLISECONDS)) {
+ fail("Timed out waiting for the activity result from "
+ + ActivityMonitorTestActivity.class.getName());
+ }
+ assertEquals("There should be 1 monitor hit", 1, cam.getHits());
+ } finally {
+ amTestActivity.finish();
+ instrumentation.removeMonitor(cam);
+ }
+ }
+
+ /**
+ * Verifies that when the activity monitor is created using by passing IntentFilter,
+ * then onMatchIntent return value is ignored.
+ */
+ public void testActivityMonitor_onMatchIntentAndIntentFilter() throws Exception {
+ final Instrumentation instrumentation = getInstrumentation();
+ final Context context = instrumentation.getTargetContext();
+
+ // Start ActivityMonitorTestActivity
+ final Intent intent = new Intent(context, ActivityMonitorTestActivity.class);
+ ActivityMonitorTestActivity amTestActivity =
+ (ActivityMonitorTestActivity) instrumentation.startActivitySync(intent);
+
+ // Initialize and set activity monitor.
+ final int expectedResultCode = 1122;
+ final String expectedAction = "matched_using_intent_filter";
+ final CustomActivityMonitor cam = new CustomActivityMonitor(
+ new IntentFilter(InstrumentationTestActivity.START_INTENT),
+ new ActivityResult(expectedResultCode, new Intent(expectedAction)),
+ true);
+ cam.setResultToReturn(new ActivityResult(1111, new Intent("matched_using_onMatchIntent")));
+ instrumentation.addMonitor(cam);
+
+ // Start explicit InstrumentationTestActivity from ActivityMonitorTestActivity and verify
+ // it is intercepted using the intentFilter as expected.
+ try {
+ final CountDownLatch latch = new CountDownLatch(1);
+ amTestActivity.setOnActivityResultListener(
+ new ActivityMonitorTestActivity.OnActivityResultListener() {
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ assertEquals("Result code is not same as expected",
+ expectedResultCode, resultCode);
+ assertNotNull("Data from activity result is null", data);
+ assertEquals("Data action is not same as expected",
+ expectedAction, data.getAction());
+ latch.countDown();
+ }
+ });
+ amTestActivity.startInstrumentationTestActivity(false);
+ if (!latch.await(TIMEOUT_FOR_ACTIVITY_LAUNCH_MS, TimeUnit.MILLISECONDS)) {
+ fail("Timed out waiting for the activity result from "
+ + ActivityMonitorTestActivity.class.getName());
+ }
+ assertEquals("There should be 1 monitor hit", 1, cam.getHits());
+ } finally {
+ amTestActivity.finish();
+ instrumentation.removeMonitor(cam);
+ }
+ }
+
+ /**
+ * Verifies that when the activity monitor is created using by passing activity class,
+ * then onMatchIntent return value is ignored.
+ */
+ public void testActivityMonitor_onMatchIntentAndActivityClass() throws Exception {
+ final Instrumentation instrumentation = getInstrumentation();
+ final Context context = instrumentation.getTargetContext();
+
+ // Start ActivityMonitorTestActivity
+ final Intent intent = new Intent(context, ActivityMonitorTestActivity.class);
+ ActivityMonitorTestActivity amTestActivity =
+ (ActivityMonitorTestActivity) instrumentation.startActivitySync(intent);
+
+ // Initialize and set activity monitor.
+ final int expectedResultCode = 2244;
+ final String expectedAction = "matched_using_activity_class";
+ final CustomActivityMonitor cam = new CustomActivityMonitor(
+ InstrumentationTestActivity.class.getName(),
+ new ActivityResult(expectedResultCode, new Intent(expectedAction)),
+ true);
+ cam.setResultToReturn(new ActivityResult(2222, new Intent("matched_using_onMatchIntent")));
+ instrumentation.addMonitor(cam);
+
+ // Start implicit InstrumentationTestActivity from ActivityMonitorTestActivity and verify
+ // it is intercepted using the activity class as expected.
+ try {
+ final CountDownLatch latch = new CountDownLatch(1);
+ amTestActivity.setOnActivityResultListener(
+ new ActivityMonitorTestActivity.OnActivityResultListener() {
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ assertEquals("Result code is not same as expected",
+ expectedResultCode, resultCode);
+ assertNotNull("Data from activity result is null", data);
+ assertEquals("Data action is not same as expected",
+ expectedAction, data.getAction());
+ latch.countDown();
+ }
+ });
+ amTestActivity.startInstrumentationTestActivity(true);
+ if (!latch.await(TIMEOUT_FOR_ACTIVITY_LAUNCH_MS, TimeUnit.MILLISECONDS)) {
+ fail("Timed out waiting for the activity result from "
+ + ActivityMonitorTestActivity.class.getName());
+ }
+ assertEquals("There should be 1 monitor hit", 1, cam.getHits());
+ } finally {
+ amTestActivity.finish();
+ instrumentation.removeMonitor(cam);
+ }
+ }
+
+ private class CustomActivityMonitor extends ActivityMonitor {
+ private ActivityResult mResultToReturn;
+
+ public CustomActivityMonitor(ActivityResult resultToReturn) {
+ super();
+ mResultToReturn = resultToReturn;
+ }
+
+ public CustomActivityMonitor(IntentFilter intentFilter, ActivityResult result,
+ boolean blocked) {
+ super(intentFilter, result, blocked);
+ }
+
+ public CustomActivityMonitor(String activityClass, ActivityResult result,
+ boolean blocked) {
+ super(activityClass, result, blocked);
+ }
+
+ public void setResultToReturn(ActivityResult resultToReturn) {
+ mResultToReturn = resultToReturn;
+ }
+
+ @Override
+ public ActivityResult onMatchIntent(Intent intent) {
+ final boolean implicitInstrumentationTestActivity = intent.getAction() != null &&
+ InstrumentationTestActivity.START_INTENT.equals(intent.getAction());
+ final boolean explicitInstrumentationTestActivity = intent.getComponent() != null &&
+ InstrumentationTestActivity.class.getName().equals(
+ intent.getComponent().getClassName());
+ if (implicitInstrumentationTestActivity || explicitInstrumentationTestActivity) {
+ return mResultToReturn;
+ }
+ return null;
+ }
+ }
}
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index f45e0c7..232abb8 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -28,7 +28,11 @@
import android.service.notification.StatusBarNotification;
import android.test.AndroidTestCase;
import android.util.Log;
-import java.util.concurrent.CountDownLatch;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
import java.util.Arrays;
@@ -37,10 +41,13 @@
final boolean DEBUG = false;
private NotificationManager mNotificationManager;
+ private String mId;
@Override
protected void setUp() throws Exception {
super.setUp();
+ // This will leave a set of channels on the device with each test run.
+ mId = UUID.randomUUID().toString();
mNotificationManager = (NotificationManager) mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
// clear the deck so that our getActiveNotifications results are predictable
@@ -55,7 +62,7 @@
public void testCreateChannel() throws InterruptedException {
final NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.enableVibration(true);
channel.setVibrationPattern(new long[] {5, 8, 2, 1});
channel.setSound(new Uri.Builder().scheme("test").build());
@@ -77,14 +84,14 @@
public void testCreateSameChannelDoesNotUpdate() throws InterruptedException {
final NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
try {
mNotificationManager.createNotificationChannel(channel);
final NotificationChannel channelDupe =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_HIGH);
+ new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channelDupe);
final NotificationChannel createdChannel =
- mNotificationManager.getNotificationChannel("id");
+ mNotificationManager.getNotificationChannel(mId);
compareChannels(channel, createdChannel);
} finally {
mNotificationManager.deleteNotificationChannel(channel.getId());
@@ -93,11 +100,11 @@
public void testCreateChannelAlreadyExistsNoOp() {
NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
try {
mNotificationManager.createNotificationChannel(channel);
NotificationChannel channelDupe =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_HIGH);
+ new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channelDupe);
compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId()));
} finally {
@@ -107,7 +114,7 @@
public void testCreateChannelInvalidImportance() {
NotificationChannel channel =
- new NotificationChannel("id2", "name", NotificationManager.IMPORTANCE_UNSPECIFIED);
+ new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_UNSPECIFIED);
try {
mNotificationManager.createNotificationChannel(channel);
} catch (IllegalArgumentException e) {
@@ -117,7 +124,7 @@
public void testDeleteChannel() {
NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_LOW);
+ new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_LOW);
mNotificationManager.createNotificationChannel(channel);
compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId()));
mNotificationManager.deleteNotificationChannel(channel.getId());
@@ -135,13 +142,16 @@
public void testGetChannel() {
NotificationChannel channel1 =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+ new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
NotificationChannel channel2 =
- new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
+ new NotificationChannel(
+ UUID.randomUUID().toString(), "name2", NotificationManager.IMPORTANCE_HIGH);
NotificationChannel channel3 =
- new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_LOW);
+ new NotificationChannel(
+ UUID.randomUUID().toString(), "name3", NotificationManager.IMPORTANCE_LOW);
NotificationChannel channel4 =
- new NotificationChannel("id4", "name4", NotificationManager.IMPORTANCE_MIN);
+ new NotificationChannel(
+ UUID.randomUUID().toString(), "name4", NotificationManager.IMPORTANCE_MIN);
try {
mNotificationManager.createNotificationChannel(channel1);
mNotificationManager.createNotificationChannel(channel2);
@@ -164,6 +174,68 @@
}
}
+ public void testGetChannels() {
+ NotificationChannel channel1 =
+ new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
+ NotificationChannel channel2 =
+ new NotificationChannel(
+ UUID.randomUUID().toString(), "name2", NotificationManager.IMPORTANCE_HIGH);
+ NotificationChannel channel3 =
+ new NotificationChannel(
+ UUID.randomUUID().toString(), "name3", NotificationManager.IMPORTANCE_LOW);
+ NotificationChannel channel4 =
+ new NotificationChannel(
+ UUID.randomUUID().toString(), "name4", NotificationManager.IMPORTANCE_MIN);
+
+ Map<String, NotificationChannel> channelMap = new HashMap<>();
+ channelMap.put(channel1.getId(), channel1);
+ channelMap.put(channel2.getId(), channel2);
+ channelMap.put(channel3.getId(), channel3);
+ channelMap.put(channel4.getId(), channel4);
+ try {
+ mNotificationManager.createNotificationChannel(channel1);
+ mNotificationManager.createNotificationChannel(channel2);
+ mNotificationManager.createNotificationChannel(channel3);
+ mNotificationManager.createNotificationChannel(channel4);
+
+ mNotificationManager.deleteNotificationChannel(channel3.getId());
+
+ List<NotificationChannel> channels = mNotificationManager.getNotificationChannels();
+ for (NotificationChannel nc : channels) {
+ if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
+ continue;
+ }
+ assertFalse(channel3.getId().equals(nc.getId()));
+ compareChannels(channelMap.get(nc.getId()), nc);
+ }
+ } finally {
+ mNotificationManager.deleteNotificationChannel(channel1.getId());
+ mNotificationManager.deleteNotificationChannel(channel2.getId());
+ mNotificationManager.deleteNotificationChannel(channel3.getId());
+ mNotificationManager.deleteNotificationChannel(channel4.getId());
+ }
+ }
+
+ public void testRecreateDeletedChannel() {
+ NotificationChannel channel =
+ new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setShowBadge(true);
+ NotificationChannel newChannel = new NotificationChannel(
+ channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
+ try {
+ mNotificationManager.createNotificationChannel(channel);
+ mNotificationManager.deleteNotificationChannel(channel.getId());
+
+ mNotificationManager.createNotificationChannel(newChannel);
+
+ compareChannels(channel,
+ mNotificationManager.getNotificationChannel(newChannel.getId()));
+
+ } finally {
+ mNotificationManager.deleteNotificationChannel(channel.getId());
+ }
+ }
+
public void testNotify() {
mNotificationManager.cancelAll();
diff --git a/tests/autofillservice/Android.mk b/tests/autofillservice/Android.mk
new file mode 100644
index 0000000..5ccc277
--- /dev/null
+++ b/tests/autofillservice/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+# TODO(b/30946317): remove guava (currently needed by truth-prebuilt)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ compatibility-device-util \
+ ctstestrunner \
+ guava \
+ truth-prebuilt \
+ ub-uiautomator
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_PACKAGE_NAME := CtsAutoFillServiceTestCases
+
+LOCAL_SDK_VERSION := test_current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
new file mode 100644
index 0000000..bd283c1
--- /dev/null
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 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="android.autofillservice.cts" >
+
+ <application>
+ <activity
+ android:name=".LoginActivity"
+ android:label="LoginActivity" />
+ <service
+ android:name=".InstrumentedAutoFillService"
+ android:label="InstrumentedAutoFillService"
+ android:permission="android.permission.BIND_AUTO_FILL" >
+ <intent-filter>
+ <action android:name="android.service.autofill.AutoFillService" />
+ </intent-filter>
+ </service>
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:label="CTS tests for the AutoFill Framework APIs."
+ android:targetPackage="android.autofillservice.cts" >
+ </instrumentation>
+
+</manifest>
\ No newline at end of file
diff --git a/tests/autofillservice/AndroidTest.xml b/tests/autofillservice/AndroidTest.xml
new file mode 100644
index 0000000..885537f9
--- /dev/null
+++ b/tests/autofillservice/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for AutoFill Framework CTS tests.">
+
+ <target_preparer
+ class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsAutoFillServiceTestCases.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.autofillservice.cts" />
+ </test>
+
+</configuration>
diff --git a/tests/autofillservice/res/layout/login_activity.xml b/tests/autofillservice/res/layout/login_activity.xml
new file mode 100644
index 0000000..6a8957da
--- /dev/null
+++ b/tests/autofillservice/res/layout/login_activity.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Username" />
+
+ <EditText
+ android:id="@+id/username"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Password" />
+
+ <EditText
+ android:id="@+id/password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <Button
+ android:id="@+id/clear"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Clear" />
+
+ <Button
+ android:id="@+id/login"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Login" />
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/output"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
new file mode 100644
index 0000000..0b6f235
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 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.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.UI_TIMEOUT_SEC;
+import static android.autofillservice.cts.Helper.runShellCommand;
+import static android.provider.Settings.Secure.AUTO_FILL_SERVICE;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+
+/**
+ * Base class for all other tests.
+ */
+@RunWith(AndroidJUnit4.class)
+abstract class AutoFillServiceTestCase {
+
+ private static final String SERVICE_NAME =
+ InstrumentedAutoFillService.class.getPackage().getName()
+ + "/." + InstrumentedAutoFillService.class.getSimpleName();
+
+ protected static UiBot sUiBot;
+
+ @BeforeClass
+ public static void setUiBot() {
+ sUiBot = new UiBot(InstrumentationRegistry.getInstrumentation(), UI_TIMEOUT_SEC);
+ }
+
+ @AfterClass
+ public static void disableService() {
+ runShellCommand("settings delete secure %s", AUTO_FILL_SERVICE);
+ assertServiceDisabled();
+ }
+
+ /**
+ * Enables the {@link InstrumentedAutoFillService} for auto-fill for the default user.
+ */
+ protected void enableService() {
+ runShellCommand(
+ "settings put secure %s %s default", AUTO_FILL_SERVICE, SERVICE_NAME);
+ assertServiceEnabled();
+ }
+
+ /**
+ * Asserts that the {@link InstrumentedAutoFillService} is enabled for the default user.
+ */
+ protected static void assertServiceEnabled() {
+ assertServiceStatus(true);
+ }
+
+ /**
+ * Asserts that the {@link InstrumentedAutoFillService} is disabled for the default user.
+ */
+ protected static void assertServiceDisabled() {
+ assertServiceStatus(false);
+ }
+
+ private static void assertServiceStatus(boolean enabled) {
+ final String actual = runShellCommand("settings get secure %s", AUTO_FILL_SERVICE);
+ final String expected = enabled ? SERVICE_NAME : "null";
+ assertWithMessage("Invalid value for secure setting %s", AUTO_FILL_SERVICE)
+ .that(actual).isEqualTo(expected);
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
new file mode 100644
index 0000000..14ab6a3
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 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.autofillservice.cts;
+
+import android.app.assist.AssistStructure;
+import android.view.autofill.AutoFillValue;
+import android.view.autofill.Dataset;
+import android.view.autofill.FillResponse;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper class used to produce a {@link FillResponse} based on expected fields that should be
+ * present in the {@link AssistStructure}.
+ *
+ * <p>Typical usage:
+ *
+ * <pre class="prettyprint">
+ * InstrumentedAutoFillService.setFillResponse(new CannedFillResponse.Builder()
+ * .addDataset(new CannedDataset.Builder("dataset_name")
+ * .setField("resource_id1", AutoFillValue.forText("value1"))
+ * .setField("resource_id2", AutoFillValue.forText("value2"))
+ * .build())
+ * .build());
+ * </pre class="prettyprint">
+ */
+final class CannedFillResponse {
+
+ final List<CannedDataset> datasets;
+
+ private CannedFillResponse(Builder builder) {
+ datasets = builder.mDatasets;
+ }
+
+ @Override
+ public String toString() {
+ return "CannedFillResponse: [datasets=" + datasets + "]";
+ }
+
+ static class Builder {
+ private final List<CannedDataset> mDatasets = new ArrayList<>();
+
+ public Builder addDataset(CannedDataset dataset) {
+ mDatasets.add(dataset);
+ return this;
+ }
+
+ public CannedFillResponse build() {
+ return new CannedFillResponse(this);
+ }
+ }
+
+ /**
+ * Helper class used to produce a {@link Dataset} based on expected fields that should be
+ * present in the {@link AssistStructure}.
+ *
+ * <p>Typical usage:
+ *
+ * <pre class="prettyprint">
+ * InstrumentedAutoFillService.setFillResponse(new CannedFillResponse.Builder()
+ * .addDataset(new CannedDataset.Builder("dataset_name")
+ * .setField("resource_id1", AutoFillValue.forText("value1"))
+ * .setField("resource_id2", AutoFillValue.forText("value2"))
+ * .build())
+ * .build());
+ * </pre class="prettyprint">
+ */
+ static class CannedDataset {
+
+ final Map<String, AutoFillValue> fields;
+ final String name;
+
+ private CannedDataset(Builder builder) {
+ fields = builder.mFields;
+ name = builder.mName;
+ }
+
+ @Override
+ public String toString() {
+ return "CannedDataset: [name=" + name + ", fields=" + fields + "]";
+ }
+
+ static class Builder {
+ private final Map<String, AutoFillValue> mFields = new HashMap<>();
+ private final String mName;
+
+ public Builder(String name) {
+ mName = name;
+ }
+
+ /**
+ * Sets the canned value of a field based on its {@code resourceId}.
+ */
+ public Builder setField(String resourceId, AutoFillValue value) {
+ mFields.put(resourceId, value);
+ return this;
+ }
+
+ public CannedDataset build() {
+ return new CannedDataset(this);
+ }
+ }
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
new file mode 100644
index 0000000..341a921
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 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.autofillservice.cts;
+
+import android.support.test.InstrumentationRegistry;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+/**
+ * Helper for common funcionalities.
+ */
+final class Helper {
+
+ private static final String TAG = "AutoFillCtsHelper";
+
+ /**
+ * Timeout (in milliseconds) for expected auto-fill requests.
+ */
+ static final long FILL_TIMEOUT_MS = 2000;
+
+ /**
+ * Timeout (in seconds) for UI operations. Typically used by {@link UiBot}.
+ */
+ static final int UI_TIMEOUT_SEC = 2;
+
+ /**
+ * Runs a Shell command, returning a trimmed response.
+ */
+ static String runShellCommand(String template, Object...args) {
+ final String command = String.format(template, args);
+ Log.d(TAG, "runShellCommand(): " + command);
+ try {
+ final String result = SystemUtil
+ .runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
+ return TextUtils.isEmpty(result) ? "" : result.trim();
+ } catch (Exception e) {
+ throw new RuntimeException("Command '" + command + "' failed: ", e);
+ }
+ }
+
+ private Helper() {
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
new file mode 100644
index 0000000..2237002
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.ViewNode;
+import android.app.assist.AssistStructure.WindowNode;
+import android.autofillservice.cts.CannedFillResponse.CannedDataset;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.service.autofill.AutoFillService;
+import android.service.autofill.FillCallback;
+import android.service.autofill.SaveCallback;
+import android.util.Log;
+import android.view.autofill.AutoFillId;
+import android.view.autofill.AutoFillValue;
+import android.view.autofill.Dataset;
+import android.view.autofill.FillResponse;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Implementation of {@link AutoFillService} used in the tests.
+ */
+public class InstrumentedAutoFillService extends AutoFillService {
+
+ private static final String TAG = "InstrumentedAutoFillService";
+
+ private static final AtomicReference<CannedFillResponse> sCannedFillResponse =
+ new AtomicReference<>();
+
+ // TODO(b/33197203, b/33802548): add tests for onConnected() / onDisconnected() and/or remove
+ // overriden methods below that are only logging their calls.
+
+ @Override
+ public void onConnected() {
+ Log.v(TAG, "onConnected()");
+ }
+
+ @Override
+ public void onDisconnected() {
+ Log.v(TAG, "onDisconnected()");
+ }
+
+ @Override
+ public void onFillRequest(AssistStructure structure, Bundle data,
+ CancellationSignal cancellationSignal, FillCallback callback) {
+ final CannedFillResponse cannedResponse = sCannedFillResponse.getAndSet(null);
+ Log.v(TAG, "onFillRequest(): cannedResponse = " + cannedResponse);
+
+ if (cannedResponse == null) {
+ callback.onSuccess(null);
+ return;
+ }
+ final FillResponse.Builder responseBuilder = new FillResponse.Builder();
+ final List<CannedDataset> datasets = cannedResponse.datasets;
+
+ if (datasets.isEmpty()) {
+ callback.onSuccess(responseBuilder.build());
+ return;
+ }
+
+ assertWithMessage("multiple datasets not supported yet").that(datasets).hasSize(1);
+
+ final CannedDataset dataset = datasets.get(0);
+
+ final Map<String, AutoFillValue> fields = dataset.fields;
+ if (fields.isEmpty()) {
+ callback.onSuccess(responseBuilder.build());
+ return;
+ }
+
+ final Dataset.Builder datasetBuilder = new Dataset.Builder(dataset.name);
+
+ Log.v(TAG, "Parsing request for activity " + structure.getActivityComponent());
+ final int nodes = structure.getWindowNodeCount();
+ for (int i = 0; i < nodes; i++) {
+ final WindowNode node = structure.getWindowNodeAt(i);
+ final ViewNode view = node.getRootViewNode();
+ fill(datasetBuilder, fields, view);
+ }
+
+ final FillResponse fillResponse =
+ responseBuilder.addDataset(datasetBuilder.build()).build();
+ Log.v(TAG, "onFillRequest(): fillResponse = " + fillResponse);
+ callback.onSuccess(fillResponse);
+ }
+
+ @Override
+ public void onSaveRequest(AssistStructure structure, Bundle data,
+ CancellationSignal cancellationSignal, SaveCallback callback) {
+ Log.v(TAG, "onSaveRequest()");
+ }
+
+ /**
+ * Sets the response returned by the service in the next
+ * {@link #onFillRequest(AssistStructure, Bundle, CancellationSignal, FillCallback)} call.
+ */
+ static void setFillResponse(CannedFillResponse response) {
+ final boolean ok = sCannedFillResponse.compareAndSet(null, response);
+ if (!ok) {
+ throw new IllegalStateException("already set: " + sCannedFillResponse.get());
+ }
+ }
+
+ private void fill(Dataset.Builder builder, Map<String, AutoFillValue> fields,
+ ViewNode view) {
+ final String resourceId = view.getIdEntry();
+
+ final AutoFillValue value = fields.get(resourceId);
+ if (value != null) {
+ final AutoFillId id = view.getAutoFillId();
+ Log.d(TAG, "setting '" + resourceId + "' (" + id + ") to " + value);
+ builder.setValue(id, value);
+ }
+
+ final int childrenSize = view.getChildCount();
+ if (childrenSize > 0) {
+ for (int i = 0; i < childrenSize; i++) {
+ fill(builder, fields, view.getChildAt(i));
+ }
+ }
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
new file mode 100644
index 0000000..86a38b7
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2017 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.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.FILL_TIMEOUT_MS;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.Activity;
+import android.autofillservice.cts.CannedFillResponse.CannedDataset;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.autofill.AutoFillValue;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Activity that has the following fields:
+ *
+ * <ul>
+ * <li>Username EditText (id: username, no input-type)
+ * <li>Password EditText (id: "username", input-type textPassword)
+ * <li>Clear Button
+ * <li>Login Button
+ * </ul>
+ */
+public class LoginActivity extends Activity {
+
+ private static final String TAG = "LoginActivity";
+
+ private EditText mUsernameEditText;
+ private EditText mPasswordEditText;
+ private TextView mOutput;
+ private Button mLoginButton;
+ private Button mClearButton;
+ private AutoFillExpectation mAutoFillExpectation;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.login_activity);
+
+ mLoginButton = (Button) findViewById(R.id.login);
+ mClearButton = (Button) findViewById(R.id.clear);
+ mUsernameEditText = (EditText) findViewById(R.id.username);
+ mPasswordEditText = (EditText) findViewById(R.id.password);
+ mOutput = (TextView) findViewById(R.id.output);
+
+ // TODO(b/33197203): remove login / clear button if not used by the tests (currently,
+ // they're only used for debugging)
+ mLoginButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ login();
+ }
+ });
+ mClearButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ resetFields();
+ }
+ });
+ }
+
+ /**
+ * Resets the values of the input fields.
+ */
+ private void resetFields() {
+ mUsernameEditText.setText("");
+ mPasswordEditText.setText("");
+ mOutput.setText("");
+ }
+
+ /**
+ * Emulates a login action.
+ */
+ // TODO(b/33197203): check if username / password matches and either show error message
+ // or redirect to a welcome 'user' page
+ private void login() {
+ final StringBuilder buffer = new StringBuilder();
+ buffer.append("Username:").append(mUsernameEditText.getText()).append(":\n");
+ buffer.append("Password:").append(mPasswordEditText.getText()).append(":\n");
+ final String output = buffer.toString();
+ Log.d(TAG, "login(): " + output);
+ mOutput.setText(output);
+ }
+
+ /**
+ * Sets the expectation for an auto-fill request, so it can be asserted through
+ * {@link #assertAutoFilled()} later.
+ *
+ * <p>It fills the {@code builder} dataset with the proper fields for {@code} username and
+ * {@code password}, so caller can use it to set a {@link CannedFillResponse}.
+ */
+ void expectAutoFill(CannedDataset.Builder builder, String username, String password) {
+ builder
+ .setField("username", AutoFillValue.forText(username))
+ .setField("password", AutoFillValue.forText(password));
+ mAutoFillExpectation = new AutoFillExpectation(username, password);
+ mUsernameEditText
+ .addTextChangedListener(new MyTextWatcher(mAutoFillExpectation.usernameLatch));
+ mPasswordEditText
+ .addTextChangedListener(new MyTextWatcher(mAutoFillExpectation.passwordLatch));
+ }
+
+ /**
+ * Asserts the activity was auto-filled with the values passed to
+ * {@link #expectAutoFill(android.autofillservice.cts.CannedFillResponse.CannedDataset.Builder,
+ * String, String)}.
+ */
+ void assertAutoFilled() throws Exception {
+ assertWithMessage("expectAutoFill() not called").that(mAutoFillExpectation).isNotNull();
+ assertField("username", mUsernameEditText,
+ mAutoFillExpectation.usernameLatch, mAutoFillExpectation.expectedUsername);
+ assertField("password", mPasswordEditText,
+ mAutoFillExpectation.passwordLatch, mAutoFillExpectation.expectedPassword);
+ }
+
+ /**
+ * Asserts the value of an input field, using a latch to make sure it was set.
+ */
+ private void assertField(String name, EditText field, CountDownLatch latch,
+ String expectedValue) throws Exception {
+ final boolean set = latch.await(FILL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertWithMessage("Timeout (%s ms) for auto-fill of field %s", FILL_TIMEOUT_MS, name)
+ .that(set).isTrue();
+ final String actualValue = field.getText().toString();
+ assertWithMessage("Wrong auto-fill value for field %s", name).that(actualValue)
+ .isEqualTo(expectedValue);
+ }
+
+ /**
+ * Holder for the expected auto-fill values.
+ */
+ private final class AutoFillExpectation {
+ private final CountDownLatch usernameLatch = new CountDownLatch(1);
+ private final CountDownLatch passwordLatch = new CountDownLatch(1);
+ private final String expectedUsername;
+ private final String expectedPassword;
+
+ private AutoFillExpectation(String username, String password) {
+ expectedUsername = username;
+ expectedPassword = password;
+ }
+ }
+
+ private class MyTextWatcher implements TextWatcher {
+ private final CountDownLatch latch;
+
+ private MyTextWatcher(CountDownLatch latch) {
+ this.latch = latch;
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ Log.d(TAG, "onTextChanged(): " + s);
+ latch.countDown();
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
new file mode 100644
index 0000000..43ed714
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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.autofillservice.cts;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import android.autofillservice.cts.CannedFillResponse.CannedDataset;
+
+@SmallTest
+public class LoginActivityTest extends AutoFillServiceTestCase {
+
+ @Rule
+ public final ActivityTestRule<LoginActivity> mActivityRule =
+ new ActivityTestRule<LoginActivity>(LoginActivity.class);
+
+ private LoginActivity mLoginActivity;
+
+ @Before
+ public void setActivity() {
+ mLoginActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testAutoFillOneDataset() throws Exception {
+ enableService();
+
+ final CannedDataset.Builder dataset = new CannedDataset.Builder("The Dude");
+ mLoginActivity.expectAutoFill(dataset, "dude", "sweet");
+
+ InstrumentedAutoFillService.setFillResponse(new CannedFillResponse.Builder()
+ .addDataset(dataset.build())
+ .build());
+
+ sUiBot.triggerFillRequest();
+ sUiBot.selectDataset("The Dude");
+
+ mLoginActivity.assertAutoFilled();
+ }
+
+ /*
+ * TODO(b/33197203, b/33802548): test other scenarios
+ *
+ * - no dataset
+ * - multiple datasets
+ * - response-level authentication (custom and fingerprint)
+ * - dataset-level authentication (custom and fingerprint)
+ *
+ * Other assertions:
+ * - illegal state thrown on callback calls
+ * - system server state after calls (for example, no pending callback)
+ */
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
new file mode 100644
index 0000000..9900e19
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.Instrumentation;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiSelector;
+import android.support.test.uiautomator.Until;
+import android.util.Log;
+
+/**
+ * Helper for UI-related needs.
+ */
+final class UiBot {
+
+ private static final String TAG = "AutoFillCtsUiBot";
+
+ private final UiDevice mDevice;
+ private final int mTimeout;
+
+ UiBot(Instrumentation instrumentation, int timeout) {
+ mDevice = UiDevice.getInstance(instrumentation);
+ mTimeout = timeout;
+ }
+
+ /**
+ * Selects an auto-fill dataset whose name should be visible in the UI.
+ */
+ void selectDataset(String name) {
+ // TODO(b/33197203): use id string when using real auto-fill bar
+ Log.v(TAG, "selectDataset(): " + name);
+
+ clickOnNotification(name.toUpperCase());
+ }
+
+ /**
+ * Triggers the auto-fill affordance UI.
+ */
+ void triggerFillRequest() {
+ Log.v(TAG, "triggerFillRequest()");
+
+ // TODO(b/33197203): use id string when using real auto-fill bar
+ clickOnNotification("AutoFill IME Emulation");
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+ // TODO(b/33197203): temporary code using a notification to request auto-fill. //
+ // Will be removed once UX decide the right way to present it to the user. //
+ /////////////////////////////////////////////////////////////////////////////////
+ private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+
+ /**
+ * Clicks on a UI element.
+ *
+ * @param uiObject UI element to be clicked.
+ * @param description Elements's description used on logging statements.
+ */
+ private void click(UiObject uiObject, String description) {
+ try {
+ boolean clicked = uiObject.click();
+ // TODO: assertion below fails sometimes, even though the click succeeded,
+ // (specially when clicking the "Just Once" button), so it's currently just logged.
+ // assertTrue("could not click on object '" + description + "'", clicked);
+
+ Log.v(TAG, "onClick for " + description + ": " + clicked);
+ } catch (UiObjectNotFoundException e) {
+ throw new IllegalStateException("exception when clicking on object '" + description
+ + "'", e);
+ }
+ }
+
+ /**
+ * Opens the system notification and clicks a given notification.
+ *
+ * @param text Notificaton's text as displayed by the UI.
+ */
+ private void clickOnNotification(String text) {
+ final UiObject notification = getNotification(text);
+ click(notification, "notification '" + text+ "'");
+ }
+
+ private UiObject getNotification(String text) {
+ final boolean opened = mDevice.openNotification();
+ Log.v(TAG, "openNotification(): " + opened);
+ final boolean gotIt = mDevice.wait(Until.hasObject(By.pkg(SYSTEMUI_PACKAGE)), mTimeout);
+ assertWithMessage("could not get system ui (%s)", SYSTEMUI_PACKAGE).that(gotIt).isTrue();
+
+ return getObject(text);
+ }
+ /**
+ * Gets an object that might not yet be available in current UI.
+ *
+ * @param text Object's text as displayed by the UI.
+ */
+ private UiObject getObject(String text) {
+ final boolean gotIt = mDevice.wait(Until.hasObject(By.text(text)), mTimeout);
+ assertWithMessage("object with text '%s') not visible yet", text).that(gotIt).isTrue();
+ return getVisibleObject(text);
+ }
+
+ /**
+ * Gets an object which is guaranteed to be present in the current UI.
+ *
+ * @param text Object's text as displayed by the UI.
+ */
+ private UiObject getVisibleObject(String text) {
+ final UiObject uiObject = mDevice.findObject(new UiSelector().text(text));
+ assertWithMessage("could not find object with '%s'", text).that(uiObject.exists()).isTrue();
+ return uiObject;
+ }
+ /////////////////////////////////////////
+ // End of temporary notification code. //
+ /////////////////////////////////////////
+}
diff --git a/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index df89297..07e810f 100644
--- a/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -714,7 +714,7 @@
*/
public int[] getAvailableToneMapModesChecked() {
Key<int[]> key = CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES;
- int[] modes = getValueFromKeyNonNull(key);
+ int[] modes = mCharacteristics.get(key);
if (modes == null) {
return new int[0];
diff --git a/tests/fragment/src/android/fragment/cts/FragmentAnimatorTest.java b/tests/fragment/src/android/fragment/cts/FragmentAnimatorTest.java
index f0dac9a..b5e9cad 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentAnimatorTest.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentAnimatorTest.java
@@ -15,34 +15,27 @@
*/
package android.fragment.cts;
-import static junit.framework.Assert.*;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.app.Fragment;
import android.app.FragmentController;
import android.app.FragmentManager;
import android.app.FragmentManagerNonConfig;
import android.app.Instrumentation;
-import android.os.Debug;
import android.os.Parcelable;
-import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.util.Pair;
import android.view.View;
-import android.view.ViewGroup;
import org.junit.Before;
import org.junit.Rule;
@@ -298,6 +291,7 @@
assertPostponed(fragment2, 0);
assertNotNull(fragment1.getView());
assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
+ assertTrue(FragmentTestUtil.isVisible(fragment1));
assertTrue(fragment1.getView().isAttachedToWindow());
fragment2.startPostponedEnterTransition();
@@ -335,7 +329,7 @@
FragmentTestUtil.popBackStackImmediate(mActivityRule);
assertNotNull(fragment1.getView());
- assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
+ assertTrue(FragmentTestUtil.isVisible(fragment1));
assertTrue(fragment1.getView().isAttachedToWindow());
assertTrue(fragment1.isAdded());
@@ -447,7 +441,8 @@
private void assertPostponed(AnimatorFragment fragment, int expectedAnimators)
throws InterruptedException {
assertTrue(fragment.mOnCreateViewCalled);
- assertEquals(View.INVISIBLE, fragment.getView().getVisibility());
+ assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+ assertFalse(FragmentTestUtil.isVisible(fragment));
assertEquals(expectedAnimators, fragment.numAnimators);
}
diff --git a/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java b/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
index b4407a0..3a69b44 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
@@ -17,17 +17,17 @@
import static org.junit.Assert.assertEquals;
-import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentController;
import android.app.FragmentManagerNonConfig;
import android.app.Instrumentation;
-import android.os.Handler;
import android.os.Parcelable;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.util.Pair;
+import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
public class FragmentTestUtil {
public static void waitForExecution(final ActivityTestRule<FragmentTestActivity> rule) {
@@ -160,4 +160,12 @@
});
return result[0];
}
+
+ public static boolean isVisible(Fragment fragment) {
+ View view = fragment.getView();
+ AccessibilityNodeInfo accessibilityNodeInfo = view.createAccessibilityNodeInfo();
+ boolean isVisible = accessibilityNodeInfo.isVisibleToUser();
+ accessibilityNodeInfo.recycle();
+ return isVisible;
+ }
}
diff --git a/tests/fragment/src/android/fragment/cts/FragmentViewTests.java b/tests/fragment/src/android/fragment/cts/FragmentViewTests.java
index 82890df..c12fb30 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentViewTests.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentViewTests.java
@@ -30,6 +30,7 @@
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -899,6 +900,105 @@
assertEquals(View.GONE, fragment2.getView().getVisibility());
}
+ // Test to ensure that popping and adding a fragment properly track the fragments added
+ // and removed.
+ @Test
+ public void popAdd() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ // One fragment with a view
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment1).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container, fragment1);
+
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ final StrictViewFragment fragment3 = new StrictViewFragment();
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ fm.popBackStack();
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+ fm.executePendingTransactions();
+ fm.popBackStack();
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment3)
+ .addToBackStack(null)
+ .commit();
+ fm.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(container, fragment3);
+ }
+
+ // Ensure that non-optimized transactions are executed individually rather than together.
+ // This forces references from one fragment to another that should be executed earlier
+ // to work.
+ @Test
+ public void nonOptimizeTogether() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fragment1.setLayoutId(R.layout.scene1);
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fragment2.setLayoutId(R.layout.text_a);
+
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .setAllowOptimization(false)
+ .addToBackStack(null)
+ .commit();
+ fm.beginTransaction()
+ .add(R.id.squareContainer, fragment2)
+ .setAllowOptimization(false)
+ .addToBackStack(null)
+ .commit();
+ fm.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(container, fragment1);
+ assertNotNull(findViewById(R.id.textA));
+ }
+
+ // Ensure that there is no problem if the child fragment manager is used before
+ // the View has been added.
+ @Test
+ public void childFragmentManager() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
+
+ final StrictViewFragment fragment1 = new ParentFragment();
+ fragment1.setLayoutId(R.layout.double_container);
+
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment1);
+ ViewGroup innerContainer = (ViewGroup)
+ fragment1.getView().findViewById(R.id.fragmentContainer1);
+
+ Fragment fragment2 = fragment1.getChildFragmentManager().findFragmentByTag("inner");
+ FragmentTestUtil.assertChildren(innerContainer, fragment2);
+ }
+
private View findViewById(int viewId) {
return mActivityRule.getActivity().findViewById(viewId);
}
@@ -921,4 +1021,26 @@
view.setVisibility(visibility);
super.onViewCreated(view, savedInstanceState);
}
- }}
+ }
+
+ public static class ParentFragment extends StrictViewFragment {
+ public ParentFragment() {
+ setLayoutId(R.layout.double_container);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = super.onCreateView(inflater, container, savedInstanceState);
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fragment2.setLayoutId(R.layout.text_a);
+
+ getChildFragmentManager().beginTransaction()
+ .add(R.id.fragmentContainer1, fragment2, "inner")
+ .addToBackStack(null)
+ .commit();
+ getChildFragmentManager().executePendingTransactions();
+ return view;
+ }
+ }
+}
diff --git a/tests/fragment/src/android/fragment/cts/PostponedTransitionTest.java b/tests/fragment/src/android/fragment/cts/PostponedTransitionTest.java
index 29a723b..e94cb5a 100644
--- a/tests/fragment/src/android/fragment/cts/PostponedTransitionTest.java
+++ b/tests/fragment/src/android/fragment/cts/PostponedTransitionTest.java
@@ -27,7 +27,6 @@
import android.app.FragmentManagerNonConfig;
import android.app.Instrumentation;
import android.os.Bundle;
-import android.os.Debug;
import android.os.Parcelable;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
@@ -629,7 +628,7 @@
assertFalse(fragment.isAdded());
assertNull(fragment.getView());
assertNotNull(mBeginningFragment.getView());
- assertEquals(View.VISIBLE, mBeginningFragment.getView().getVisibility());
+ assertTrue(FragmentTestUtil.isVisible(mBeginningFragment));
assertTrue(mBeginningFragment.getView().isAttachedToWindow());
}
@@ -687,7 +686,9 @@
assertTrue(fromFragment.getView().isAttachedToWindow());
assertTrue(toFragment.getView().isAttachedToWindow());
assertEquals(View.VISIBLE, fromFragment.getView().getVisibility());
- assertEquals(View.INVISIBLE, toFragment.getView().getVisibility());
+ assertTrue(FragmentTestUtil.isVisible(fromFragment));
+ assertEquals(View.VISIBLE, toFragment.getView().getVisibility());
+ assertFalse(FragmentTestUtil.isVisible(toFragment));
assureNoTransition(fromFragment);
assureNoTransition(toFragment);
assertTrue(fromFragment.isResumed());
diff --git a/tests/jank/Android.mk b/tests/jank/Android.mk
index fe3644a..0d4f533 100644
--- a/tests/jank/Android.mk
+++ b/tests/jank/Android.mk
@@ -27,6 +27,12 @@
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner ub-uiautomator ub-janktesthelper
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ub-janktesthelper \
+ junit \
+ legacy-android-test
include $(BUILD_CTS_PACKAGE)
diff --git a/tests/jdwp/runner/device-side/Android.mk b/tests/jdwp/runner/device-side/Android.mk
index c30cf88..871fc39 100644
--- a/tests/jdwp/runner/device-side/Android.mk
+++ b/tests/jdwp/runner/device-side/Android.mk
@@ -24,7 +24,7 @@
LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util junit
# don't include these packages in any target
LOCAL_MODULE_TAGS := optional
diff --git a/tests/leanbackjank/Android.mk b/tests/leanbackjank/Android.mk
index b24b1da..25fd9a0 100644
--- a/tests/leanbackjank/Android.mk
+++ b/tests/leanbackjank/Android.mk
@@ -35,7 +35,8 @@
ub-janktesthelper \
android-support-v17-leanback \
android-support-v7-recyclerview \
- android-support-v4
+ android-support-v4 \
+ legacy-android-test
include $(BUILD_CTS_PACKAGE)
diff --git a/tests/libcore/luni/Android.mk b/tests/libcore/luni/Android.mk
index f722f5fe..2d65cd3 100644
--- a/tests/libcore/luni/Android.mk
+++ b/tests/libcore/luni/Android.mk
@@ -47,8 +47,6 @@
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
-LOCAL_JAVA_RESOURCE_DIRS := resources
-
LOCAL_JAVA_RESOURCE_FILES := \
libcore/expectations/brokentests.txt \
libcore/expectations/icebox.txt \
diff --git a/tests/netlegacy22.api/Android.mk b/tests/netlegacy22.api/Android.mk
index 93f848b..3fca098 100644
--- a/tests/netlegacy22.api/Android.mk
+++ b/tests/netlegacy22.api/Android.mk
@@ -27,7 +27,7 @@
LOCAL_SDK_VERSION := 22
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
diff --git a/tests/netlegacy22.permission/Android.mk b/tests/netlegacy22.permission/Android.mk
index 8f6f12e..d21cd676 100644
--- a/tests/netlegacy22.permission/Android.mk
+++ b/tests/netlegacy22.permission/Android.mk
@@ -27,7 +27,7 @@
LOCAL_SDK_VERSION := 22
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
diff --git a/tests/tests/app.usage/Android.mk b/tests/tests/app.usage/Android.mk
index 0303dbd..f8bab5b 100644
--- a/tests/tests/app.usage/Android.mk
+++ b/tests/tests/app.usage/Android.mk
@@ -26,7 +26,11 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctstestrunner \
+ android-support-test \
+ junit \
+ legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/app/Android.mk b/tests/tests/app/Android.mk
index cf76aa0b..d710ce8 100644
--- a/tests/tests/app/Android.mk
+++ b/tests/tests/app/Android.mk
@@ -26,7 +26,11 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctstestrunner \
+ android-support-test \
+ junit \
+ legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/appwidget/Android.mk b/tests/tests/appwidget/Android.mk
index d97d7d7..077461d 100644
--- a/tests/tests/appwidget/Android.mk
+++ b/tests/tests/appwidget/Android.mk
@@ -20,13 +20,20 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-java-files-under, common/src)
LOCAL_PACKAGE_NAME := CtsAppWidgetTestCases
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target-minus-junit4 ctstestrunner hamcrest
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ mockito-target-minus-junit4 \
+ ctstestrunner \
+ junit \
+ legacy-android-test
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/appwidget/AndroidTest.xml b/tests/tests/appwidget/AndroidTest.xml
index 9a8bad2..6e4ee66 100644
--- a/tests/tests/appwidget/AndroidTest.xml
+++ b/tests/tests/appwidget/AndroidTest.xml
@@ -15,6 +15,8 @@
<configuration description="Config for CTS App Widget test cases">
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
<option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsAppWidgetLauncher1.apk" />
+ <option name="test-file-name" value="CtsAppWidgetLauncher2.apk" />
<option name="test-file-name" value="CtsAppWidgetTestCases.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/tests/appwidget/common/src/android/appwidget/cts/common/Constants.java b/tests/tests/appwidget/common/src/android/appwidget/cts/common/Constants.java
new file mode 100644
index 0000000..954a81b
--- /dev/null
+++ b/tests/tests/appwidget/common/src/android/appwidget/cts/common/Constants.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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.appwidget.cts.common;
+
+public class Constants {
+
+ public static final String ACTION_CONFIRM_PIN =
+ "android.appwidget.cts.packages.AppWidgetConfirmPin";
+
+ public static final String ACTION_SETUP_REPLY =
+ "android.appwidget.cts.SETUP_REPLY";
+
+ public static final String EXTRA_SUCCESS = "SUCCESS";
+ public static final String EXTRA_PACKAGE = "PACKAGE_NAME";
+ public static final String EXTRA_REQUEST = "REQUEST";
+
+}
diff --git a/tests/tests/appwidget/packages/Android.mk b/tests/tests/appwidget/packages/Android.mk
new file mode 100644
index 0000000..cddf11e
--- /dev/null
+++ b/tests/tests/appwidget/packages/Android.mk
@@ -0,0 +1,17 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/appwidget/packages/launchermanifest/Android.mk b/tests/tests/appwidget/packages/launchermanifest/Android.mk
new file mode 100644
index 0000000..dc9ce04
--- /dev/null
+++ b/tests/tests/appwidget/packages/launchermanifest/Android.mk
@@ -0,0 +1,57 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+#-----------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsAppWidgetLauncher1
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_AAPT_FLAGS += --rename-manifest-package android.appwidget.cts.packages.launcher1
+
+include $(BUILD_CTS_PACKAGE)
+
+#-----------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsAppWidgetLauncher2
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_AAPT_FLAGS += --rename-manifest-package android.appwidget.cts.packages.launcher2
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/appwidget/packages/launchermanifest/AndroidManifest.xml b/tests/tests/appwidget/packages/launchermanifest/AndroidManifest.xml
new file mode 100644
index 0000000..ad82a7d
--- /dev/null
+++ b/tests/tests/appwidget/packages/launchermanifest/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 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="android.appwidget.cts.packages">
+
+ <application>
+ <activity android:name="Launcher">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.HOME" />
+ </intent-filter>
+ </activity>
+ <activity android:name="AppWidgetConfirmPin">
+ <intent-filter>
+ <action android:name="android.content.pm.action.CONFIRM_PIN_ITEM" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/tests/appwidget/packages/src/android/appwidget/cts/packages/AppWidgetConfirmPin.java b/tests/tests/appwidget/packages/src/android/appwidget/cts/packages/AppWidgetConfirmPin.java
new file mode 100644
index 0000000..4b50c80
--- /dev/null
+++ b/tests/tests/appwidget/packages/src/android/appwidget/cts/packages/AppWidgetConfirmPin.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 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.appwidget.cts.packages;
+
+import android.app.Activity;
+import android.appwidget.cts.common.Constants;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.PinItemRequest;
+import android.os.Bundle;
+
+public class AppWidgetConfirmPin extends Activity {
+
+ private PinItemRequest mRequest;
+
+ private BroadcastReceiver mReceiver;
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ try {
+ final LauncherApps launcherApps = getSystemService(LauncherApps.class);
+ mRequest = launcherApps.getPinItemRequest(getIntent());
+
+ if (mRequest == null) {
+ throw new IllegalArgumentException("Null request");
+ }
+
+ if (mRequest.getRequestType() != PinItemRequest.REQUEST_TYPE_APPWIDGET ||
+ mRequest.getAppWidgetProviderInfo(this) == null) {
+ throw new IllegalArgumentException("Wrong request");
+ }
+
+ mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onCommandReceive(intent);
+ }
+ };
+ registerReceiver(mReceiver, new IntentFilter(Constants.ACTION_CONFIRM_PIN));
+ sendSetupReply(true);
+ } catch (Exception e) {
+ sendSetupReply(false);
+ finish();
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mReceiver != null) {
+ unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ }
+
+ private void sendSetupReply(boolean success) {
+ sendBroadcast(new Intent(Constants.ACTION_SETUP_REPLY)
+ .putExtra(Constants.EXTRA_SUCCESS, success)
+ .putExtra(Constants.EXTRA_PACKAGE, getPackageName())
+ .putExtra(Constants.EXTRA_REQUEST, mRequest));
+ }
+
+ private void onCommandReceive(Intent intent) {
+ mRequest.accept(intent.getExtras());
+ finish();
+ }
+}
diff --git a/tests/tests/appwidget/packages/src/android/appwidget/cts/packages/Launcher.java b/tests/tests/appwidget/packages/src/android/appwidget/cts/packages/Launcher.java
new file mode 100644
index 0000000..661bd55
--- /dev/null
+++ b/tests/tests/appwidget/packages/src/android/appwidget/cts/packages/Launcher.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.appwidget.cts.packages;
+
+import android.app.Activity;
+
+public class Launcher extends Activity {
+}
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
index 313f009..6348c6a 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
@@ -40,45 +40,33 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.appwidget.cts.R;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
-import android.test.InstrumentationTestCase;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService.RemoteViewsFactory;
-import libcore.io.IoUtils;
-import libcore.io.Streams;
+
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.mockito.InOrder;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import java.io.FileInputStream;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
-public class AppWidgetTest extends InstrumentationTestCase {
+public class AppWidgetTest extends AppWidgetTestCase {
private static final long OPERATION_TIMEOUT = 20 * 1000; // 20 sec
- private static final String FIRST_APP_WIDGET_CONFIGURE_ACTIVITY =
- "android.appwidget.cts.provider.FirstAppWidgetConfigureActivity";
-
- private static final String SECOND_APP_WIDGET_CONFIGURE_ACTIVITY =
- "android.appwidget.cts.provider.SecondAppWidgetConfigureActivity";
-
private final Object mLock = new Object();
@Override
@@ -96,11 +84,6 @@
"appwidget revokebind --package android.appwidget.cts --user 0";
- private boolean hasAppWidgets() {
- return getInstrumentation().getTargetContext().getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS);
- }
-
public void testGetAppInstalledProvidersForCurrentUserLegacy() throws Exception {
if (!hasAppWidgets()) {
return;
@@ -1313,117 +1296,25 @@
@SuppressWarnings("deprecation")
private void assertExpectedInstalledProviders(List<AppWidgetProviderInfo> providers) {
- boolean firstProviderVerified = false;
- boolean secondProviderVerified = false;
-
- ComponentName firstComponentName = new ComponentName(
- getInstrumentation().getTargetContext().getPackageName(),
- FirstAppWidgetProvider.class.getName());
-
- ComponentName secondComponentName = new ComponentName(
- getInstrumentation().getTargetContext().getPackageName(),
- SecondAppWidgetProvider.class.getName());
-
- final int providerCount = providers.size();
- for (int i = 0; i < providerCount; i++) {
- AppWidgetProviderInfo provider = providers.get(i);
-
- if (firstComponentName.equals(provider.provider)
- && android.os.Process.myUserHandle().equals(provider.getProfile())) {
- assertEquals(getNormalizedDimensionResource(R.dimen.first_min_appwidget_size),
- provider.minWidth);
- assertEquals(getNormalizedDimensionResource(R.dimen.first_min_appwidget_size),
- provider.minHeight);
- assertEquals(getNormalizedDimensionResource(
- R.dimen.first_min_resize_appwidget_size), provider.minResizeWidth);
- assertEquals(getNormalizedDimensionResource(
- R.dimen.first_min_resize_appwidget_size), provider.minResizeHeight);
- assertEquals(getIntResource(R.integer.first_update_period_millis),
- provider.updatePeriodMillis);
- assertEquals(getInstrumentation().getTargetContext().getPackageName(),
- provider.configure.getPackageName());
- assertEquals(FIRST_APP_WIDGET_CONFIGURE_ACTIVITY,
- provider.configure.getClassName());
- assertEquals(getIntResource(R.integer.first_resize_mode),
- provider.resizeMode);
- assertEquals(getIntResource(R.integer.first_widget_category),
- provider.widgetCategory);
- assertEquals(R.layout.first_initial_layout,
- provider.initialLayout);
- assertEquals(R.layout.first_initial_keyguard_layout,
- provider.initialKeyguardLayout);
- assertEquals(R.drawable.first_android_icon,
- provider.previewImage);
- assertEquals(R.id.first_auto_advance_view_id,
- provider.autoAdvanceViewId);
- firstProviderVerified = true;
- } else if (secondComponentName.equals(provider.provider)
- && android.os.Process.myUserHandle().equals(provider.getProfile())) {
- assertEquals(getNormalizedDimensionResource(R.dimen.second_min_appwidget_size),
- provider.minWidth);
- assertEquals(getNormalizedDimensionResource(R.dimen.second_min_appwidget_size),
- provider.minHeight);
- assertEquals(getNormalizedDimensionResource(
- R.dimen.second_min_resize_appwidget_size), provider.minResizeWidth);
- assertEquals(getNormalizedDimensionResource(
- R.dimen.second_min_resize_appwidget_size), provider.minResizeHeight);
- assertEquals(getIntResource(R.integer.second_update_period_millis),
- provider.updatePeriodMillis);
- assertEquals(getInstrumentation().getTargetContext().getPackageName(),
- provider.configure.getPackageName());
- assertEquals(SECOND_APP_WIDGET_CONFIGURE_ACTIVITY,
- provider.configure.getClassName());
- assertEquals(getIntResource(R.integer.second_resize_mode),
- provider.resizeMode);
- assertEquals(getIntResource(R.integer.second_widget_category),
- provider.widgetCategory);
- assertEquals(R.layout.second_initial_layout,
- provider.initialLayout);
- assertEquals(R.layout.second_initial_keyguard_layout,
- provider.initialKeyguardLayout);
- assertEquals(R.drawable.second_android_icon,
- provider.previewImage);
- assertEquals(R.id.second_auto_advance_view_id,
- provider.autoAdvanceViewId);
- secondProviderVerified = true;
- }
- }
-
- assertTrue(firstProviderVerified && secondProviderVerified);
+ boolean[] verifiedWidgets = verifyInstalledProviders(providers);
+ assertTrue(verifiedWidgets[0]);
+ assertTrue(verifiedWidgets[1]);
}
- private void grantBindAppWidgetPermission() {
- executeShellCommandIgnoreOutput(GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND);
+ private void grantBindAppWidgetPermission() throws Exception {
+ runShellCommand(GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND);
}
- private void revokeBindAppWidgetPermission() {
- executeShellCommandIgnoreOutput(REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND);
- }
-
- private void executeShellCommandIgnoreOutput(String command) {
- ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
- .executeShellCommand(command);
- try {
- Streams.readFully(new FileInputStream(pfd.getFileDescriptor()));
- } catch (IOException ioe) {
- IoUtils.closeQuietly(pfd);
- }
+ private void revokeBindAppWidgetPermission() throws Exception {
+ runShellCommand(REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND);
}
private AppWidgetProviderInfo getFirstAppWidgetProviderInfo() {
- ComponentName firstComponentName = new ComponentName(
- getInstrumentation().getTargetContext().getPackageName(),
- FirstAppWidgetProvider.class.getName());
-
- return getProviderInfo(firstComponentName);
+ return getProviderInfo(getFirstWidgetComponent());
}
private AppWidgetProviderInfo getSecondAppWidgetProviderInfo() {
- ComponentName secondComponentName = new ComponentName(
- getInstrumentation().getTargetContext().getPackageName(),
- SecondAppWidgetProvider.class.getName());
-
- return getProviderInfo(secondComponentName);
+ return getProviderInfo(getSecondWidgetComponent());
}
private AppWidgetProviderInfo getProviderInfo(ComponentName componentName) {
@@ -1442,15 +1333,6 @@
return null;
}
- private int getNormalizedDimensionResource(int resId) {
- return getInstrumentation().getTargetContext().getResources()
- .getDimensionPixelSize(resId);
- }
-
- private int getIntResource(int resId) {
- return getInstrumentation().getTargetContext().getResources().getInteger(resId);
- }
-
private AppWidgetManager getAppWidgetManager() {
return (AppWidgetManager) getInstrumentation().getTargetContext()
.getSystemService(Context.APPWIDGET_SERVICE);
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java
new file mode 100644
index 0000000..fb0dbd1
--- /dev/null
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 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.appwidget.cts;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.appwidget.cts.provider.FirstAppWidgetProvider;
+import android.appwidget.cts.provider.SecondAppWidgetProvider;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.os.ParcelFileDescriptor;
+import android.test.InstrumentationTestCase;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class AppWidgetTestCase extends InstrumentationTestCase {
+ private static final String FIRST_APP_WIDGET_CONFIGURE_ACTIVITY =
+ "android.appwidget.cts.provider.FirstAppWidgetConfigureActivity";
+
+ private static final String SECOND_APP_WIDGET_CONFIGURE_ACTIVITY =
+ "android.appwidget.cts.provider.SecondAppWidgetConfigureActivity";
+
+ public boolean hasAppWidgets() {
+ return getInstrumentation().getTargetContext().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS);
+ }
+
+ public boolean[] verifyInstalledProviders(List<AppWidgetProviderInfo> providers) {
+ boolean firstProviderVerified = false;
+ boolean secondProviderVerified = false;
+
+ ComponentName firstComponentName = getFirstWidgetComponent();
+ ComponentName secondComponentName = getSecondWidgetComponent();
+
+ final int providerCount = providers.size();
+ for (int i = 0; i < providerCount; i++) {
+ AppWidgetProviderInfo provider = providers.get(i);
+
+ if (firstComponentName.equals(provider.provider)
+ && android.os.Process.myUserHandle().equals(provider.getProfile())) {
+ assertEquals(getNormalizedDimensionResource(android.appwidget.cts.R.dimen.first_min_appwidget_size),
+ provider.minWidth);
+ assertEquals(getNormalizedDimensionResource(android.appwidget.cts.R.dimen.first_min_appwidget_size),
+ provider.minHeight);
+ assertEquals(getNormalizedDimensionResource(
+ android.appwidget.cts.R.dimen.first_min_resize_appwidget_size), provider.minResizeWidth);
+ assertEquals(getNormalizedDimensionResource(
+ android.appwidget.cts.R.dimen.first_min_resize_appwidget_size), provider.minResizeHeight);
+ assertEquals(getIntResource(android.appwidget.cts.R.integer.first_update_period_millis),
+ provider.updatePeriodMillis);
+ assertEquals(getInstrumentation().getTargetContext().getPackageName(),
+ provider.configure.getPackageName());
+ assertEquals(FIRST_APP_WIDGET_CONFIGURE_ACTIVITY,
+ provider.configure.getClassName());
+ assertEquals(getIntResource(android.appwidget.cts.R.integer.first_resize_mode),
+ provider.resizeMode);
+ assertEquals(getIntResource(android.appwidget.cts.R.integer.first_widget_category),
+ provider.widgetCategory);
+ assertEquals(android.appwidget.cts.R.layout.first_initial_layout,
+ provider.initialLayout);
+ assertEquals(android.appwidget.cts.R.layout.first_initial_keyguard_layout,
+ provider.initialKeyguardLayout);
+ assertEquals(android.appwidget.cts.R.drawable.first_android_icon,
+ provider.previewImage);
+ assertEquals(android.appwidget.cts.R.id.first_auto_advance_view_id,
+ provider.autoAdvanceViewId);
+ firstProviderVerified = true;
+ } else if (secondComponentName.equals(provider.provider)
+ && android.os.Process.myUserHandle().equals(provider.getProfile())) {
+ assertEquals(getNormalizedDimensionResource(android.appwidget.cts.R.dimen.second_min_appwidget_size),
+ provider.minWidth);
+ assertEquals(getNormalizedDimensionResource(android.appwidget.cts.R.dimen.second_min_appwidget_size),
+ provider.minHeight);
+ assertEquals(getNormalizedDimensionResource(
+ android.appwidget.cts.R.dimen.second_min_resize_appwidget_size), provider.minResizeWidth);
+ assertEquals(getNormalizedDimensionResource(
+ android.appwidget.cts.R.dimen.second_min_resize_appwidget_size), provider.minResizeHeight);
+ assertEquals(getIntResource(android.appwidget.cts.R.integer.second_update_period_millis),
+ provider.updatePeriodMillis);
+ assertEquals(getInstrumentation().getTargetContext().getPackageName(),
+ provider.configure.getPackageName());
+ assertEquals(SECOND_APP_WIDGET_CONFIGURE_ACTIVITY,
+ provider.configure.getClassName());
+ assertEquals(getIntResource(android.appwidget.cts.R.integer.second_resize_mode),
+ provider.resizeMode);
+ assertEquals(getIntResource(android.appwidget.cts.R.integer.second_widget_category),
+ provider.widgetCategory);
+ assertEquals(android.appwidget.cts.R.layout.second_initial_layout,
+ provider.initialLayout);
+ assertEquals(android.appwidget.cts.R.layout.second_initial_keyguard_layout,
+ provider.initialKeyguardLayout);
+ assertEquals(android.appwidget.cts.R.drawable.second_android_icon,
+ provider.previewImage);
+ assertEquals(android.appwidget.cts.R.id.second_auto_advance_view_id,
+ provider.autoAdvanceViewId);
+ secondProviderVerified = true;
+ }
+ }
+
+ return new boolean[]{firstProviderVerified, secondProviderVerified};
+ }
+
+ private int getNormalizedDimensionResource(int resId) {
+ return getInstrumentation().getTargetContext().getResources()
+ .getDimensionPixelSize(resId);
+ }
+
+ private int getIntResource(int resId) {
+ return getInstrumentation().getTargetContext().getResources().getInteger(resId);
+ }
+
+ public ComponentName getFirstWidgetComponent() {
+ return new ComponentName(
+ getInstrumentation().getTargetContext().getPackageName(),
+ FirstAppWidgetProvider.class.getName());
+ }
+
+ public ComponentName getSecondWidgetComponent() {
+ return new ComponentName(
+ getInstrumentation().getTargetContext().getPackageName(),
+ SecondAppWidgetProvider.class.getName());
+ }
+
+ public ArrayList<String> runShellCommand(String command) throws Exception {
+ ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
+ .executeShellCommand(command);
+
+ ArrayList<String> ret = new ArrayList<>();
+ // Read the input stream fully.
+ try (BufferedReader r = new BufferedReader(
+ new InputStreamReader(new ParcelFileDescriptor.AutoCloseInputStream(pfd)))) {
+ String line;
+ while ((line = r.readLine()) != null) {
+ ret.add(line);
+ }
+ }
+ return ret;
+ }
+}
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
new file mode 100644
index 0000000..a337143
--- /dev/null
+++ b/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 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.appwidget.cts;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.cts.common.Constants;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.LauncherApps;
+import android.os.Handler;
+
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class RequestPinAppWidgetTest extends AppWidgetTestCase {
+
+ private static final String LAUNCHER_CLASS = "android.appwidget.cts.packages.Launcher";
+ private static final String ACTION_PIN_RESULT = "android.appwidget.cts.ACTION_PIN_RESULT";
+
+ private String mDefaultLauncher;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mDefaultLauncher = getDefaultLauncher();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ // Set the launcher back
+ setLauncher(mDefaultLauncher);
+ }
+
+ private void runPinWidgetTest(final String launcherPkg) throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+ setLauncher(launcherPkg + "/" + LAUNCHER_CLASS);
+
+ Context context = getInstrumentation().getContext();
+
+ // Request to pin widget
+ BlockingReceiver setupReceiver = new BlockingReceiver()
+ .register(Constants.ACTION_SETUP_REPLY);
+
+ PendingIntent pinResult = PendingIntent.getBroadcast(context, 0,
+ new Intent(ACTION_PIN_RESULT), PendingIntent.FLAG_ONE_SHOT);
+ AppWidgetManager.getInstance(context).requestPinAppWidget(
+ getFirstWidgetComponent(), pinResult);
+
+ setupReceiver.await();
+ // Verify that the confirmation dialog was opened
+ assertTrue(setupReceiver.mResult.getBooleanExtra(Constants.EXTRA_SUCCESS, false));
+ assertEquals(launcherPkg, setupReceiver.mResult.getStringExtra(Constants.EXTRA_PACKAGE));
+ setupReceiver.unregister();
+
+ LauncherApps.PinItemRequest req =
+ setupReceiver.mResult.getParcelableExtra(Constants.EXTRA_REQUEST);
+ assertNotNull(req);
+ // Verify that multiple calls to getAppWidgetProviderInfo have proper dimension.
+ boolean[] providerInfo = verifyInstalledProviders(Arrays.asList(
+ req.getAppWidgetProviderInfo(context), req.getAppWidgetProviderInfo(context)));
+ assertTrue(providerInfo[0]);
+
+ // Accept the request
+ BlockingReceiver resultReceiver = new BlockingReceiver().register(ACTION_PIN_RESULT);
+ context.sendBroadcast(new Intent(Constants.ACTION_CONFIRM_PIN)
+ .setPackage(launcherPkg)
+ .putExtra("dummy", "dummy-2"));
+ resultReceiver.await();
+
+ // Verify that the result contain the extras
+ assertEquals("dummy-2", resultReceiver.mResult.getStringExtra("dummy"));
+ resultReceiver.unregister();
+ }
+
+ public void testPinWidget_launcher1() throws Exception {
+ runPinWidgetTest("android.appwidget.cts.packages.launcher1");
+ }
+
+ public void testPinWidget_launcher2() throws Exception {
+ runPinWidgetTest("android.appwidget.cts.packages.launcher2");
+ }
+
+ private String getDefaultLauncher() throws Exception {
+ final String PREFIX = "Launcher: ComponentInfo{";
+ final String POSTFIX = "}";
+ for (String s : runShellCommand("cmd shortcut get-default-launcher")) {
+ if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) {
+ return s.substring(PREFIX.length(), s.length() - POSTFIX.length());
+ }
+ }
+ throw new Exception("Default launcher not found");
+ }
+
+ private void setLauncher(String component) throws Exception {
+ runShellCommand("cmd package set-home-activity --user "
+ + getInstrumentation().getContext().getUserId() + " " + component);
+ }
+
+ private class BlockingReceiver extends BroadcastReceiver {
+ private final CountDownLatch notifier = new CountDownLatch(1);
+
+ private Intent mResult;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mResult = new Intent(intent);
+ notifier.countDown();
+ }
+
+ public BlockingReceiver register(String action) {
+ Context context = getInstrumentation().getContext();
+ context.registerReceiver(this, new IntentFilter(action),
+ null, new Handler(context.getMainLooper()));
+ return this;
+ }
+
+ public void await() throws Exception {
+ assertTrue(notifier.await(20, TimeUnit.SECONDS));
+ }
+
+ public void unregister() {
+ getInstrumentation().getContext().unregisterReceiver(this);
+ }
+ }
+}
diff --git a/tests/tests/carrierapi/Android.mk b/tests/tests/carrierapi/Android.mk
index 4cb2c38..298c6ab 100644
--- a/tests/tests/carrierapi/Android.mk
+++ b/tests/tests/carrierapi/Android.mk
@@ -22,7 +22,11 @@
# and when built explicitly put it in the data partition
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctstestrunner \
+ compatibility-device-util \
+ junit \
+ legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/contactsproviderwipe/Android.mk b/tests/tests/contactsproviderwipe/Android.mk
new file mode 100644
index 0000000..b7bd687
--- /dev/null
+++ b/tests/tests/contactsproviderwipe/Android.mk
@@ -0,0 +1,45 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-java-files-under, common/src)
+
+LOCAL_PACKAGE_NAME := CtsContactsProviderWipe
+
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_SDK_VERSION := test_current
+
+include $(BUILD_CTS_PACKAGE)
+#include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/contactsproviderwipe/AndroidManifest.xml b/tests/tests/contactsproviderwipe/AndroidManifest.xml
new file mode 100644
index 0000000..9ac9ad0
--- /dev/null
+++ b/tests/tests/contactsproviderwipe/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 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="android.provider.cts.contactsproviderwipe">
+
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+ <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.provider.cts.contactsproviderwipe">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+</manifest>
+
diff --git a/tests/tests/contactsproviderwipe/AndroidTest.xml b/tests/tests/contactsproviderwipe/AndroidTest.xml
new file mode 100644
index 0000000..e20faa6
--- /dev/null
+++ b/tests/tests/contactsproviderwipe/AndroidTest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Provider test cases">
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsContactsProviderWipe.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.provider.cts.contactsproviderwipe" />
+ <option name="runtime-hint" value="3m" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/tests/contactsproviderwipe/src/android/provider/cts/contactsproviderwipe/ContactsContract_Wipe.java b/tests/tests/contactsproviderwipe/src/android/provider/cts/contactsproviderwipe/ContactsContract_Wipe.java
new file mode 100644
index 0000000..2bea43f
--- /dev/null
+++ b/tests/tests/contactsproviderwipe/src/android/provider/cts/contactsproviderwipe/ContactsContract_Wipe.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2017 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.provider.cts.contactsproviderwipe;
+
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.ProviderStatus;
+import android.support.test.InstrumentationRegistry;
+import android.test.AndroidTestCase;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * CTS tests for CP2 regarding data wipe.
+ *
+ * <p>We can't use CtsProviderTestCases for this test because CtsProviderTestCases creates
+ * a stable connection to the contacts provider, which would cause the test process to be killed
+ * when the CP2 process gets killed (for "pm clear").
+ */
+public class ContactsContract_Wipe extends AndroidTestCase {
+ public static final String TAG = "ContactsContract_PS";
+
+ /** 1 hour in milliseconds. */
+ private static final long ONE_HOUR_IN_MILLIS = 1000L * 60 * 60;
+
+ private long getDatabaseCreationTimestamp() {
+ try (Cursor cursor = getContext().getContentResolver().query(
+ ProviderStatus.CONTENT_URI, null, null, null, null)) {
+ assertTrue(cursor.moveToFirst());
+
+ final Long timestamp = cursor.getLong(
+ cursor.getColumnIndexOrThrow(ProviderStatus.DATABASE_CREATION_TIMESTAMP));
+ assertNotNull(timestamp);
+
+ return timestamp;
+ }
+ }
+
+ private void assertBigger(long bigger, long smaller) {
+ assertTrue("Expecting " + bigger + " > " + smaller, bigger > smaller);
+ }
+
+ private String getContactsProviderPackageName() {
+ final List<ProviderInfo> list = getContext().getPackageManager().queryContentProviders(
+ null, 0, PackageManager.MATCH_ALL);
+ assertNotNull(list);
+ for (ProviderInfo pi : list) {
+ if (TextUtils.isEmpty(pi.authority)) {
+ continue;
+ }
+ for (String authority : pi.authority.split(";")) {
+ Log.i(TAG, "Found " + authority);
+ if (ContactsContract.AUTHORITY.equals(authority)) {
+ return pi.packageName;
+ }
+ }
+ }
+ fail("Contacts provider package not found.");
+ return null;
+ }
+
+ static List<String> readAll(ParcelFileDescriptor pfd) {
+ try {
+ try {
+ final ArrayList<String> ret = new ArrayList<>();
+ try (BufferedReader r = new BufferedReader(
+ new FileReader(pfd.getFileDescriptor()))) {
+ String line;
+ while ((line = r.readLine()) != null) {
+ ret.add(line);
+ }
+ r.readLine();
+ }
+ return ret;
+ } finally {
+ pfd.close();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static String concatResult(List<String> result) {
+ final StringBuilder sb = new StringBuilder();
+ for (String s : result) {
+ sb.append(s);
+ sb.append("\n");
+ }
+ return sb.toString().trim();
+ }
+
+ private void wipeContactsProvider() {
+ final String providerPackage = getContactsProviderPackageName();
+
+ Log.i(TAG, "Wiping " + providerPackage + "...");
+
+ final String result = concatResult(readAll(
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+ "pm clear " + providerPackage)));
+ Log.i(TAG, "Result:" + result);
+
+ assertEquals("Success", result);
+ }
+
+ public void testCreationTimestamp() throws Exception {
+ final long originalTimestamp = getDatabaseCreationTimestamp();
+
+ Thread.sleep(1);
+
+ final long start = System.currentTimeMillis();
+
+ Log.i(TAG, "start=" + start);
+ Log.i(TAG, "originalTimestamp=" + originalTimestamp);
+
+ // Check: the (old) creation time should be smaller than the start time (=now).
+ // Add 1 hour to compensate for possible day light saving.
+ assertBigger(start + ONE_HOUR_IN_MILLIS, originalTimestamp);
+
+ Thread.sleep(1);
+
+ wipeContactsProvider();
+
+ // Check: the creation time should be bigger than the start time.
+ final long newTimestamp = getDatabaseCreationTimestamp();
+ Log.i(TAG, "newTimestamp=" + newTimestamp);
+
+ assertBigger(newTimestamp, start);
+ }
+
+ private void checkDatabaseWipeNotification(Uri notificationUri) throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AtomicReference<Uri> notifiedUri = new AtomicReference<>();
+
+ getContext().getContentResolver().registerContentObserver(notificationUri,
+ /* notifyForDescendants=*/ false,
+ new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ Log.i(TAG, "Received notification on " + uri);
+ notifiedUri.set(uri);
+ latch.countDown();
+ }
+ });
+
+ wipeContactsProvider();
+
+ assertTrue("Didn't receive content change notification",
+ latch.await(60, TimeUnit.SECONDS));
+
+ assertEquals(notificationUri, notifiedUri.get());
+ }
+
+ public void testDatabaseWipeNotification() throws Exception {
+ checkDatabaseWipeNotification(ProviderStatus.CONTENT_URI);
+ checkDatabaseWipeNotification(ProviderStatus.STATUS_CHANGE_NOTIFICATION_CONTENT_URI);
+ }
+}
diff --git a/tests/tests/content/Android.mk b/tests/tests/content/Android.mk
index 67fc3fa..684e665 100644
--- a/tests/tests/content/Android.mk
+++ b/tests/tests/content/Android.mk
@@ -27,7 +27,9 @@
android-support-multidex \
compatibility-device-util \
ctstestrunner \
- services.core
+ services.core \
+ junit \
+ legacy-android-test
# Use multi-dex as the compatibility-common-util-devicesidelib dependency
# on compatibility-device-util pushes us beyond 64k methods.
diff --git a/tests/tests/database/Android.mk b/tests/tests/database/Android.mk
index 36ca51b..1be93c6 100644
--- a/tests/tests/database/Android.mk
+++ b/tests/tests/database/Android.mk
@@ -21,7 +21,12 @@
# and when built explicitly put it in the data partition
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES += android-common ctstestrunner ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-common \
+ ctstestrunner \
+ ctstestrunner \
+ junit \
+ legacy-android-test
LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/tests/tests/debug/libdebugtest/android_debug_cts.cpp b/tests/tests/debug/libdebugtest/android_debug_cts.cpp
index 70cb41c..2c7c967 100644
--- a/tests/tests/debug/libdebugtest/android_debug_cts.cpp
+++ b/tests/tests/debug/libdebugtest/android_debug_cts.cpp
@@ -19,9 +19,13 @@
#include <sys/ptrace.h>
#include <sys/types.h>
+#include <sys/uio.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <functional>
+#include <vector>
+
#define LOG_TAG "Cts-DebugTest"
#define assert_or_exit(x) \
@@ -48,8 +52,19 @@
return true;
}
-static bool child(pid_t parent) __attribute__((noreturn));
-static bool child(pid_t parent) {
+static bool run_test(const std::function<void(pid_t)> &test) {
+ pid_t pid = fork();
+ assert_or_return(pid >= 0);
+ if (pid != 0)
+ return parent(pid);
+ else {
+ // child
+ test(getppid());
+ _exit(0);
+ }
+}
+
+static void ptraceAttach(pid_t parent) {
assert_or_exit(ptrace(PTRACE_ATTACH, parent, nullptr, nullptr) == 0);
int status;
assert_or_exit(waitpid(parent, &status, __WALL) == parent);
@@ -57,15 +72,52 @@
assert_or_exit(WSTOPSIG(status) == SIGSTOP);
assert_or_exit(ptrace(PTRACE_DETACH, parent, nullptr, nullptr) == 0);
- _exit(0);
}
// public static native boolean ptraceAttach();
extern "C" jboolean Java_android_debug_cts_DebugTest_ptraceAttach(JNIEnv *, jclass) {
- pid_t pid = fork();
- assert_or_return(pid >= 0);
- if (pid != 0)
- return parent(pid);
- else
- child(getppid());
+ return run_test(ptraceAttach);
+}
+
+
+static void processVmReadv(pid_t parent, const std::vector<long *> &addresses) {
+ long destination;
+ iovec local = { &destination, sizeof destination };
+
+ for (long *address : addresses) {
+ // Since we are forked, the address will be valid in the remote process as well.
+ iovec remote = { address, sizeof *address };
+ __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "%s About to read %p\n", __func__,
+ address);
+ assert_or_exit(process_vm_readv(parent, &local, 1, &remote, 1, 0) == sizeof destination);
+
+ // Compare the data with the contents of our memory.
+ assert_or_exit(destination == *address);
+ }
+}
+
+static long global_variable = 0x47474747;
+// public static native boolean processVmReadv();
+extern "C" jboolean Java_android_debug_cts_DebugTest_processVmReadv(JNIEnv *, jclass) {
+ long stack_variable = 0x42424242;
+ // This runs the test with a selection of different kinds of addresses and
+ // makes sure the child process (simulating a debugger) can read them.
+ return run_test([&](pid_t parent) {
+ processVmReadv(parent, std::vector<long *>{
+ &global_variable, &stack_variable,
+ reinterpret_cast<long *>(&processVmReadv)});
+ });
+}
+
+// public static native boolean processVmReadvNullptr();
+extern "C" jboolean Java_android_debug_cts_DebugTest_processVmReadvNullptr(JNIEnv *, jclass) {
+ // Make sure reading unallocated memory behaves reasonably.
+ return run_test([](pid_t parent) {
+ long destination;
+ iovec local = {&destination, sizeof destination};
+ iovec remote = {nullptr, sizeof(long)};
+
+ assert_or_exit(process_vm_readv(parent, &local, 1, &remote, 1, 0) == -1);
+ assert_or_exit(errno == EFAULT);
+ });
}
diff --git a/tests/tests/debug/src/android/debug/cts/DebugTest.java b/tests/tests/debug/src/android/debug/cts/DebugTest.java
index 9fae539..ca55d9c 100644
--- a/tests/tests/debug/src/android/debug/cts/DebugTest.java
+++ b/tests/tests/debug/src/android/debug/cts/DebugTest.java
@@ -25,8 +25,17 @@
}
public static native boolean ptraceAttach();
-
public void test_ptraceAttach() {
assertEquals(true, ptraceAttach());
}
+
+ public static native boolean processVmReadv();
+ public void test_processVmReadv() {
+ assertEquals(true, processVmReadv());
+ }
+
+ public static native boolean processVmReadvNullptr();
+ public void test_processVmReadvNullptr() {
+ assertEquals(true, processVmReadvNullptr());
+ }
}
diff --git a/tests/tests/dpi/Android.mk b/tests/tests/dpi/Android.mk
index 0c158121..963db7a 100644
--- a/tests/tests/dpi/Android.mk
+++ b/tests/tests/dpi/Android.mk
@@ -17,7 +17,7 @@
include $(CLEAR_VARS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner junit legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -41,6 +41,8 @@
# CTS tests, so drop it into a library that other tests can use.
include $(CLEAR_VARS)
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
+
LOCAL_SRC_FILES := src/android/dpi/cts/DefaultManifestAttributesTest.java
LOCAL_MODULE_TAGS := optional
diff --git a/tests/tests/dpi2/Android.mk b/tests/tests/dpi2/Android.mk
index 2919ef8..6e7c649 100644
--- a/tests/tests/dpi2/Android.mk
+++ b/tests/tests/dpi2/Android.mk
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
# We use the DefaultManifestAttributesTest from the android.cts.dpi package.
-LOCAL_STATIC_JAVA_LIBRARIES := android.cts.dpi ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := android.cts.dpi ctstestrunner junit
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/dreams/Android.mk b/tests/tests/dreams/Android.mk
index b268e37..eb46897 100644
--- a/tests/tests/dreams/Android.mk
+++ b/tests/tests/dreams/Android.mk
@@ -24,7 +24,7 @@
# When built, explicitly put it in the data partition.
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner junit legacy-android-test
LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/tests/tests/graphics/Android.mk b/tests/tests/graphics/Android.mk
index ee3b453..3059d71 100644
--- a/tests/tests/graphics/Android.mk
+++ b/tests/tests/graphics/Android.mk
@@ -27,7 +27,9 @@
mockito-target-minus-junit4 \
compatibility-device-util \
ctstestrunner \
- android-support-annotations
+ android-support-annotations \
+ junit \
+ legacy-android-test
LOCAL_JNI_SHARED_LIBRARIES := libctsgraphics_jni
diff --git a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
index a5fa1ab..a4b57bd 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
@@ -24,10 +24,12 @@
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Canvas.EdgeType;
import android.graphics.Canvas.VertexMode;
import android.graphics.Color;
+import android.graphics.ComposeShader;
import android.graphics.DrawFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
@@ -35,10 +37,12 @@
import android.graphics.Path.Direction;
import android.graphics.Picture;
import android.graphics.PorterDuff.Mode;
+import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.Region.Op;
+import android.graphics.Shader;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -1949,6 +1953,38 @@
assertEquals(DisplayMetrics.DENSITY_HIGH, mCanvas.getDensity());
}
+ @Test(expected = IllegalStateException.class)
+ public void testDrawHwBitmapInSwCanvas() {
+ Bitmap hwBitmap = mImmutableBitmap.copy(Config.HARDWARE, false);
+ mCanvas.drawBitmap(hwBitmap, 0, 0, null);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testHwBitmapShaderInSwCanvas1() {
+ Bitmap hwBitmap = mImmutableBitmap.copy(Config.HARDWARE, false);
+ BitmapShader bitmapShader = new BitmapShader(hwBitmap, Shader.TileMode.REPEAT,
+ Shader.TileMode.REPEAT);
+ RadialGradient gradientShader = new RadialGradient(10, 10, 30, Color.BLACK, Color.CYAN,
+ Shader.TileMode.REPEAT);
+ Shader shader = new ComposeShader(gradientShader, bitmapShader, Mode.OVERLAY);
+ Paint p = new Paint();
+ p.setShader(shader);
+ mCanvas.drawRect(0, 0, 10, 10, p);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testHwBitmapShaderInSwCanvas2() {
+ Bitmap hwBitmap = mImmutableBitmap.copy(Config.HARDWARE, false);
+ BitmapShader bitmapShader = new BitmapShader(hwBitmap, Shader.TileMode.REPEAT,
+ Shader.TileMode.REPEAT);
+ RadialGradient gradientShader = new RadialGradient(10, 10, 30, Color.BLACK, Color.CYAN,
+ Shader.TileMode.REPEAT);
+ Shader shader = new ComposeShader(bitmapShader, gradientShader, Mode.OVERLAY);
+ Paint p = new Paint();
+ p.setShader(shader);
+ mCanvas.drawRect(0, 0, 10, 10, p);
+ }
+
private void preCompare() {
final float[] values = new float[FLOAT_ARRAY_LEN];
mCanvas.getMatrix().getValues(values);
diff --git a/tests/tests/graphics/src/android/graphics/cts/ColorSpaceTest.java b/tests/tests/graphics/src/android/graphics/cts/ColorSpaceTest.java
index 4494656..cd3d2e0 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ColorSpaceTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ColorSpaceTest.java
@@ -109,7 +109,6 @@
DoubleUnaryOperator op = Math::sqrt;
ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", new float[6], new float[2],
op, sIdentity, 0.0f, 1.0f);
- assertEquals(op, cs.getOetf());
assertEquals(0.5, cs.getOetf().applyAsDouble(0.25), 1e-5);
}
@@ -123,7 +122,6 @@
DoubleUnaryOperator op = x -> x * x;
ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", new float[6], new float[2],
sIdentity, op, 0.0f, 1.0f);
- assertEquals(op, cs.getEotf());
assertEquals(0.0625, cs.getEotf().applyAsDouble(0.25), 1e-5);
}
@@ -672,7 +670,7 @@
// These cannot change
assertEquals(0, ColorSpace.get(ColorSpace.Named.SRGB).getId());
assertEquals(-1, ColorSpace.MIN_ID);
- assertEquals(64, ColorSpace.MAX_ID);
+ assertEquals(63, ColorSpace.MAX_ID);
}
@Test
diff --git a/tests/tests/graphics/src/android/graphics/cts/ColorTest.java b/tests/tests/graphics/src/android/graphics/cts/ColorTest.java
index 7df4360..4801a1a 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ColorTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ColorTest.java
@@ -37,6 +37,8 @@
public void testArgb() {
assertEquals(Color.RED, Color.argb(0xff, 0xff, 0x00, 0x00));
assertEquals(Color.YELLOW, Color.argb(0xff, 0xff, 0xff, 0x00));
+ assertEquals(Color.RED, Color.argb(1.0f, 1.0f, 0.0f, 0.0f));
+ assertEquals(Color.YELLOW, Color.argb(1.0f, 1.0f, 1.0f, 0.0f));
}
@Test
@@ -112,6 +114,8 @@
public void testRgb() {
assertEquals(Color.RED, Color.rgb(0xff, 0x00, 0x00));
assertEquals(Color.YELLOW, Color.rgb(0xff, 0xff, 0x00));
+ assertEquals(Color.RED, Color.rgb(1.0f, 0.0f, 0.0f));
+ assertEquals(Color.YELLOW, Color.rgb(1.0f, 1.0f, 0.0f));
}
@Test(expected=RuntimeException.class)
diff --git a/tests/tests/graphics/src/android/graphics/cts/Color_ColorLongTest.java b/tests/tests/graphics/src/android/graphics/cts/Color_ColorLongTest.java
new file mode 100644
index 0000000..a124dbc
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/cts/Color_ColorLongTest.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2017 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 android.graphics.Color;
+import android.graphics.ColorSpace;
+import android.graphics.ColorSpace.Named;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static android.graphics.Color.alpha;
+import static android.graphics.Color.blue;
+import static android.graphics.Color.colorSpace;
+import static android.graphics.Color.convert;
+import static android.graphics.Color.green;
+import static android.graphics.Color.luminance;
+import static android.graphics.Color.pack;
+import static android.graphics.Color.red;
+import static android.graphics.Color.toArgb;
+import static android.graphics.Color.valueOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class Color_ColorLongTest {
+ @Test
+ public void testRed() {
+ ColorSpace p3 = ColorSpace.get(Named.DISPLAY_P3);
+
+ assertEquals(0.5f, red(pack(0.5f, 0.0f, 1.0f)), 0.01f);
+ assertEquals(0.5f, red(pack(0.5f, 0.0f, 1.0f, 1.0f, p3)), 0.01f);
+ }
+
+ @Test
+ public void testGreen() {
+ ColorSpace p3 = ColorSpace.get(Named.DISPLAY_P3);
+
+ assertEquals(0.7f, green(pack(0.5f, 0.7f, 1.0f)), 0.01f);
+ assertEquals(0.7f, green(pack(0.5f, 0.7f, 1.0f, 1.0f, p3)), 0.01f);
+ }
+
+ @Test
+ public void testBlue() {
+ ColorSpace p3 = ColorSpace.get(Named.DISPLAY_P3);
+
+ assertEquals(1.0f, blue(pack(0.5f, 0.7f, 1.0f)), 0.01f);
+ assertEquals(1.0f, blue(pack(0.5f, 0.7f, 1.0f, 1.0f, p3)), 0.01f);
+ }
+
+ @Test
+ public void testAlpha() {
+ ColorSpace p3 = ColorSpace.get(Named.DISPLAY_P3);
+
+ assertEquals(0.25f, alpha(pack(0.5f, 0.7f, 1.0f, 0.25f)), 0.01f);
+ assertEquals(0.25f, alpha(pack(0.5f, 0.7f, 1.0f, 0.25f, p3)), 0.01f);
+ }
+
+ @Test
+ public void testColorSpace() {
+ ColorSpace srgb = ColorSpace.get(Named.SRGB);
+ ColorSpace p3 = ColorSpace.get(Named.DISPLAY_P3);
+
+ assertEquals(srgb, colorSpace(pack(0.5f, 0.7f, 1.0f)));
+ assertEquals(p3, colorSpace(pack(0.5f, 0.7f, 1.0f, 1.0f, p3)));
+ }
+
+ @Test
+ public void testIsSrgb() {
+ ColorSpace p3 = ColorSpace.get(Named.DISPLAY_P3);
+
+ assertTrue(Color.isSrgb(pack(0.5f, 0.7f, 1.0f)));
+ assertFalse(Color.isSrgb(pack(0.5f, 0.7f, 1.0f, 1.0f, p3)));
+
+ assertTrue(Color.valueOf(0.5f, 0.7f, 1.0f).isSrgb());
+ assertFalse(Color.valueOf(0.5f, 0.7f, 1.0f, 1.0f, p3).isSrgb());
+ }
+
+ @Test
+ public void testIsWideGamut() {
+ ColorSpace p3 = ColorSpace.get(Named.DISPLAY_P3);
+
+ assertFalse(Color.isWideGamut(pack(0.5f, 0.7f, 1.0f)));
+ assertTrue(Color.isWideGamut(pack(0.5f, 0.7f, 1.0f, 1.0f, p3)));
+
+ assertFalse(Color.valueOf(0.5f, 0.7f, 1.0f).isWideGamut());
+ assertTrue(Color.valueOf(0.5f, 0.7f, 1.0f, 1.0f, p3).isWideGamut());
+ }
+
+ @Test
+ public void testIsInColorSpace() {
+ ColorSpace p3 = ColorSpace.get(Named.DISPLAY_P3);
+
+ assertFalse(Color.isInColorSpace(pack(0.5f, 0.7f, 1.0f), p3));
+ assertTrue(Color.isInColorSpace(pack(0.5f, 0.7f, 1.0f, 1.0f, p3), p3));
+ }
+
+ @Test
+ public void testValueOf() {
+ ColorSpace p3 = ColorSpace.get(Named.DISPLAY_P3);
+
+ Color color1 = valueOf(0x7fff00ff);
+ assertEquals(1.0f, color1.red(), 0.01f);
+ assertEquals(0.0f, color1.green(), 0.01f);
+ assertEquals(1.0f, color1.blue(), 0.01f);
+ assertEquals(0.5f, color1.alpha(), 0.01f);
+ assertTrue(color1.getColorSpace().isSrgb());
+
+ Color color2 = valueOf(0.5f, 0.7f, 1.0f);
+ assertEquals(0.5f, color2.red(), 0.01f);
+ assertEquals(0.7f, color2.green(), 0.01f);
+ assertEquals(1.0f, color2.blue(), 0.01f);
+ assertEquals(1.0f, color2.alpha(), 0.01f);
+ assertTrue(color2.getColorSpace().isSrgb());
+
+ Color color3 = valueOf(0.5f, 0.5f, 1.0f, 0.25f);
+ assertEquals(0.5f, color3.red(), 0.01f);
+ assertEquals(0.5f, color3.green(), 0.01f);
+ assertEquals(1.0f, color3.blue(), 0.01f);
+ assertEquals(0.25f, color3.alpha(), 0.01f);
+ assertTrue(color3.getColorSpace().isSrgb());
+
+ Color color4 = valueOf(0.5f, 0.5f, 1.0f, 0.25f, p3);
+ assertEquals(0.5f, color4.red(), 0.01f);
+ assertEquals(0.5f, color4.green(), 0.01f);
+ assertEquals(1.0f, color4.blue(), 0.01f);
+ assertEquals(0.25f, color4.alpha(), 0.01f);
+ assertFalse(color4.getColorSpace().isSrgb());
+ assertEquals(p3, color4.getColorSpace());
+
+ Color color5 = valueOf(pack(0.5f, 0.5f, 1.0f, 0.25f, p3));
+ assertEquals(0.5f, color5.red(), 0.01f);
+ assertEquals(0.5f, color5.green(), 0.01f);
+ assertEquals(1.0f, color5.blue(), 0.01f);
+ assertEquals(0.25f, color5.alpha(), 0.01f);
+ assertFalse(color5.getColorSpace().isSrgb());
+ assertEquals(p3, color5.getColorSpace());
+
+ Color color6 = valueOf(pack(0.5f, 0.5f, 1.0f, 0.25f));
+ assertEquals(0.5f, color6.red(), 0.01f);
+ assertEquals(0.5f, color6.green(), 0.01f);
+ assertEquals(1.0f, color6.blue(), 0.01f);
+ assertEquals(0.25f, color6.alpha(), 0.01f);
+ assertTrue(color6.getColorSpace().isSrgb());
+
+ Color color7 = valueOf(new float[] { 0.5f, 0.5f, 1.0f, 0.25f }, ColorSpace.get(Named.SRGB));
+ assertEquals(0.5f, color7.red(), 0.01f);
+ assertEquals(0.5f, color7.green(), 0.01f);
+ assertEquals(1.0f, color7.blue(), 0.01f);
+ assertEquals(0.25f, color7.alpha(), 0.01f);
+ assertTrue(color7.getColorSpace().isSrgb());
+
+ float[] components = { 0.5f, 0.5f, 1.0f, 0.25f, 0.5f, 0.8f };
+ Color color8 = valueOf(components, ColorSpace.get(Named.SRGB));
+ assertEquals(0.5f, color8.red(), 0.01f);
+ assertEquals(0.5f, color8.green(), 0.01f);
+ assertEquals(1.0f, color8.blue(), 0.01f);
+ assertEquals(0.25f, color8.alpha(), 0.01f);
+ assertTrue(color8.getColorSpace().isSrgb());
+ // Make sure we received a copy
+ components[0] = 127.0f;
+ assertNotEquals(color8.red(), components[0]);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testValueOfFailure() {
+ valueOf(new float[] { 0.5f, 0.5f, 1.0f }, ColorSpace.get(Named.SRGB));
+ }
+
+ @Test
+ public void testModel() {
+ Color c = valueOf(new float[] { 0.5f, 0.5f, 1.0f, 1.0f }, ColorSpace.get(Named.CIE_XYZ));
+ assertEquals(ColorSpace.Model.XYZ, c.getModel());
+ }
+
+ @Test
+ public void testComponents() {
+ Color c = valueOf(0.1f, 0.2f, 0.3f, 0.4f);
+
+ assertEquals(4, c.getComponentCount());
+ assertEquals(c.red(), c.getComponent(0), 0.0f);
+ assertEquals(c.green(), c.getComponent(1), 0.0f);
+ assertEquals(c.blue(), c.getComponent(2), 0.0f);
+ assertEquals(c.alpha(), c.getComponent(3), 0.0f);
+
+ float[] components = c.getComponents();
+ assertEquals(c.getComponentCount(), components.length);
+ assertEquals(c.red(), components[0], 0.0f);
+ assertEquals(c.green(), components[1], 0.0f);
+ assertEquals(c.blue(), components[2], 0.0f);
+ assertEquals(c.alpha(), components[3], 0.0f);
+
+ // Make sure we received a copy
+ components[0] = 127.0f;
+ assertNotEquals(c.red(), components[0]);
+ }
+
+ @Test(expected = ArrayIndexOutOfBoundsException.class)
+ public void testComponentOutOfBounds() {
+ valueOf(0.1f, 0.2f, 0.3f, 0.4f).getComponent(4);
+ }
+
+ @Test
+ public void testToArgb() {
+ assertEquals(0xff8000ff, toArgb(pack(0.5f, 0.0f, 1.0f)));
+
+ long color = pack(0.8912f, 0.4962f, 0.1164f, 1.0f, ColorSpace.get(Named.ADOBE_RGB));
+ // Red if 0x7f instead of 0x80 because the rounding error caused by the
+ // intermediate fp16 representation
+ assertEquals(0xffff7f00, toArgb(color));
+
+ assertEquals(0xff8000ff, valueOf(0.5f, 0.0f, 1.0f).toArgb());
+ assertEquals(0xffff7f00,
+ valueOf(0.8912f, 0.4962f, 0.1164f, 1.0f, ColorSpace.get(Named.ADOBE_RGB)).toArgb());
+ }
+
+ @Test
+ public void testPackSrgb() {
+ ColorSpace srgb = ColorSpace.get(Named.SRGB);
+
+ long color1 = pack(0.5f, 0.0f, 1.0f);
+ long color2 = pack(0.5f, 0.0f, 1.0f, 1.0f);
+ long color3 = pack(0.5f, 0.0f, 1.0f, 1.0f, srgb);
+
+ assertEquals(color1, color2);
+ assertEquals(color1, color3);
+
+ assertEquals(0xff8000ff, (int) (color1 >>> 32));
+
+ long color4 = pack(0.5f, 0.0f, 1.0f);
+
+ assertEquals(0.5f, red(color4), 0.01f);
+ assertEquals(0.0f, green(color4), 0.01f);
+ assertEquals(1.0f, blue(color4), 0.01f);
+ assertEquals(1.0f, alpha(color4), 0.01f);
+
+ long color5 = pack(0xff8000ff);
+
+ assertEquals(color1, color5);
+ assertEquals(0xff8000ff, (int) (color5 >>> 32));
+
+ assertEquals(color1, valueOf(0.5f, 0.0f, 1.0f).pack());
+ }
+
+ @Test
+ public void testPack() {
+ ColorSpace p3 = ColorSpace.get(Named.DISPLAY_P3);
+ long color = pack(0.5f, 0.0f, 1.0f, 0.25f, p3);
+
+ assertEquals(0.5f, red(color), 0.01f);
+ assertEquals(0.0f, green(color), 0.01f);
+ assertEquals(1.0f, blue(color), 0.01f);
+ assertEquals(0.25f, alpha(color), 0.01f);
+
+ assertEquals(color, valueOf(0.5f, 0.0f, 1.0f, 0.25f, p3).pack());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testPackFailure() {
+ ColorSpace colorSpace = new ColorSpace.Rgb("Fake",
+ new float[] { 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f },
+ new float[] { 0.34567f, 0.35850f }, x -> x * 2.0f, x -> x / 2.0f, 0.0f, 1.0f);
+ pack(0.5f, 0.0f, 1.0f, 0.25f, colorSpace);
+ }
+
+ @Test
+ public void testLuminanceSrgb() {
+ assertEquals(0.0722f, luminance(pack(0.0f, 0.0f, 1.0f)), 0.000001f);
+ assertEquals(0.2126f, luminance(pack(1.0f, 0.0f, 0.0f)), 0.000001f);
+ assertEquals(0.7152f, luminance(pack(0.0f, 1.0f, 0.0f)), 0.000001f);
+ assertEquals(1.0f, luminance(pack(1.0f, 1.0f, 1.0f)), 0.0f);
+ assertEquals(0.0f, luminance(pack(0.0f, 0.0f, 0.0f)), 0.0f);
+
+ assertEquals(0.0722f, valueOf(0.0f, 0.0f, 1.0f).luminance(), 0.000001f);
+ assertEquals(0.2126f, valueOf(1.0f, 0.0f, 0.0f).luminance(), 0.000001f);
+ assertEquals(0.7152f, valueOf(0.0f, 1.0f, 0.0f).luminance(), 0.000001f);
+ assertEquals(1.0f, valueOf(1.0f, 1.0f, 1.0f).luminance(), 0.0f);
+ assertEquals(0.0f, valueOf(0.0f, 0.0f, 0.0f).luminance(), 0.0f);
+ }
+
+ @Test
+ public void testLuminance() {
+ ColorSpace p3 = ColorSpace.get(Named.DISPLAY_P3);
+ assertEquals(0.0722f, luminance(pack(0.0f, 0.0f, 1.0f, 1.0f, p3)), 0.000001f);
+ assertEquals(0.2126f, luminance(pack(1.0f, 0.0f, 0.0f, 1.0f, p3)), 0.000001f);
+ assertEquals(0.7152f, luminance(pack(0.0f, 1.0f, 0.0f, 1.0f, p3)), 0.000001f);
+ assertEquals(1.0f, luminance(pack(1.0f, 1.0f, 1.0f, 1.0f, p3)), 0.0f);
+ assertEquals(0.0f, luminance(pack(0.0f, 0.0f, 0.0f, 1.0f, p3)), 0.0f);
+
+ assertEquals(0.0722f, valueOf(0.0f, 0.0f, 1.0f, 1.0f, p3).luminance(), 0.000001f);
+ assertEquals(0.2126f, valueOf(1.0f, 0.0f, 0.0f, 1.0f, p3).luminance(), 0.000001f);
+ assertEquals(0.7152f, valueOf(0.0f, 1.0f, 0.0f, 1.0f, p3).luminance(), 0.000001f);
+ assertEquals(1.0f, valueOf(1.0f, 1.0f, 1.0f, 1.0f, p3).luminance(), 0.0f);
+ assertEquals(0.0f, valueOf(0.0f, 0.0f, 0.0f, 1.0f, p3).luminance(), 0.0f);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testLuminanceFunctionFailure() {
+ luminance(pack(1.0f, 1.0f, 1.0f, 1.0f, ColorSpace.get(Named.CIE_LAB)));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testLuminanceMethodFailure() {
+ valueOf(1.0f, 1.0f, 1.0f, 1.0f, ColorSpace.get(Named.CIE_LAB)).luminance();
+ }
+
+ @Test
+ public void testConvertMethod() {
+ Color sRgb = Color.valueOf(1.0f, 0.5f, 0.0f);
+ Color adobeRgb = sRgb.convert(ColorSpace.get(Named.ADOBE_RGB));
+
+ assertEquals(ColorSpace.get(Named.ADOBE_RGB), adobeRgb.getColorSpace());
+ assertEquals(0.8912f, adobeRgb.red(), 0.001f);
+ assertEquals(0.4962f, adobeRgb.green(), 0.001f);
+ assertEquals(0.1164f, adobeRgb.blue(), 0.001f);
+ }
+
+ @Test
+ public void testConvertColorInt() {
+ long color = convert(0xffff8000, ColorSpace.get(Named.ADOBE_RGB));
+
+ assertEquals(ColorSpace.get(Named.ADOBE_RGB), colorSpace(color));
+ assertEquals(0.8912f, red(color), 0.01f);
+ assertEquals(0.4962f, green(color), 0.01f);
+ assertEquals(0.1164f, blue(color), 0.01f);
+ }
+
+ @Test
+ public void testConvertColorLong() {
+ long color = convert(pack(1.0f, 0.5f, 0.0f, 1.0f, ColorSpace.get(Named.DISPLAY_P3)),
+ ColorSpace.get(Named.ADOBE_RGB));
+
+ assertEquals(0.9499f, red(color), 0.01f);
+ assertEquals(0.4597f, green(color), 0.01f);
+ assertEquals(0.0000f, blue(color), 0.01f);
+ }
+
+ @Test
+ public void testConvertConnector() {
+ ColorSpace p3 = ColorSpace.get(Named.DISPLAY_P3);
+ ColorSpace.Connector connector = ColorSpace.connect(p3, ColorSpace.get(Named.ADOBE_RGB));
+ long color = convert(pack(1.0f, 0.5f, 0.0f, 1.0f, p3), connector);
+
+ assertEquals(0.9499f, red(color), 0.01f);
+ assertEquals(0.4597f, green(color), 0.01f);
+ assertEquals(0.0000f, blue(color), 0.01f);
+
+ color = convert(1.0f, 0.5f, 0.0f, 1.0f, connector);
+
+ assertEquals(0.9499f, red(color), 0.01f);
+ assertEquals(0.4597f, green(color), 0.01f);
+ assertEquals(0.0000f, blue(color), 0.01f);
+ }
+
+ @Test
+ public void testConvert() {
+ long color = convert(1.0f, 0.5f, 0.0f, 1.0f,
+ ColorSpace.get(Named.DISPLAY_P3), ColorSpace.get(Named.ADOBE_RGB));
+
+ assertEquals(0.9499f, red(color), 0.01f);
+ assertEquals(0.4597f, green(color), 0.01f);
+ assertEquals(0.0000f, blue(color), 0.01f);
+ }
+}
diff --git a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
index 15a3a43..4916ffa 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
@@ -900,21 +900,6 @@
}
@Test
- public void testSetGetFontVariationSettings() {
- Paint p = new Paint();
-
- // The default variation settings should be null.
- assertNull(p.getFontVariationSettings());
-
- final String settings = "'wdth' 1.0";
- p.setFontVariationSettings(settings);
- assertEquals(settings, p.getFontVariationSettings());
-
- p.setFontVariationSettings("");
- assertNull(p.getFontVariationSettings());
- }
-
- @Test
public void testGetTextBounds() {
Paint p = new Paint();
p.setTextSize(10);
diff --git a/tests/tests/keystore/Android.mk b/tests/tests/keystore/Android.mk
index 68828ac..ae1b804 100644
--- a/tests/tests/keystore/Android.mk
+++ b/tests/tests/keystore/Android.mk
@@ -30,7 +30,9 @@
core-tests-support \
compatibility-device-util \
ctstestrunner \
- guava
+ guava \
+ junit \
+ legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/keystore/src/android/keystore/cts/AuthorizationList.java b/tests/tests/keystore/src/android/keystore/cts/AuthorizationList.java
index d488b26..f3ee3e4 100644
--- a/tests/tests/keystore/src/android/keystore/cts/AuthorizationList.java
+++ b/tests/tests/keystore/src/android/keystore/cts/AuthorizationList.java
@@ -79,6 +79,8 @@
public static final int KM_PURPOSE_DECRYPT = 1;
public static final int KM_PURPOSE_SIGN = 2;
public static final int KM_PURPOSE_VERIFY = 3;
+ public static final int KM_PURPOSE_DERIVE_KEY = 4;
+ public static final int KM_PURPOSE_WRAP_KEY = 5;
// User authenticators.
public static final int HW_AUTH_PASSWORD = 1 << 0;
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index 4c27d0e..98f85e7 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -29,6 +29,7 @@
import static android.keystore.cts.AuthorizationList.KM_PURPOSE_ENCRYPT;
import static android.keystore.cts.AuthorizationList.KM_PURPOSE_SIGN;
import static android.keystore.cts.AuthorizationList.KM_PURPOSE_VERIFY;
+import static android.keystore.cts.AuthorizationList.KM_PURPOSE_WRAP_KEY;
import static android.keystore.cts.RootOfTrust.KM_VERIFIED_BOOT_VERIFIED;
import static android.security.keystore.KeyProperties.DIGEST_NONE;
import static android.security.keystore.KeyProperties.DIGEST_SHA256;
@@ -42,6 +43,7 @@
import static android.security.keystore.KeyProperties.PURPOSE_ENCRYPT;
import static android.security.keystore.KeyProperties.PURPOSE_SIGN;
import static android.security.keystore.KeyProperties.PURPOSE_VERIFY;
+import static android.security.keystore.KeyProperties.PURPOSE_WRAP_KEY;
import static android.security.keystore.KeyProperties.SIGNATURE_PADDING_RSA_PKCS1;
import static android.security.keystore.KeyProperties.SIGNATURE_PADDING_RSA_PSS;
import static org.hamcrest.CoreMatchers.is;
@@ -220,6 +222,7 @@
int[] purposes = {
PURPOSE_SIGN | PURPOSE_VERIFY,
PURPOSE_ENCRYPT | PURPOSE_DECRYPT,
+ PURPOSE_WRAP_KEY,
};
String[][] encryptionPaddingModes = {
{
@@ -249,13 +252,18 @@
},
};
+ // We don't allow the caller to specify padding mode for wrapping.
+ String[][] wrapKeyPaddingModes = {};
+
for (int keySize : keySizes) {
for (byte[] challenge : challenges) {
for (int purpose : purposes) {
if (isEncryptionPurpose(purpose)) {
testRsaAttestations(keySize, challenge, purpose, encryptionPaddingModes);
- } else {
+ } else if (isSignaturePurpose(purpose)){
testRsaAttestations(keySize, challenge, purpose, signaturePaddingModes);
+ } else {
+ testRsaAttestations(keySize, challenge, purpose, wrapKeyPaddingModes);
}
}
}
@@ -378,6 +386,7 @@
// Because we sometimes set "no padding", allow non-randomized encryption.
builder.setRandomizedEncryptionRequired(false);
}
+
if (isSignaturePurpose(purposes)) {
builder.setSignaturePaddings(paddingModes);
}
@@ -413,6 +422,10 @@
expectedKeyUsage[KEY_USAGE_KEY_ENCIPHERMENT_BIT_OFFSET] = true;
expectedKeyUsage[KEY_USAGE_DATA_ENCIPHERMENT_BIT_OFFSET] = true;
}
+ if (isWrapPurpose(purposes)) {
+ expectedKeyUsage[KEY_USAGE_KEY_ENCIPHERMENT_BIT_OFFSET] = true;
+ expectedKeyUsage[KEY_USAGE_DATA_ENCIPHERMENT_BIT_OFFSET] = false;
+ }
assertThat(attestationCert.getKeyUsage(), is(expectedKeyUsage));
}
@@ -803,6 +816,10 @@
return (purposes & PURPOSE_SIGN) != 0 || (purposes & PURPOSE_VERIFY) != 0;
}
+ private boolean isWrapPurpose(int purposes) {
+ return (purposes & PURPOSE_WRAP_KEY) != 0;
+ }
+
private ImmutableSet<Integer> buildPurposeSet(int purposes) {
ImmutableSet.Builder<Integer> builder = ImmutableSet.builder();
if ((purposes & PURPOSE_SIGN) != 0)
@@ -813,6 +830,8 @@
builder.add(KM_PURPOSE_ENCRYPT);
if ((purposes & PURPOSE_DECRYPT) != 0)
builder.add(KM_PURPOSE_DECRYPT);
+ if ((purposes & PURPOSE_WRAP_KEY) != 0)
+ builder.add(KM_PURPOSE_WRAP_KEY);
return builder.build();
}
diff --git a/tests/tests/libcorefileio/Android.mk b/tests/tests/libcorefileio/Android.mk
index 29226bf..d329c9d 100644
--- a/tests/tests/libcorefileio/Android.mk
+++ b/tests/tests/libcorefileio/Android.mk
@@ -21,7 +21,7 @@
# and when built explicitly put it in the data partition
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner junit legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/location/src/android/location/cts/LocationManagerTest.java b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
index f829bf3..3af213e 100644
--- a/tests/tests/location/src/android/location/cts/LocationManagerTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
@@ -994,16 +994,24 @@
}
/**
- * Test case for bug 33091107.
+ * Test case for bug 33091107, where a malicious app used to be able to fool a real provider
+ * into providing a mock location that isn't marked as being mock.
*/
public void testLocationShouldStillBeMarkedMockWhenProvidersDoNotMatch()
- throws InterruptedException {
+ throws InterruptedException {
double latitude = 20;
double longitude = 40;
- // Register a listener for the location we are about to set.
- updateLocationAndWait(
- TEST_MOCK_PROVIDER_NAME, LocationManager.GPS_PROVIDER, latitude, longitude);
+ List<String> providers = mManager.getAllProviders();
+ if (providers.isEmpty()) {
+ // Device doesn't have any providers. Can't perform this test, and no need to do so:
+ // no providers that malicious app could fool
+ return;
+ }
+ String realProviderToFool = providers.get(0);
+
+ // Register for location updates, then set a mock location and ensure it is marked "mock"
+ updateLocationAndWait(TEST_MOCK_PROVIDER_NAME, realProviderToFool, latitude, longitude);
}
@UiThreadTest
diff --git a/tests/tests/location2/Android.mk b/tests/tests/location2/Android.mk
index 4273444..a081213 100644
--- a/tests/tests/location2/Android.mk
+++ b/tests/tests/location2/Android.mk
@@ -24,7 +24,7 @@
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner junit legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index 1886093..92f4755 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -44,7 +44,14 @@
# include both the 32 and 64 bit versions
LOCAL_MULTILIB := both
-LOCAL_STATIC_JAVA_LIBRARIES := ctsmediautil compatibility-device-util ctstestserver ctstestrunner ndkaudio android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctsmediautil \
+ compatibility-device-util \
+ ctstestserver \
+ ctstestrunner \
+ ndkaudio \
+ junit \
+ legacy-android-test
LOCAL_JNI_SHARED_LIBRARIES := libctsmediacodec_jni libaudio_jni libnativehelper_compat_libc++ libndkaudioLib
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index 700ecfd..2801dd0 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -30,8 +30,9 @@
import android.media.MediaRecorder.OnErrorListener;
import android.media.MediaRecorder.OnInfoListener;
import android.media.MediaMetadataRetriever;
-import android.os.Environment;
import android.os.ConditionVariable;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
import android.support.test.filters.SmallTest;
import android.platform.test.annotations.RequiresDevice;
import android.test.ActivityInstrumentationTestCase2;
@@ -45,8 +46,11 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
import java.lang.InterruptedException;
import java.lang.Runnable;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -90,10 +94,14 @@
private File mOutFile2;
private Camera mCamera;
private MediaStubActivity mActivity = null;
+ private int mFileIndex;
private MediaRecorder mMediaRecorder;
private ConditionVariable mMaxDurationCond;
private ConditionVariable mMaxFileSizeCond;
+ private ConditionVariable mMaxFileSizeApproachingCond;
+ private ConditionVariable mNextOutputFileStartedCond;
+ private boolean mExpectMaxFileCond;
public MediaRecorderTest() {
super("android.media.cts", MediaStubActivity.class);
@@ -129,9 +137,13 @@
mMediaRecorder = new MediaRecorder();
mOutFile = new File(OUTPUT_PATH);
mOutFile2 = new File(OUTPUT_PATH2);
+ mFileIndex = 0;
mMaxDurationCond = new ConditionVariable();
mMaxFileSizeCond = new ConditionVariable();
+ mMaxFileSizeApproachingCond = new ConditionVariable();
+ mNextOutputFileStartedCond = new ConditionVariable();
+ mExpectMaxFileCond = true;
mMediaRecorder.setOutputFile(OUTPUT_PATH);
mMediaRecorder.setOnInfoListener(new OnInfoListener() {
@@ -178,6 +190,10 @@
mMaxDurationCond = null;
mMaxFileSizeCond.close();
mMaxFileSizeCond = null;
+ mMaxFileSizeApproachingCond.close();
+ mMaxFileSizeApproachingCond = null;
+ mNextOutputFileStartedCond.close();
+ mNextOutputFileStartedCond = null;
mActivity = null;
super.tearDown();
}
@@ -593,10 +609,163 @@
checkOutputFileSize(OUTPUT_PATH, fileSize, tolerance);
}
+ public void testRecordExceedFileSizeLimit() throws Exception {
+ if (!hasMicrophone() || !hasCamera() || !hasAmrNb() || !hasH264()) {
+ MediaUtils.skipTest("no microphone, camera, or codecs");
+ return;
+ }
+ long fileSize = 128 * 1024;
+ long tolerance = 50 * 1024;
+ List<String> recordFileList = new ArrayList<String>();
+ mFileIndex = 0;
+
+ // Override the handler in setup for test.
+ mMediaRecorder.setOnInfoListener(new OnInfoListener() {
+ public void onInfo(MediaRecorder mr, int what, int extra) {
+ mOnInfoCalled = true;
+ if (what ==
+ MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
+ Log.v(TAG, "max duration reached");
+ mMaxDurationCond.open();
+ } else if (what ==
+ MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
+ if (!mExpectMaxFileCond) {
+ fail("Do not expect MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED");
+ } else {
+ Log.v(TAG, "max file size reached");
+ mMaxFileSizeCond.open();
+ }
+ } else if (what ==
+ MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING) {
+ Log.v(TAG, "max file size is approaching");
+ mMaxFileSizeApproachingCond.open();
+
+ // Test passing a read-only FileDescriptor and expect IOException.
+ if (mFileIndex == 1) {
+ RandomAccessFile file = null;
+ try {
+ String path = OUTPUT_PATH + '0';
+ file = new RandomAccessFile(path, "r");
+ mMediaRecorder.setNextOutputFile(file.getFD());
+ fail("Expect IOException.");
+ } catch (IOException e) {
+ // Expected.
+ } finally {
+ try {
+ file.close();
+ } catch (IOException e) {
+ fail("Fail to close file");
+ }
+ }
+ }
+
+ // Test passing a read-only FileDescriptor and expect IOException.
+ if (mFileIndex == 2) {
+ ParcelFileDescriptor out = null;
+ String path = null;
+ try {
+ path = OUTPUT_PATH + '0';
+ out = ParcelFileDescriptor.open(new File(path),
+ ParcelFileDescriptor.MODE_READ_ONLY | ParcelFileDescriptor.MODE_CREATE);
+ mMediaRecorder.setNextOutputFile(out.getFileDescriptor());
+ fail("Expect IOException.");
+ } catch (IOException e) {
+ // Expected.
+ } finally {
+ try {
+ out.close();
+ } catch (IOException e) {
+ fail("Fail to close file");
+ }
+ }
+ }
+
+ // Test passing a write-only FileDescriptor and expect IOException.
+ if (mFileIndex == 3) {
+ ParcelFileDescriptor out = null;
+ String path = null;
+ try {
+ path = OUTPUT_PATH + '9';
+ out = ParcelFileDescriptor.open(new File(path),
+ ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE);
+ mMediaRecorder.setNextOutputFile(out.getFileDescriptor());
+ fail("Expect IOException.");
+ } catch (IOException e) {
+ // Expected.
+ } finally {
+ try {
+ out.close();
+ new File(path).delete();
+ } catch (IOException e) {
+ fail("Fail to close file");
+ }
+ }
+ }
+
+ // only record 5 files.
+ if (mFileIndex < 6) {
+ try {
+ String path = OUTPUT_PATH + mFileIndex;
+ mMediaRecorder.setNextOutputFile(path);
+ recordFileList.add(path);
+ mFileIndex++;
+ } catch (IOException e) {
+ fail("Fail to set next output file error: " + e);
+ }
+ }
+ } else if (what ==
+ MediaRecorder.MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED) {
+ Log.v(TAG, "Next output file started");
+ mNextOutputFileStartedCond.open();
+ }
+ }
+ });
+ mExpectMaxFileCond = false;
+ mMediaRecorder.setOutputFile(OUTPUT_PATH + mFileIndex);
+ recordFileList.add(OUTPUT_PATH + mFileIndex);
+ mFileIndex++;
+ mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+ mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
+ mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
+ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+ mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
+ mMediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
+ mMediaRecorder.setVideoEncodingBitRate(256000);
+ mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
+ mMediaRecorder.setMaxFileSize(fileSize);
+ mMediaRecorder.prepare();
+ mMediaRecorder.start();
+
+ // Record total 5 files including previous one.
+ int fileCount = 0;
+ while (fileCount < 5) {
+ if (!mMaxFileSizeApproachingCond.block(MAX_FILE_SIZE_TIMEOUT_MS)) {
+ fail("timed out waiting for MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING");
+ }
+ if (!mNextOutputFileStartedCond.block(MAX_FILE_SIZE_TIMEOUT_MS)) {
+ fail("timed out waiting for MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED");
+ }
+ fileCount++;
+ mMaxFileSizeApproachingCond.close();
+ mNextOutputFileStartedCond.close();
+ }
+
+ mExpectMaxFileCond = true;
+ if (!mMaxFileSizeCond.block(MAX_FILE_SIZE_TIMEOUT_MS)) {
+ fail("timed out waiting for MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED");
+ }
+ mMediaRecorder.stop();
+
+ for (String filePath : recordFileList) {
+ checkOutputFileSize(filePath, fileSize, tolerance);
+ }
+ }
+
private void checkOutputFileSize(final String fileName, long fileSize, long tolerance) {
- assertTrue(mOutFile.exists());
- assertEquals(fileSize, mOutFile.length(), tolerance);
- assertTrue(mOutFile.delete());
+ File file = new File(fileName);
+ assertTrue(file.exists());
+ assertEquals(fileSize, file.length(), tolerance);
+ assertTrue(file.delete());
}
public void testOnErrorListener() throws Exception {
diff --git a/tests/tests/net/Android.mk b/tests/tests/net/Android.mk
index 4a77640..98cde9b 100644
--- a/tests/tests/net/Android.mk
+++ b/tests/tests/net/Android.mk
@@ -34,8 +34,14 @@
LOCAL_PACKAGE_NAME := CtsNetTestCases
-LOCAL_STATIC_JAVA_LIBRARIES := core-tests-support compatibility-device-util \
- ctstestrunner ctstestserver mockwebserver
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ core-tests-support \
+ compatibility-device-util \
+ ctstestrunner \
+ ctstestserver \
+ mockwebserver \
+ junit \
+ legacy-android-test
# uncomment when b/13249961 is fixed
#LOCAL_SDK_VERSION := current
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
index 897e5cf..dcedb18 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -22,7 +22,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.location.LocationManager;
import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
@@ -35,6 +34,8 @@
import android.test.AndroidTestCase;
import android.util.Log;
+import com.android.compatibility.common.util.WifiConfigCreator;
+
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashSet;
@@ -68,6 +69,8 @@
private static final String TAG = "WifiManagerTest";
private static final String SSID1 = "\"WifiManagerTest\"";
private static final String SSID2 = "\"WifiManagerTestModified\"";
+ private static final String PROXY_TEST_SSID = "SomeProxyAp";
+ private static final String ADD_NETWORK_EXCEPTION_SUBSTR = "addNetwork";
private static final int TIMEOUT_MSEC = 6000;
private static final int WAIT_MSEC = 60;
private static final int DURATION = 10000;
@@ -75,6 +78,8 @@
private static final int WIFI_SCAN_TEST_CACHE_DELAY_MILLIS = 3 * 60 * 1000;
private static final int WIFI_SCAN_TEST_ITERATIONS = 5;
+ private static final String TEST_PAC_URL = "http://www.example.com/proxy.pac";
+
private IntentFilter mIntentFilter;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -405,6 +410,31 @@
}
}
+ /**
+ * Verifies that addNetwork() fails for WifiConfigurations containing a non-null http proxy when
+ * the caller doesn't have OVERRIDE_WIFI_CONFIG permission, DeviceOwner or ProfileOwner device
+ * management policies
+ */
+ public void testSetHttpProxy_PermissionFail() throws Exception {
+ if (!WifiFeature.isWifiSupported(getContext())) {
+ // skip the test if WiFi is not supported
+ return;
+ }
+ WifiConfigCreator configCreator = new WifiConfigCreator(getContext());
+ boolean exceptionThrown = false;
+ try {
+ configCreator.addHttpProxyNetworkVerifyAndRemove(
+ PROXY_TEST_SSID, TEST_PAC_URL);
+ } catch (IllegalStateException e) {
+ // addHttpProxyNetworkVerifyAndRemove throws three IllegalStateException,
+ // expect it to throw for the addNetwork operation
+ if (e.getMessage().contains(ADD_NETWORK_EXCEPTION_SUBSTR)) {
+ exceptionThrown = true;
+ }
+ }
+ assertTrue(exceptionThrown);
+ }
+
private Set<String> getEnabledNetworks(List<WifiConfiguration> configuredNetworks) {
Set<String> ssids = new HashSet<String>();
for (WifiConfiguration wifiConfig : configuredNetworks) {
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index 7effcab..c99e236 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -25,7 +25,12 @@
LOCAL_MULTILIB := both
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test compatibility-device-util ctstestrunner guava platform-test-annotations
+ android-support-test \
+ compatibility-device-util \
+ ctstestrunner \
+ guava \
+ junit \
+ legacy-android-test
LOCAL_JNI_SHARED_LIBRARIES := libcts_jni libctsos_jni libnativehelper_compat_libc++
diff --git a/tests/tests/os/jni/Android.mk b/tests/tests/os/jni/Android.mk
index 1e4eb25..bcb9d6f 100644
--- a/tests/tests/os/jni/Android.mk
+++ b/tests/tests/os/jni/Android.mk
@@ -30,6 +30,15 @@
android_os_cts_NoExecutePermissionTest.cpp \
android_os_cts_SeccompTest.cpp
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+
+LOCAL_SHARED_LIBRARIES := libnativehelper_compat_libc++ liblog libdl
+LOCAL_CXX_STL := none
+
+LOCAL_SRC_FILES += android_os_cts_CpuFeatures.cpp
+LOCAL_C_INCLUDES += ndk/sources/cpufeatures
+LOCAL_STATIC_LIBRARIES := cpufeatures libc++_static libminijail
+
# Select the architectures on which seccomp-bpf are supported. This is used to
# include extra test files that will not compile on architectures where it is
# not supported.
@@ -42,19 +51,10 @@
endif
ifeq ($(ARCH_SUPPORTS_SECCOMP),1)
- LOCAL_SRC_FILES += seccomp-tests/tests/seccomp_bpf_tests.c
+ LOCAL_STATIC_LIBRARIES += external_seccomp_tests
# This define controls the behavior of OSFeatures.needsSeccompSupport().
LOCAL_CFLAGS += -DARCH_SUPPORTS_SECCOMP
endif
-LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
-
-LOCAL_SHARED_LIBRARIES := libnativehelper_compat_libc++ liblog libdl
-LOCAL_CXX_STL := none
-
-LOCAL_SRC_FILES += android_os_cts_CpuFeatures.cpp
-LOCAL_C_INCLUDES += ndk/sources/cpufeatures
-LOCAL_STATIC_LIBRARIES := cpufeatures libc++_static libminijail
-
include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/os/jni/android_os_cts_SeccompTest.cpp b/tests/tests/os/jni/android_os_cts_SeccompTest.cpp
index 44cfc7e..b853c96 100644
--- a/tests/tests/os/jni/android_os_cts_SeccompTest.cpp
+++ b/tests/tests/os/jni/android_os_cts_SeccompTest.cpp
@@ -21,15 +21,9 @@
#if defined(ARCH_SUPPORTS_SECCOMP)
#include <libminijail.h>
+#include <seccomp_bpf_tests.h>
#endif
-#include "seccomp-tests/tests/test_harness.h"
-
-// Forward declare from seccomp_bpf_tests.c.
-extern "C" {
-struct __test_metadata* get_seccomp_test_list();
-}
-
static const char TAG[] = "SeccompBpfTest-Native";
jboolean android_security_cts_SeccompBpfTest_runKernelUnitTest(
@@ -57,6 +51,7 @@
return false;
#else
minijail* j = minijail_new();
+ minijail_no_new_privs(j);
minijail_use_seccomp_filter(j);
minijail_set_seccomp_filter_tsync(j);
minijail_parse_seccomp_filters_from_fd(j, policyFd);
diff --git a/tests/tests/os/jni/seccomp-tests/LICENSE b/tests/tests/os/jni/seccomp-tests/LICENSE
deleted file mode 100644
index b9e779f..0000000
--- a/tests/tests/os/jni/seccomp-tests/LICENSE
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tests/tests/os/jni/seccomp-tests/README b/tests/tests/os/jni/seccomp-tests/README
deleted file mode 100644
index c8cd2ad..0000000
--- a/tests/tests/os/jni/seccomp-tests/README
+++ /dev/null
@@ -1,4 +0,0 @@
-seccomp
--------
-
-Landing place for code relating to seccomp_filter work for the Linux kernel.
diff --git a/tests/tests/os/jni/seccomp-tests/README.android b/tests/tests/os/jni/seccomp-tests/README.android
deleted file mode 100644
index cad678a..0000000
--- a/tests/tests/os/jni/seccomp-tests/README.android
+++ /dev/null
@@ -1,22 +0,0 @@
-This is the kernel unittest for seccomp-bpf sandboxing.
-
-URL: https://github.com/redpig/seccomp
-Revision: e65c79a14dc2bbb6d8dbf12ebf71905e2253a4b2
-License: BSD
-
-Local modifications:
-- Remove usage of pthread_cancel()
-- Use __android_log_print() instead of fprintf()
-- Rename main() to seccomp_test_main()
-- Add get_seccomp_test_list()
-- Backport TEST(syscall_restart) from upstream
-- Add parentheses in macro IS_SECCOMP_EVENT(status),
- __TEST_IMPL, __TEST_F_IMPL, __EXPECT_STR, and _CONSTRUCTOR_ORDER_BACKWARD.
-
-The diff of modifications can be found in local-modifications-android.diff
-and local-modifications-android-2.diff. Note, the TEST(syscall_restart)
-backport is the only change in local-modifications-android-2.diff
-
-Additional modification is to backport fixes for Android Native Bridge:
-https://patchwork.kernel.org/patch/7537891/. This is not found in the above
-diff file. The patch is located in local-modifications-strict-args-fd88d16.diff.
diff --git a/tests/tests/os/jni/seccomp-tests/local-modifications-android-2.diff b/tests/tests/os/jni/seccomp-tests/local-modifications-android-2.diff
deleted file mode 100644
index b56fc359..0000000
--- a/tests/tests/os/jni/seccomp-tests/local-modifications-android-2.diff
+++ /dev/null
@@ -1,153 +0,0 @@
-diff --git a/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c b/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
-index 3c238e6..7481dd2 100644
---- a/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
-+++ b/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
-@@ -19,7 +19,6 @@
- #include <linux/prctl.h>
- #include <linux/ptrace.h>
- #include <linux/seccomp.h>
--#include <poll.h>
- #include <pthread.h>
- #include <semaphore.h>
- #include <signal.h>
-@@ -28,6 +27,7 @@
- #include <string.h>
- #include <linux/elf.h>
- #include <sys/uio.h>
-+#include <sys/utsname.h>
- #include <fcntl.h> // ANDROID
- #include <sys/mman.h>
- #include <sys/times.h>
-@@ -1798,7 +1798,8 @@ TEST_F(TSYNC, two_siblings_not_under_filter) {
- }
-
- /* Make sure restarted syscalls are seen directly as "restart_syscall". */
--TEST(syscall_restart) {
-+TEST(syscall_restart)
-+{
- long ret;
- unsigned long msg;
- pid_t child_pid;
-@@ -1815,20 +1816,25 @@ TEST(syscall_restart) {
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 5, 0),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_exit, 4, 0),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_rt_sigreturn, 3, 0),
-- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_poll, 4, 0),
-+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_nanosleep, 4, 0),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_restart_syscall, 4, 0),
-
- /* Allow __NR_write for easy logging. */
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_write, 0, 1),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
-- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE|0x100), /* poll */
-- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE|0x200), /* restart */
-+ /* The nanosleep jump target. */
-+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE|0x100),
-+ /* The restart_syscall jump target. */
-+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE|0x200),
- };
- struct sock_fprog prog = {
-- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
-+ .len = (unsigned short) (sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
-+#if defined(__arm__)
-+ struct utsname utsbuf;
-+#endif
-
- ASSERT_EQ(0, pipe(pipefd));
-
-@@ -1837,10 +1843,7 @@ TEST(syscall_restart) {
- if (child_pid == 0) {
- /* Child uses EXPECT not ASSERT to deliver status correctly. */
- char buf = ' ';
-- struct pollfd fds = {
-- .fd = pipefd[0],
-- .events = POLLIN,
-- };
-+ struct timespec timeout = { };
-
- /* Attach parent as tracer and stop. */
- EXPECT_EQ(0, ptrace(PTRACE_TRACEME));
-@@ -1864,10 +1867,11 @@ TEST(syscall_restart) {
- TH_LOG("Failed to get sync data from read()");
- }
-
-- /* Start poll to be interrupted. */
-+ /* Start nanosleep to be interrupted. */
-+ timeout.tv_sec = 1;
- errno = 0;
-- EXPECT_EQ(1, poll(&fds, 1, -1)) {
-- TH_LOG("Call to poll() failed (errno %d)", errno);
-+ EXPECT_EQ(0, nanosleep(&timeout, NULL)) {
-+ TH_LOG("Call to nanosleep() failed (errno %d)", errno);
- }
-
- /* Read final sync from parent. */
-@@ -1892,14 +1896,14 @@ TEST(syscall_restart) {
- ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
- ASSERT_EQ(1, write(pipefd[1], ".", 1));
-
-- /* Wait for poll() to start. */
-+ /* Wait for nanosleep() to start. */
- ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
- ASSERT_EQ(true, WIFSTOPPED(status));
- ASSERT_EQ(SIGTRAP, WSTOPSIG(status));
- ASSERT_EQ(PTRACE_EVENT_SECCOMP, (status >> 16));
- ASSERT_EQ(0, ptrace(PTRACE_GETEVENTMSG, child_pid, NULL, &msg));
- ASSERT_EQ(0x100, msg);
-- EXPECT_EQ(__NR_poll, get_syscall(_metadata, child_pid));
-+ EXPECT_EQ(__NR_nanosleep, get_syscall(_metadata, child_pid));
-
- /* Might as well check siginfo for sanity while we're here. */
- ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &info));
-@@ -1910,7 +1914,7 @@ TEST(syscall_restart) {
- /* Verify signal delivery came from child (seccomp-triggered). */
- EXPECT_EQ(child_pid, info.si_pid);
-
-- /* Interrupt poll with SIGSTOP (which we'll need to handle). */
-+ /* Interrupt nanosleep with SIGSTOP (which we'll need to handle). */
- ASSERT_EQ(0, kill(child_pid, SIGSTOP));
- ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
- ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
-@@ -1920,7 +1924,7 @@ TEST(syscall_restart) {
- ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &info));
- EXPECT_EQ(getpid(), info.si_pid);
-
-- /* Restart poll with SIGCONT, which triggers restart_syscall. */
-+ /* Restart nanosleep with SIGCONT, which triggers restart_syscall. */
- ASSERT_EQ(0, kill(child_pid, SIGCONT));
- ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
- ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
-@@ -1934,16 +1938,25 @@ TEST(syscall_restart) {
- ASSERT_EQ(SIGTRAP, WSTOPSIG(status));
- ASSERT_EQ(PTRACE_EVENT_SECCOMP, (status >> 16));
- ASSERT_EQ(0, ptrace(PTRACE_GETEVENTMSG, child_pid, NULL, &msg));
-+
- ASSERT_EQ(0x200, msg);
- ret = get_syscall(_metadata, child_pid);
- #if defined(__arm__)
-- /* FIXME: ARM does not expose true syscall in registers. */
-- EXPECT_EQ(__NR_poll, ret);
--#else
-- EXPECT_EQ(__NR_restart_syscall, ret);
-+ /*
-+ * FIXME:
-+ * - native ARM registers do NOT expose true syscall.
-+ * - compat ARM registers on ARM64 DO expose true syscall.
-+ */
-+ ASSERT_EQ(0, uname(&utsbuf));
-+ if (strncmp(utsbuf.machine, "arm", 3) == 0) {
-+ EXPECT_EQ(__NR_nanosleep, ret);
-+ } else
- #endif
-+ {
-+ EXPECT_EQ(__NR_restart_syscall, ret);
-+ }
-
-- /* Write again to end poll. */
-+ /* Write again to end test. */
- ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
- ASSERT_EQ(1, write(pipefd[1], "!", 1));
- EXPECT_EQ(0, close(pipefd[1]));
diff --git a/tests/tests/os/jni/seccomp-tests/local-modifications-android.diff b/tests/tests/os/jni/seccomp-tests/local-modifications-android.diff
deleted file mode 100644
index 288261a..0000000
--- a/tests/tests/os/jni/seccomp-tests/local-modifications-android.diff
+++ /dev/null
@@ -1,59 +0,0 @@
-diff --git a/tests/seccomp_bpf_tests.c b/tests/seccomp_bpf_tests.c
-index deb78d1..98b0231 100644
---- a/tests/seccomp_bpf_tests.c
-+++ b/tests/seccomp_bpf_tests.c
-@@ -1457,7 +1457,7 @@ FIXTURE_TEARDOWN(TSYNC) {
- if (!s->tid)
- continue;
- if (pthread_kill(s->tid, 0)) {
-- pthread_cancel(s->tid);
-+ //pthread_cancel(s->tid); // ANDROID
- pthread_join(s->tid, &status);
- }
- }
-@@ -1940,4 +1940,10 @@ TEST(syscall_restart) {
- * - ...
- */
-
-+// ANDROID:begin
-+struct __test_metadata* get_seccomp_test_list() {
-+ return __test_list;
-+}
-+// ANDROID:end
-+
- TEST_HARNESS_MAIN
-diff --git a/tests/test_harness.h b/tests/test_harness.h
-index 47ee027..597e40c 100644
---- a/tests/test_harness.h
-+++ b/tests/test_harness.h
-@@ -49,6 +49,8 @@
- #include <sys/wait.h>
- #include <unistd.h>
-
-+#include <android/log.h> // ANDROID
-+
- /* All exported functionality should be declared through this macro. */
- #define TEST_API(x) _##x
-
-@@ -206,9 +208,11 @@
- } while (0)
-
- /* Unconditional logger for internal use. */
-+// ANDROID:begin
- #define __TH_LOG(fmt, ...) \
-- fprintf(TH_LOG_STREAM, "%s:%d:%s:" fmt "\n", \
-+ __android_log_print(ANDROID_LOG_ERROR, "SeccompBpfTest-KernelUnit", "%s:%d:%s:" fmt "\n", \
- __FILE__, __LINE__, _metadata->name, ##__VA_ARGS__)
-+// ANDROID:end
-
- /* Defines the test function and creates the registration stub. */
- #define _TEST(test_name) __TEST_IMPL(test_name, -1)
-@@ -292,7 +296,7 @@
- if (!__constructor_order) \
- __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD; \
- } \
-- int main(int argc, char **argv) { return test_harness_run(argc, argv); }
-+ int seccomp_test_main(int argc, char **argv) { return test_harness_run(argc, argv); } // ANDROID
-
- #define _ASSERT_EQ(_expected, _seen) \
- __EXPECT(_expected, _seen, ==, 1)
diff --git a/tests/tests/os/jni/seccomp-tests/local-modifications-strict-args-fd88d16.diff b/tests/tests/os/jni/seccomp-tests/local-modifications-strict-args-fd88d16.diff
deleted file mode 100644
index a289147..0000000
--- a/tests/tests/os/jni/seccomp-tests/local-modifications-strict-args-fd88d16.diff
+++ /dev/null
@@ -1,102 +0,0 @@
-diff --git a/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c b/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
-index 98b0231..3c238e6 100644
---- a/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
-+++ b/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
-@@ -28,6 +28,9 @@
- #include <string.h>
- #include <linux/elf.h>
- #include <sys/uio.h>
-+#include <fcntl.h> // ANDROID
-+#include <sys/mman.h>
-+#include <sys/times.h>
-
- #define _GNU_SOURCE
- #include <unistd.h>
-@@ -386,14 +389,16 @@ TEST_SIGNAL(KILL_one, SIGSYS) {
- }
-
- TEST_SIGNAL(KILL_one_arg_one, SIGSYS) {
-+ void *fatal_address;
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
-- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
-+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_times, 1, 0),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- /* Only both with lower 32-bit for now. */
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_arg(0)),
-- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x0C0FFEE, 0, 1),
-+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K,
-+ (unsigned long)&fatal_address, 0, 1),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
-@@ -403,23 +408,29 @@ TEST_SIGNAL(KILL_one_arg_one, SIGSYS) {
- };
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
-- pid_t pid = getpid();
-+ struct tms timebuf;
-+ clock_t clock = times(&timebuf);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
- ASSERT_EQ(0, ret);
-
- EXPECT_EQ(parent, syscall(__NR_getppid));
-- EXPECT_EQ(pid, syscall(__NR_getpid));
-- /* getpid() should never return. */
-- EXPECT_EQ(0, syscall(__NR_getpid, 0x0C0FFEE));
-+ EXPECT_LE(clock, syscall(__NR_times, &timebuf));
-+ /* times() should never return. */
-+ EXPECT_EQ(0, syscall(__NR_times, &fatal_address));
- }
-
- TEST_SIGNAL(KILL_one_arg_six, SIGSYS) {
-+#ifndef __NR_mmap2
-+ int sysno = __NR_mmap;
-+#else
-+ int sysno = __NR_mmap2;
-+#endif
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
-- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
-+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, sysno, 1, 0),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- /* Only both with lower 32-bit for now. */
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_arg(5)),
-@@ -433,16 +444,29 @@ TEST_SIGNAL(KILL_one_arg_six, SIGSYS) {
- };
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
-- pid_t pid = getpid();
-+ int fd;
-+ void *map1, *map2;
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
- ASSERT_EQ(0, ret);
-
-+ fd = open("/dev/zero", O_RDONLY);
-+ ASSERT_NE(-1, fd);
-+
- EXPECT_EQ(parent, syscall(__NR_getppid));
-- EXPECT_EQ(pid, syscall(__NR_getpid));
-- /* getpid() should never return. */
-- EXPECT_EQ(0, syscall(__NR_getpid, 1, 2, 3, 4, 5, 0x0C0FFEE));
-+ map1 = (void *)syscall(sysno,
-+ NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, fd, PAGE_SIZE);
-+ EXPECT_NE(MAP_FAILED, map1);
-+ /* mmap2() should never return. */
-+ map2 = (void *)syscall(sysno,
-+ NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, fd, 0x0C0FFEE);
-+ EXPECT_EQ(MAP_FAILED, map2);
-+
-+ /* The test failed, so clean up the resources. */
-+ munmap(map1, PAGE_SIZE);
-+ munmap(map2, PAGE_SIZE);
-+ close(fd);
- }
-
- /* TODO(wad) add 64-bit versus 32-bit arg tests. */
diff --git a/tests/tests/os/jni/seccomp-tests/tests/.gitignore b/tests/tests/os/jni/seccomp-tests/tests/.gitignore
deleted file mode 100644
index cdfc7c6..0000000
--- a/tests/tests/os/jni/seccomp-tests/tests/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-sigsegv
-resumption
-seccomp_bpf_tests
diff --git a/tests/tests/os/jni/seccomp-tests/tests/Makefile b/tests/tests/os/jni/seccomp-tests/tests/Makefile
deleted file mode 100644
index 88994b3..0000000
--- a/tests/tests/os/jni/seccomp-tests/tests/Makefile
+++ /dev/null
@@ -1,23 +0,0 @@
-CFLAGS += -Wall
-EXEC=resumption seccomp_bpf_tests sigsegv
-
-all: $(EXEC)
-
-clean:
- rm -f $(EXEC)
-
-seccomp_bpf_tests: seccomp_bpf_tests.c test_harness.h
- $(CC) seccomp_bpf_tests.c -o seccomp_bpf_tests $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -pthread
-
-resumption: resumption.c test_harness.h
- $(CC) $^ -o $@ $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -ggdb3
-
-sigsegv: sigsegv.c test_harness.h
- $(CC) $^ -o $@ $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -ggdb3
-
-run_tests: $(EXEC)
- ./seccomp_bpf_tests
- ./resumption
- ./sigsegv
-
-.PHONY: clean run_tests
diff --git a/tests/tests/os/jni/seccomp-tests/tests/resumption.c b/tests/tests/os/jni/seccomp-tests/tests/resumption.c
deleted file mode 100644
index 41795c0..0000000
--- a/tests/tests/os/jni/seccomp-tests/tests/resumption.c
+++ /dev/null
@@ -1,217 +0,0 @@
-/* seccomp_bpf_tests.c
- * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- *
- * Test code for seccomp bpf.
- */
-
-#include <asm/siginfo.h>
-#define __have_siginfo_t 1
-#define __have_sigval_t 1
-#define __have_sigevent_t 1
-
-#include <linux/filter.h>
-#include <sys/prctl.h>
-#include <linux/prctl.h>
-#include <linux/seccomp.h>
-#include <stddef.h>
-#include <stdbool.h>
-#include <string.h>
-#include <syscall.h>
-#define __USE_GNU 1
-#include <sys/ucontext.h>
-#include <sys/mman.h>
-
-#include "test_harness.h"
-
-#ifndef PR_SET_NO_NEW_PRIVS
-#define PR_SET_NO_NEW_PRIVS 38
-#define PR_GET_NO_NEW_PRIVS 39
-#endif
-
-#if defined(__i386__)
-#define REG_IP REG_EIP
-#define REG_SP REG_ESP
-#define REG_RESULT REG_EAX
-#define REG_SYSCALL REG_EAX
-#define REG_ARG0 REG_EBX
-#define REG_ARG1 REG_ECX
-#define REG_ARG2 REG_EDX
-#define REG_ARG3 REG_ESI
-#define REG_ARG4 REG_EDI
-#define REG_ARG5 REG_EBP
-#elif defined(__x86_64__)
-#define REG_IP REG_RIP
-#define REG_SP REG_RSP
-#define REG_RESULT REG_RAX
-#define REG_SYSCALL REG_RAX
-#define REG_ARG0 REG_RDI
-#define REG_ARG1 REG_RSI
-#define REG_ARG2 REG_RDX
-#define REG_ARG3 REG_R10
-#define REG_ARG4 REG_R8
-#define REG_ARG5 REG_R9
-#endif
-
-FIXTURE_DATA(TRAP) {
- struct sock_fprog prog;
-};
-
-/* XXX: will need one per arch, etc.
- * thankfully _arch can tell us the calling convention!
- */
-extern void *thunk_ip; /* label for the instruction _after_ syscall */
-static void syscall_thunk(void)
-{
- asm("syscall; thunk_ip:");
-}
-
-static time_t vsyscall_time(time_t *p)
-{
- register time_t t asm ("rax");
- __attribute__((unused)) register time_t *p1 asm ("rdi") = p;
- __asm__("call 0xffffffffff600400 \n");
- return t;
-}
-
-
-#if 0
-/* For instance, we could jump here instead. */
-static void compat_thunk(void)
-{
- asm("int 0x80");
-}
-#endif
-
-FIXTURE_SETUP(TRAP) {
- /* instruction after the syscall. Will be arch specific, of course. */
- unsigned long thunk_addr = (unsigned long)&thunk_ip;
- TH_LOG("Thunk: 0x%lX\n", thunk_addr);
- {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- /* Whitelist anything you might need in the sigaction */
-#ifdef __NR_sigreturn
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_sigreturn, 3, 0),
-#endif
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_exit, 2, 0),
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_rt_sigreturn, 1, 0),
- /* Allow __NR_write so easy logging. */
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_write, 0, 1),
- BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
- /* Check if we're within the thunk. */
- BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
- offsetof(struct seccomp_data, instruction_pointer)),
- /* XXX: make this 32-bit friendly. */
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ((__u32*)&thunk_addr)[0], 0, 3),
- BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
- offsetof(struct seccomp_data, instruction_pointer)+sizeof(int)),
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ((__u32*)&thunk_addr)[1], 0, 1),
- BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
- BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP),
- };
- memset(&self->prog, 0, sizeof(self->prog));
- self->prog.filter = malloc(sizeof(filter));
- ASSERT_NE(NULL, self->prog.filter);
- memcpy(self->prog.filter, filter, sizeof(filter));
- self->prog.len = (unsigned short)(sizeof(filter)/sizeof(filter[0]));
- }
-}
-
-FIXTURE_TEARDOWN(TRAP) {
- if (self->prog.filter)
- free(self->prog.filter);
-};
-
-struct arch_sigsys {
- void *_call_addr; /* calling user insn */
- int _syscall; /* triggering system call number */
- unsigned int _arch; /* AUDIT_ARCH_* of syscall */
-};
-
-static void TRAP_action(int nr, siginfo_t *info, void *void_context)
-{
- ucontext_t *ctx = (ucontext_t *)void_context;
- char buf[256];
- int len;
- int do_ret = 1;
- struct arch_sigsys *sys = (struct arch_sigsys *)
-#ifdef si_syscall
- &(info->si_call_addr);
-#else
- &(info->si_pid);
-#endif
-
- if (info->si_code != SYS_SECCOMP)
- return;
- if (!ctx)
- return;
- len = snprintf(buf, sizeof(buf),
- "@0x%lX:%X:%d:0x%lX:0x%lX:0x%lX:0x%lX:0x%lX:0x%lX\n",
- (unsigned long)sys->_call_addr,
- sys->_arch,
- sys->_syscall,
- (unsigned long)ctx->uc_mcontext.gregs[REG_ARG0],
- (unsigned long)ctx->uc_mcontext.gregs[REG_ARG1],
- (unsigned long)ctx->uc_mcontext.gregs[REG_ARG2],
- (unsigned long)ctx->uc_mcontext.gregs[REG_ARG3],
- (unsigned long)ctx->uc_mcontext.gregs[REG_ARG4],
- (unsigned long)ctx->uc_mcontext.gregs[REG_ARG5]);
- /* Send the soft-fail to our "listener" */
- syscall(__NR_write, STDOUT_FILENO, buf, len);
- if (ctx->uc_mcontext.gregs[REG_IP] >= 0xffffffffff600000ULL &&
- ctx->uc_mcontext.gregs[REG_IP] < 0xffffffffff601000ULL)
- do_ret = 0;
- if (do_ret) {
- /* push [REG_IP] */
- ctx->uc_mcontext.gregs[REG_SP] -= sizeof(unsigned long);
- *((unsigned long *)ctx->uc_mcontext.gregs[REG_SP]) =
- ctx->uc_mcontext.gregs[REG_IP];
- }
- /* jmp syscall_thunk */
- ctx->uc_mcontext.gregs[REG_IP] = (unsigned long)syscall_thunk;
- return;
-}
-
-TEST_F(TRAP, handler) {
- int ret;
- struct sigaction act;
- pid_t pid;
- sigset_t mask;
- memset(&act, 0, sizeof(act));
- sigemptyset(&mask);
- sigaddset(&mask, SIGSYS);
-
- act.sa_sigaction = &TRAP_action;
- act.sa_flags = SA_SIGINFO;
- ret = sigaction(SIGSYS, &act, NULL);
- ASSERT_EQ(0, ret) {
- TH_LOG("sigaction failed");
- }
- ret = sigprocmask(SIG_UNBLOCK, &mask, NULL);
- ASSERT_EQ(0, ret) {
- TH_LOG("sigprocmask failed");
- }
-
- /* Get the pid to compare against. */
- pid = getpid();
-
- ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog);
- ASSERT_EQ(0, ret);
-
- /* Call anything! */
- ret = syscall(__NR_getpid);
- ASSERT_EQ(pid, ret);
- ret = syscall(__NR_close, 0);
- ASSERT_EQ(0, ret);
- ret = syscall(__NR_close, 0);
- ASSERT_EQ(-1, ret);
- printf("The time is %ld\n", vsyscall_time(NULL));
- ASSERT_LT(0, vsyscall_time(NULL));
-}
-
-TEST_HARNESS_MAIN
diff --git a/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c b/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
deleted file mode 100644
index a813361..0000000
--- a/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
+++ /dev/null
@@ -1,1986 +0,0 @@
-/* seccomp_bpf_tests.c
- * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- *
- * Test code for seccomp bpf.
- */
-
-#include <asm/siginfo.h>
-#define __have_siginfo_t 1
-#define __have_sigval_t 1
-#define __have_sigevent_t 1
-
-#include <errno.h>
-#include <linux/filter.h>
-#include <sys/prctl.h>
-#include <sys/ptrace.h>
-#include <sys/user.h>
-#include <linux/prctl.h>
-#include <linux/ptrace.h>
-#include <linux/seccomp.h>
-#include <pthread.h>
-#include <semaphore.h>
-#include <signal.h>
-#include <stddef.h>
-#include <stdbool.h>
-#include <string.h>
-#include <linux/elf.h>
-#include <sys/uio.h>
-#include <sys/utsname.h>
-#include <fcntl.h> // ANDROID
-#include <sys/mman.h>
-#include <sys/times.h>
-
-#define _GNU_SOURCE
-#include <unistd.h>
-#include <sys/syscall.h>
-
-#include "test_harness.h"
-
-#ifndef PR_SET_PTRACER
-# define PR_SET_PTRACER 0x59616d61
-#endif
-
-#ifndef PR_SET_NO_NEW_PRIVS
-#define PR_SET_NO_NEW_PRIVS 38
-#define PR_GET_NO_NEW_PRIVS 39
-#endif
-
-#ifndef PR_SECCOMP_EXT
-#define PR_SECCOMP_EXT 43
-#endif
-
-#ifndef SECCOMP_EXT_ACT
-#define SECCOMP_EXT_ACT 1
-#endif
-
-#ifndef SECCOMP_EXT_ACT_TSYNC
-#define SECCOMP_EXT_ACT_TSYNC 1
-#endif
-
-#ifndef SECCOMP_MODE_STRICT
-#define SECCOMP_MODE_STRICT 1
-#endif
-
-#ifndef SECCOMP_MODE_FILTER
-#define SECCOMP_MODE_FILTER 2
-#endif
-
-#ifndef SECCOMP_RET_KILL
-#define SECCOMP_RET_KILL 0x00000000U // kill the task immediately
-#define SECCOMP_RET_TRAP 0x00030000U // disallow and force a SIGSYS
-#define SECCOMP_RET_ERRNO 0x00050000U // returns an errno
-#define SECCOMP_RET_TRACE 0x7ff00000U // pass to a tracer or disallow
-#define SECCOMP_RET_ALLOW 0x7fff0000U // allow
-
-/* Masks for the return value sections. */
-#define SECCOMP_RET_ACTION 0x7fff0000U
-#define SECCOMP_RET_DATA 0x0000ffffU
-
-struct seccomp_data {
- int nr;
- __u32 arch;
- __u64 instruction_pointer;
- __u64 args[6];
-};
-#endif
-
-#define syscall_arg(_n) (offsetof(struct seccomp_data, args[_n]))
-
-#define SIBLING_EXIT_UNKILLED 0xbadbeef
-#define SIBLING_EXIT_FAILURE 0xbadface
-#define SIBLING_EXIT_NEWPRIVS 0xbadfeed
-
-TEST(mode_strict_support) {
- long ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, NULL, NULL, NULL);
- ASSERT_EQ(0, ret) {
- TH_LOG("Kernel does not support CONFIG_SECCOMP");
- }
- syscall(__NR_exit, 1);
-}
-
-TEST_SIGNAL(mode_strict_cannot_call_prctl, SIGKILL) {
- long ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, NULL, NULL, NULL);
- ASSERT_EQ(0, ret) {
- TH_LOG("Kernel does not support CONFIG_SECCOMP");
- }
- syscall(__NR_prctl, PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL, NULL, NULL);
- EXPECT_FALSE(true) {
- TH_LOG("Unreachable!");
- }
-}
-
-/* Note! This doesn't test no new privs behavior */
-TEST(no_new_privs_support) {
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- EXPECT_EQ(0, ret) {
- TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
- }
-}
-
-/* Tests kernel support by checking for a copy_from_user() fault on * NULL. */
-TEST(mode_filter_support) {
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0);
- ASSERT_EQ(0, ret) {
- TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
- }
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL, NULL, NULL);
- EXPECT_EQ(-1, ret);
- EXPECT_EQ(EFAULT, errno) {
- TH_LOG("Kernel does not support CONFIG_SECCOMP_FILTER!");
- }
-}
-
-TEST(mode_filter_without_nnp) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
- long ret = prctl(PR_GET_NO_NEW_PRIVS, 0, NULL, 0, 0);
- ASSERT_LE(0, ret) {
- TH_LOG("Expected 0 or unsupported for NO_NEW_PRIVS");
- }
- errno = 0;
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
- /* Succeeds with CAP_SYS_ADMIN, fails without */
- /* TODO(wad) check caps not euid */
- if (geteuid()) {
- EXPECT_EQ(-1, ret);
- EXPECT_EQ(EACCES, errno);
- } else {
- EXPECT_EQ(0, ret);
- }
-}
-
-#define MAX_INSNS_PER_PATH 32768
-
-TEST(filter_size_limits) {
- int i;
- int count = BPF_MAXINSNS + 1;
- struct sock_filter allow[] = {
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_filter *filter;
- struct sock_fprog prog = { };
-
- filter = calloc(count, sizeof(*filter));
- ASSERT_NE(NULL, filter);
-
- for (i = 0; i < count; i++) {
- filter[i] = allow[0];
- }
-
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- prog.filter = filter;
- prog.len = count;
-
- /* Too many filter instructions in a single filter. */
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
- ASSERT_NE(0, ret) {
- TH_LOG("Installing %d insn filter was allowed", prog.len);
- }
-
- /* One less is okay, though. */
- prog.len -= 1;
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
- ASSERT_EQ(0, ret) {
- TH_LOG("Installing %d insn filter wasn't allowed", prog.len);
- }
-}
-
-TEST(filter_chain_limits) {
- int i;
- int count = BPF_MAXINSNS;
- struct sock_filter allow[] = {
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_filter *filter;
- struct sock_fprog prog = { };
-
- filter = calloc(count, sizeof(*filter));
- ASSERT_NE(NULL, filter);
-
- for (i = 0; i < count; i++) {
- filter[i] = allow[0];
- }
-
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- prog.filter = filter;
- prog.len = 1;
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
- ASSERT_EQ(0, ret);
-
- prog.len = count;
-
- /* Too many total filter instructions. */
- for (i = 0; i < MAX_INSNS_PER_PATH; i++) {
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
- if (ret != 0)
- break;
- }
- ASSERT_NE(0, ret) {
- TH_LOG("Allowed %d %d-insn filters (total with penalties:%d)",
- i, count, i * (count + 4));
- }
-}
-
-TEST(mode_filter_cannot_move_to_strict) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
-
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, NULL, 0, 0);
- EXPECT_EQ(-1, ret);
- EXPECT_EQ(EINVAL, errno);
-}
-
-
-TEST(mode_filter_get_seccomp) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
-
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_GET_SECCOMP, 0, 0, 0, 0);
- EXPECT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_GET_SECCOMP, 0, 0, 0, 0);
- EXPECT_EQ(2, ret);
-}
-
-
-TEST(ALLOW_all) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
-
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
- ASSERT_EQ(0, ret);
-}
-
-TEST(empty_prog) {
- struct sock_filter filter[] = {
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
-
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
- EXPECT_EQ(-1, ret);
- EXPECT_EQ(EINVAL, errno);
-}
-
-TEST_SIGNAL(unknown_ret_is_kill_inside, SIGSYS) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_RET|BPF_K, 0x10000000U),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
-
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
- ASSERT_EQ(0, ret);
- EXPECT_EQ(0, syscall(__NR_getpid)) {
- TH_LOG("getpid() shouldn't ever return");
- }
-}
-
-/* return code >= 0x80000000 is unused. */
-TEST_SIGNAL(unknown_ret_is_kill_above_allow, SIGSYS) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_RET|BPF_K, 0x90000000U),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
-
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
- ASSERT_EQ(0, ret);
- EXPECT_EQ(0, syscall(__NR_getpid)) {
- TH_LOG("getpid() shouldn't ever return");
- }
-}
-
-TEST_SIGNAL(KILL_all, SIGSYS) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
-
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
- ASSERT_EQ(0, ret);
-}
-
-TEST_SIGNAL(KILL_one, SIGSYS) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 0, 1),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
- ASSERT_EQ(0, ret);
-
- EXPECT_EQ(parent, syscall(__NR_getppid));
- /* getpid() should never return. */
- EXPECT_EQ(0, syscall(__NR_getpid));
-}
-
-TEST_SIGNAL(KILL_one_arg_one, SIGSYS) {
- void *fatal_address;
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_times, 1, 0),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- /* Only both with lower 32-bit for now. */
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_arg(0)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K,
- (unsigned long)&fatal_address, 0, 1),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
- struct tms timebuf;
- clock_t clock = times(&timebuf);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
- ASSERT_EQ(0, ret);
-
- EXPECT_EQ(parent, syscall(__NR_getppid));
- EXPECT_LE(clock, syscall(__NR_times, &timebuf));
- /* times() should never return. */
- EXPECT_EQ(0, syscall(__NR_times, &fatal_address));
-}
-
-TEST_SIGNAL(KILL_one_arg_six, SIGSYS) {
-#ifndef __NR_mmap2
- int sysno = __NR_mmap;
-#else
- int sysno = __NR_mmap2;
-#endif
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, sysno, 1, 0),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- /* Only both with lower 32-bit for now. */
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_arg(5)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x0C0FFEE, 0, 1),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
- int fd;
- void *map1, *map2;
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
- ASSERT_EQ(0, ret);
-
- fd = open("/dev/zero", O_RDONLY);
- ASSERT_NE(-1, fd);
-
- EXPECT_EQ(parent, syscall(__NR_getppid));
- map1 = (void *)syscall(sysno,
- NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, fd, PAGE_SIZE);
- EXPECT_NE(MAP_FAILED, map1);
- /* mmap2() should never return. */
- map2 = (void *)syscall(sysno,
- NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, fd, 0x0C0FFEE);
- EXPECT_EQ(MAP_FAILED, map2);
-
- /* The test failed, so clean up the resources. */
- munmap(map1, PAGE_SIZE);
- munmap(map2, PAGE_SIZE);
- close(fd);
-}
-
-/* TODO(wad) add 64-bit versus 32-bit arg tests. */
-
-TEST(arg_out_of_range) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_arg(6)),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
- EXPECT_EQ(-1, ret);
- EXPECT_EQ(EINVAL, errno);
-}
-
-TEST(ERRNO_one) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | E2BIG),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
- ASSERT_EQ(0, ret);
-
- EXPECT_EQ(parent, syscall(__NR_getppid));
- EXPECT_EQ(-1, read(0, NULL, 0));
- EXPECT_EQ(E2BIG, errno);
-}
-
-TEST(ERRNO_one_ok) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | 0),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
- ASSERT_EQ(0, ret);
-
- EXPECT_EQ(parent, syscall(__NR_getppid));
- /* "errno" of 0 is ok. */
- EXPECT_EQ(0, read(0, NULL, 0));
-}
-
-FIXTURE_DATA(TRAP) {
- struct sock_fprog prog;
-};
-
-FIXTURE_SETUP(TRAP) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 0, 1),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- memset(&self->prog, 0, sizeof(self->prog));
- self->prog.filter = malloc(sizeof(filter));
- ASSERT_NE(NULL, self->prog.filter);
- memcpy(self->prog.filter, filter, sizeof(filter));
- self->prog.len = (unsigned short)(sizeof(filter)/sizeof(filter[0]));
-}
-
-FIXTURE_TEARDOWN(TRAP) {
- if (self->prog.filter)
- free(self->prog.filter);
-};
-
-TEST_F_SIGNAL(TRAP, dfl, SIGSYS) {
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog);
- ASSERT_EQ(0, ret);
- syscall(__NR_getpid);
-}
-
-/* Ensure that SIGSYS overrides SIG_IGN */
-TEST_F_SIGNAL(TRAP, ign, SIGSYS) {
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- signal(SIGSYS, SIG_IGN);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog);
- ASSERT_EQ(0, ret);
- syscall(__NR_getpid);
-}
-
-static struct siginfo TRAP_info;
-static volatile int TRAP_nr;
-static void TRAP_action(int nr, siginfo_t *info, void *void_context)
-{
- memcpy(&TRAP_info, info, sizeof(TRAP_info));
- TRAP_nr = nr;
- return;
-}
-
-TEST_F(TRAP, handler) {
- int ret, test;
- struct sigaction act;
- sigset_t mask;
- memset(&act, 0, sizeof(act));
- sigemptyset(&mask);
- sigaddset(&mask, SIGSYS);
-
- act.sa_sigaction = &TRAP_action;
- act.sa_flags = SA_SIGINFO;
- ret = sigaction(SIGSYS, &act, NULL);
- ASSERT_EQ(0, ret) {
- TH_LOG("sigaction failed");
- }
- ret = sigprocmask(SIG_UNBLOCK, &mask, NULL);
- ASSERT_EQ(0, ret) {
- TH_LOG("sigprocmask failed");
- }
-
- ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog);
- ASSERT_EQ(0, ret);
- TRAP_nr = 0;
- memset(&TRAP_info, 0, sizeof(TRAP_info));
- /* Expect the registers to be rolled back. (nr = error) may vary
- * based on arch. */
- ret = syscall(__NR_getpid);
- /* Silence gcc warning about volatile. */
- test = TRAP_nr;
- EXPECT_EQ(SIGSYS, test);
- struct local_sigsys {
- void *_call_addr; /* calling user insn */
- int _syscall; /* triggering system call number */
- unsigned int _arch; /* AUDIT_ARCH_* of syscall */
- } *sigsys = (struct local_sigsys *)
-#ifdef si_syscall
- &(TRAP_info.si_call_addr);
-#else
- &TRAP_info.si_pid;
-#endif
- EXPECT_EQ(__NR_getpid, sigsys->_syscall);
- /* Make sure arch is non-zero. */
- EXPECT_NE(0, sigsys->_arch);
- EXPECT_NE(0, (unsigned long)sigsys->_call_addr);
-}
-
-FIXTURE_DATA(precedence) {
- struct sock_fprog allow;
- struct sock_fprog trace;
- struct sock_fprog error;
- struct sock_fprog trap;
- struct sock_fprog kill;
-};
-
-FIXTURE_SETUP(precedence) {
- struct sock_filter allow_insns[] = {
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_filter trace_insns[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE),
- };
- struct sock_filter error_insns[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO),
- };
- struct sock_filter trap_insns[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP),
- };
- struct sock_filter kill_insns[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
- };
- memset(self, 0, sizeof(*self));
-#define FILTER_ALLOC(_x) \
- self->_x.filter = malloc(sizeof(_x##_insns)); \
- ASSERT_NE(NULL, self->_x.filter); \
- memcpy(self->_x.filter, &_x##_insns, sizeof(_x##_insns)); \
- self->_x.len = (unsigned short)(sizeof(_x##_insns)/sizeof(_x##_insns[0]))
- FILTER_ALLOC(allow);
- FILTER_ALLOC(trace);
- FILTER_ALLOC(error);
- FILTER_ALLOC(trap);
- FILTER_ALLOC(kill);
-}
-
-FIXTURE_TEARDOWN(precedence) {
-#define FILTER_FREE(_x) if (self->_x.filter) free(self->_x.filter)
- FILTER_FREE(allow);
- FILTER_FREE(trace);
- FILTER_FREE(error);
- FILTER_FREE(trap);
- FILTER_FREE(kill);
-}
-
-TEST_F(precedence, allow_ok) {
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
- pid_t res = 0;
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->kill);
- ASSERT_EQ(0, ret);
- /* Should work just fine. */
- res = syscall(__NR_getppid);
- EXPECT_EQ(parent, res);
-}
-
-TEST_F_SIGNAL(precedence, kill_is_highest, SIGSYS) {
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
- pid_t res = 0;
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->kill);
- ASSERT_EQ(0, ret);
- /* Should work just fine. */
- res = syscall(__NR_getppid);
- EXPECT_EQ(parent, res);
- /* getpid() should never return. */
- res = syscall(__NR_getpid);
- EXPECT_EQ(0, res);
-}
-
-TEST_F_SIGNAL(precedence, kill_is_highest_in_any_order, SIGSYS) {
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->kill);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
- ASSERT_EQ(0, ret);
- /* Should work just fine. */
- EXPECT_EQ(parent, syscall(__NR_getppid));
- /* getpid() should never return. */
- EXPECT_EQ(0, syscall(__NR_getpid));
-}
-
-TEST_F_SIGNAL(precedence, trap_is_second, SIGSYS) {
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
- ASSERT_EQ(0, ret);
- /* Should work just fine. */
- EXPECT_EQ(parent, syscall(__NR_getppid));
- /* getpid() should never return. */
- EXPECT_EQ(0, syscall(__NR_getpid));
-}
-
-TEST_F_SIGNAL(precedence, trap_is_second_in_any_order, SIGSYS) {
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
- ASSERT_EQ(0, ret);
- /* Should work just fine. */
- EXPECT_EQ(parent, syscall(__NR_getppid));
- /* getpid() should never return. */
- EXPECT_EQ(0, syscall(__NR_getpid));
-}
-
-TEST_F(precedence, errno_is_third) {
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
- ASSERT_EQ(0, ret);
- /* Should work just fine. */
- EXPECT_EQ(parent, syscall(__NR_getppid));
- EXPECT_EQ(0, syscall(__NR_getpid));
-}
-
-TEST_F(precedence, errno_is_third_in_any_order) {
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
- ASSERT_EQ(0, ret);
- /* Should work just fine. */
- EXPECT_EQ(parent, syscall(__NR_getppid));
- EXPECT_EQ(0, syscall(__NR_getpid));
-}
-
-TEST_F(precedence, trace_is_fourth) {
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
- ASSERT_EQ(0, ret);
- /* Should work just fine. */
- EXPECT_EQ(parent, syscall(__NR_getppid));
- /* No ptracer */
- EXPECT_EQ(-1, syscall(__NR_getpid));
-}
-
-TEST_F(precedence, trace_is_fourth_in_any_order) {
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- pid_t parent = getppid();
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
- ASSERT_EQ(0, ret);
- /* Should work just fine. */
- EXPECT_EQ(parent, syscall(__NR_getppid));
- /* No ptracer */
- EXPECT_EQ(-1, syscall(__NR_getpid));
-}
-
-#ifndef PTRACE_O_TRACESECCOMP
-#define PTRACE_O_TRACESECCOMP 0x00000080
-#endif
-
-/* Catch the Ubuntu 12.04 value error. */
-#if PTRACE_EVENT_SECCOMP != 7
-#undef PTRACE_EVENT_SECCOMP
-#endif
-
-#ifndef PTRACE_EVENT_SECCOMP
-#define PTRACE_EVENT_SECCOMP 7
-#endif
-
-#define IS_SECCOMP_EVENT(status) (((status) >> 16) == PTRACE_EVENT_SECCOMP)
-bool tracer_running;
-void tracer_stop(int sig)
-{
- tracer_running = false;
-}
-
-typedef void tracer_func_t(struct __test_metadata *_metadata,
- pid_t tracee, int status, void *args);
-
-void tracer(struct __test_metadata *_metadata, int fd, pid_t tracee,
- tracer_func_t tracer_func, void *args) {
- int ret = -1;
- struct sigaction action = {
- .sa_handler = tracer_stop,
- };
-
- /* Allow external shutdown. */
- tracer_running = true;
- ASSERT_EQ(0, sigaction(SIGUSR1, &action, NULL));
-
- errno = 0;
- while (ret == -1 && errno != EINVAL) {
- ret = ptrace(PTRACE_ATTACH, tracee, NULL, 0);
- }
- ASSERT_EQ(0, ret) {
- kill(tracee, SIGKILL);
- }
- /* Wait for attach stop */
- wait(NULL);
-
- ret = ptrace(PTRACE_SETOPTIONS, tracee, NULL, PTRACE_O_TRACESECCOMP);
- ASSERT_EQ(0, ret) {
- TH_LOG("Failed to set PTRACE_O_TRACESECCOMP");
- kill(tracee, SIGKILL);
- }
- ptrace(PTRACE_CONT, tracee, NULL, 0);
-
- /* Unblock the tracee */
- ASSERT_EQ(1, write(fd, "A", 1));
- ASSERT_EQ(0, close(fd));
-
- /* Run until we're shut down. Must assert to stop execution. */
- while (tracer_running) {
- int status;
- if (wait(&status) != tracee)
- continue;
- if (WIFSIGNALED(status) || WIFEXITED(status))
- /* Child is dead. Time to go. */
- return;
-
- /* Make sure this is a seccomp event. */
- ASSERT_EQ(true, IS_SECCOMP_EVENT(status));
-
- tracer_func(_metadata, tracee, status, args);
-
- ret = ptrace(PTRACE_CONT, tracee, NULL, NULL);
- ASSERT_EQ(0, ret);
- }
- /* Directly report the status of our test harness results. */
- syscall(__NR_exit, _metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
-}
-
-/* Common tracer setup/teardown functions. */
-void cont_handler(int num) {
-}
-pid_t setup_trace_fixture(struct __test_metadata *_metadata,
- tracer_func_t func, void *args) {
- char sync;
- int pipefd[2];
- pid_t tracer_pid;
- pid_t tracee = getpid();
-
- /* Setup a pipe for clean synchronization. */
- ASSERT_EQ(0, pipe(pipefd));
-
- /* Fork a child which we'll promote to tracer */
- tracer_pid = fork();
- ASSERT_LE(0, tracer_pid);
- signal(SIGALRM, cont_handler);
- if (tracer_pid == 0) {
- close(pipefd[0]);
- tracer(_metadata, pipefd[1], tracee, func, args);
- syscall(__NR_exit, 0);
- }
- close(pipefd[1]);
- prctl(PR_SET_PTRACER, tracer_pid, 0, 0, 0);
- read(pipefd[0], &sync, 1);
- close(pipefd[0]);
-
- return tracer_pid;
-}
-void teardown_trace_fixture(struct __test_metadata *_metadata,
- pid_t tracer) {
- if (tracer) {
- int status;
- /*
- * Extract the exit code from the other process and
- * adopt it for ourselves in case its asserts failed.
- */
- ASSERT_EQ(0, kill(tracer, SIGUSR1));
- ASSERT_EQ(tracer, waitpid(tracer, &status, 0));
- if (WEXITSTATUS(status))
- _metadata->passed = 0;
- }
-}
-
-/* "poke" tracer arguments and function. */
-struct tracer_args_poke_t {
- unsigned long poke_addr;
-};
-
-void tracer_poke(struct __test_metadata *_metadata, pid_t tracee, int status,
- void *args) {
- int ret;
- unsigned long msg;
- struct tracer_args_poke_t *info = (struct tracer_args_poke_t *)args;
-
- ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg);
- EXPECT_EQ(0, ret);
- /* If this fails, don't try to recover. */
- ASSERT_EQ(0x1001, msg) {
- kill(tracee, SIGKILL);
- }
- /*
- * Poke in the message.
- * Registers are not touched to try to keep this relatively arch
- * agnostic.
- */
- ret = ptrace(PTRACE_POKEDATA, tracee, info->poke_addr, 0x1001);
- EXPECT_EQ(0, ret);
-}
-
-FIXTURE_DATA(TRACE_poke) {
- struct sock_fprog prog;
- pid_t tracer;
- long poked;
- struct tracer_args_poke_t tracer_args;
-};
-
-FIXTURE_SETUP(TRACE_poke) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1001),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
-
- self->poked = 0;
- memset(&self->prog, 0, sizeof(self->prog));
- self->prog.filter = malloc(sizeof(filter));
- ASSERT_NE(NULL, self->prog.filter);
- memcpy(self->prog.filter, filter, sizeof(filter));
- self->prog.len = (unsigned short)(sizeof(filter)/sizeof(filter[0]));
-
- /* Set up tracer args. */
- self->tracer_args.poke_addr = (unsigned long)&self->poked;
-
- /* Launch tracer. */
- self->tracer = setup_trace_fixture(_metadata, tracer_poke,
- &self->tracer_args);
-}
-
-FIXTURE_TEARDOWN(TRACE_poke) {
- teardown_trace_fixture(_metadata, self->tracer);
- if (self->prog.filter)
- free(self->prog.filter);
-};
-
-TEST_F(TRACE_poke, read_has_side_effects) {
- ssize_t ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
- ASSERT_EQ(0, ret);
-
- EXPECT_EQ(0, self->poked);
- ret = read(-1, NULL, 0);
- EXPECT_EQ(-1, ret);
- EXPECT_EQ(0x1001, self->poked);
-}
-
-TEST_F(TRACE_poke, getpid_runs_normally) {
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
- ASSERT_EQ(0, ret);
-
- EXPECT_EQ(0, self->poked);
- EXPECT_NE(0, syscall(__NR_getpid));
- EXPECT_EQ(0, self->poked);
-}
-
-#if defined(__x86_64__)
-# define ARCH_REGS struct user_regs_struct
-# define SYSCALL_NUM orig_rax
-# define SYSCALL_RET rax
-#elif defined(__i386__)
-# define ARCH_REGS struct user_regs_struct
-# define SYSCALL_NUM orig_eax
-# define SYSCALL_RET eax
-#elif defined(__arm__)
-# define ARCH_REGS struct pt_regs
-# define SYSCALL_NUM ARM_r7
-# define SYSCALL_RET ARM_r0
-#elif defined(__aarch64__)
-# define ARCH_REGS struct user_pt_regs
-# define SYSCALL_NUM regs[8]
-# define SYSCALL_RET regs[0]
-#else
-# error "Do not know how to find your architecture's registers and syscalls"
-#endif
-
-/* Architecture-specific syscall fetching routine. */
-int get_syscall(struct __test_metadata *_metadata, pid_t tracee) {
- struct iovec iov;
- ARCH_REGS regs;
-
- iov.iov_base = ®s;
- iov.iov_len = sizeof(regs);
- EXPECT_EQ(0, ptrace(PTRACE_GETREGSET, tracee, NT_PRSTATUS, &iov)) {
- TH_LOG("PTRACE_GETREGSET failed");
- return -1;
- }
-
- return regs.SYSCALL_NUM;
-}
-
-/* Architecture-specific syscall changing routine. */
-void change_syscall(struct __test_metadata *_metadata,
- pid_t tracee, int syscall) {
- struct iovec iov;
- int ret;
- ARCH_REGS regs;
-
- iov.iov_base = ®s;
- iov.iov_len = sizeof(regs);
- ret = ptrace(PTRACE_GETREGSET, tracee, NT_PRSTATUS, &iov);
- EXPECT_EQ(0, ret);
-
-#if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__)
- {
- regs.SYSCALL_NUM = syscall;
- }
-
-#elif defined(__arm__)
-# ifndef PTRACE_SET_SYSCALL
-# define PTRACE_SET_SYSCALL 23
-# endif
- {
- ret = ptrace(PTRACE_SET_SYSCALL, tracee, NULL, syscall);
- EXPECT_EQ(0, ret);
- }
-
-#else
- ASSERT_EQ(1, 0) {
- TH_LOG("How is the syscall changed on this architecture?");
- }
-#endif
-
- /* If syscall is skipped, change return value. */
- if (syscall == -1)
- regs.SYSCALL_RET = 1;
-
- ret = ptrace(PTRACE_SETREGSET, tracee, NT_PRSTATUS, &iov);
- EXPECT_EQ(0, ret);
-}
-
-void tracer_syscall(struct __test_metadata *_metadata, pid_t tracee,
- int status, void *args) {
- int ret;
- unsigned long msg;
-
- /* Make sure we got the right message. */
- ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg);
- EXPECT_EQ(0, ret);
-
- switch (msg) {
- case 0x1002:
- /* change getpid to getppid. */
- change_syscall(_metadata, tracee, __NR_getppid);
- break;
- case 0x1003:
- /* skip gettid. */
- change_syscall(_metadata, tracee, -1);
- break;
- case 0x1004:
- /* do nothing (allow getppid) */
- break;
- default:
- EXPECT_EQ(0, msg) {
- TH_LOG("Unknown PTRACE_GETEVENTMSG: 0x%lx", msg);
- kill(tracee, SIGKILL);
- }
- }
-
-}
-
-FIXTURE_DATA(TRACE_syscall) {
- struct sock_fprog prog;
- pid_t tracer, mytid, mypid, parent;
-};
-
-FIXTURE_SETUP(TRACE_syscall) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 0, 1),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1002),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_gettid, 0, 1),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1003),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1004),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
-
- memset(&self->prog, 0, sizeof(self->prog));
- self->prog.filter = malloc(sizeof(filter));
- ASSERT_NE(NULL, self->prog.filter);
- memcpy(self->prog.filter, filter, sizeof(filter));
- self->prog.len = (unsigned short)(sizeof(filter)/sizeof(filter[0]));
-
- /* Prepare some testable syscall results. */
- self->mytid = syscall(__NR_gettid);
- ASSERT_GT(self->mytid, 0);
- ASSERT_NE(self->mytid, 1) {
- TH_LOG("Running this test as init is not supported. :)");
- }
-
- self->mypid = getpid();
- ASSERT_GT(self->mypid, 0);
- ASSERT_EQ(self->mytid, self->mypid);
-
- self->parent = getppid();
- ASSERT_GT(self->parent, 0);
- ASSERT_NE(self->parent, self->mypid);
-
- /* Launch tracer. */
- self->tracer = setup_trace_fixture(_metadata, tracer_syscall, NULL);
-}
-
-FIXTURE_TEARDOWN(TRACE_syscall) {
- teardown_trace_fixture(_metadata, self->tracer);
- if (self->prog.filter)
- free(self->prog.filter);
-};
-
-TEST_F(TRACE_syscall, syscall_allowed) {
- long ret;
-
- ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
- ASSERT_EQ(0, ret);
-
- /* getppid works as expected (no changes). */
- EXPECT_EQ(self->parent, syscall(__NR_getppid));
- EXPECT_NE(self->mypid, syscall(__NR_getppid));
-}
-
-TEST_F(TRACE_syscall, syscall_redirected) {
- long ret;
-
- ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
- ASSERT_EQ(0, ret);
-
- /* getpid has been redirected to getppid as expected. */
- EXPECT_EQ(self->parent, syscall(__NR_getpid));
- EXPECT_NE(self->mypid, syscall(__NR_getpid));
-}
-
-TEST_F(TRACE_syscall, syscall_dropped) {
- long ret;
-
- ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
- ASSERT_EQ(0, ret);
-
- /* gettid has been skipped and an altered return value stored. */
- EXPECT_EQ(1, syscall(__NR_gettid));
- EXPECT_NE(self->mytid, syscall(__NR_gettid));
-}
-
-#ifndef __NR_seccomp
-# if defined(__i386__)
-# define __NR_seccomp 354
-# elif defined(__x86_64__)
-# define __NR_seccomp 317
-# elif defined(__arm__)
-# define __NR_seccomp 383
-# elif defined(__aarch64__)
-# define __NR_seccomp 277
-# else
-# warning "seccomp syscall number unknown for this architecture"
-# define __NR_seccomp 0xffff
-# endif
-#endif
-
-#ifndef SECCOMP_SET_MODE_STRICT
-#define SECCOMP_SET_MODE_STRICT 0
-#endif
-
-#ifndef SECCOMP_SET_MODE_FILTER
-#define SECCOMP_SET_MODE_FILTER 1
-#endif
-
-#ifndef SECCOMP_FLAG_FILTER_TSYNC
-#define SECCOMP_FLAG_FILTER_TSYNC 1
-#endif
-
-#ifndef seccomp
-int seccomp(unsigned int op, unsigned int flags, struct sock_fprog *filter)
-{
- errno = 0;
- return syscall(__NR_seccomp, op, flags, filter);
-}
-#endif
-
-TEST(seccomp_syscall) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret) {
- TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
- }
-
- /* Reject insane operation. */
- ret = seccomp(-1, 0, &prog);
- EXPECT_EQ(EINVAL, errno) {
- TH_LOG("Did not reject crazy op value!");
- }
-
- /* Reject strict with flags or pointer. */
- ret = seccomp(SECCOMP_SET_MODE_STRICT, -1, NULL);
- EXPECT_EQ(EINVAL, errno) {
- TH_LOG("Did not reject mode strict with flags!");
- }
- ret = seccomp(SECCOMP_SET_MODE_STRICT, 0, &prog);
- EXPECT_EQ(EINVAL, errno) {
- TH_LOG("Did not reject mode strict with uargs!");
- }
-
- /* Reject insane args for filter. */
- ret = seccomp(SECCOMP_SET_MODE_FILTER, -1, &prog);
- EXPECT_EQ(EINVAL, errno) {
- TH_LOG("Did not reject crazy filter flags!");
- }
- ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, NULL);
- EXPECT_EQ(EFAULT, errno) {
- TH_LOG("Did not reject NULL filter!");
- }
-
- ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog);
- EXPECT_EQ(0, errno) {
- TH_LOG("Kernel does not support SECCOMP_SET_MODE_FILTER: %s",
- strerror(errno));
- }
-}
-
-TEST(seccomp_syscall_mode_lock) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0);
- ASSERT_EQ(0, ret) {
- TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
- }
-
- ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog);
- EXPECT_EQ(0, ret) {
- TH_LOG("Could not install filter!");
- }
-
- /* Make sure neither entry point will switch to strict. */
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, 0, 0, 0);
- EXPECT_EQ(EINVAL, errno) {
- TH_LOG("Switched to mode strict!");
- }
-
- ret = seccomp(SECCOMP_SET_MODE_STRICT, 0, NULL);
- EXPECT_EQ(EINVAL, errno) {
- TH_LOG("Switched to mode strict!");
- }
-}
-
-TEST(TSYNC_first) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
- long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0);
- ASSERT_EQ(0, ret) {
- TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
- }
-
- ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FLAG_FILTER_TSYNC,
- &prog);
- EXPECT_EQ(0, ret) {
- TH_LOG("Could not install initial filter with TSYNC!");
- }
-}
-
-#define TSYNC_SIBLINGS 2
-struct tsync_sibling {
- pthread_t tid;
- pid_t system_tid;
- sem_t *started;
- pthread_cond_t *cond;
- pthread_mutex_t *mutex;
- int diverge;
- int num_waits;
- struct sock_fprog *prog;
- struct __test_metadata *metadata;
-};
-
-FIXTURE_DATA(TSYNC) {
- struct sock_fprog root_prog, apply_prog;
- struct tsync_sibling sibling[TSYNC_SIBLINGS];
- sem_t started;
- pthread_cond_t cond;
- pthread_mutex_t mutex;
- int sibling_count;
-};
-
-FIXTURE_SETUP(TSYNC) {
- struct sock_filter root_filter[] = {
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_filter apply_filter[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- memset(&self->root_prog, 0, sizeof(self->root_prog));
- memset(&self->apply_prog, 0, sizeof(self->apply_prog));
- memset(&self->sibling, 0, sizeof(self->sibling));
- self->root_prog.filter = malloc(sizeof(root_filter));
- ASSERT_NE(NULL, self->root_prog.filter);
- memcpy(self->root_prog.filter, &root_filter, sizeof(root_filter));
- self->root_prog.len = (unsigned short)(sizeof(root_filter)/sizeof(root_filter[0]));
-
- self->apply_prog.filter = malloc(sizeof(apply_filter));
- ASSERT_NE(NULL, self->apply_prog.filter);
- memcpy(self->apply_prog.filter, &apply_filter, sizeof(apply_filter));
- self->apply_prog.len = (unsigned short)(sizeof(apply_filter)/sizeof(apply_filter[0]));
-
- self->sibling_count = 0;
- pthread_mutex_init(&self->mutex, NULL);
- pthread_cond_init(&self->cond, NULL);
- sem_init(&self->started, 0, 0);
- self->sibling[0].tid = 0;
- self->sibling[0].cond = &self->cond;
- self->sibling[0].started = &self->started;
- self->sibling[0].mutex = &self->mutex;
- self->sibling[0].diverge = 0;
- self->sibling[0].num_waits = 1;
- self->sibling[0].prog = &self->root_prog;
- self->sibling[0].metadata = _metadata;
- self->sibling[1].tid = 0;
- self->sibling[1].cond = &self->cond;
- self->sibling[1].started = &self->started;
- self->sibling[1].mutex = &self->mutex;
- self->sibling[1].diverge = 0;
- self->sibling[1].prog = &self->root_prog;
- self->sibling[1].num_waits = 1;
- self->sibling[1].metadata = _metadata;
-}
-
-FIXTURE_TEARDOWN(TSYNC) {
- int sib = 0;
- if (self->root_prog.filter)
- free(self->root_prog.filter);
- if (self->apply_prog.filter)
- free(self->apply_prog.filter);
-
- for ( ; sib < self->sibling_count; ++sib) {
- struct tsync_sibling *s = &self->sibling[sib];
- void *status;
- if (!s->tid)
- continue;
- if (pthread_kill(s->tid, 0)) {
- //pthread_cancel(s->tid); // ANDROID
- pthread_join(s->tid, &status);
- }
- }
- pthread_mutex_destroy(&self->mutex);
- pthread_cond_destroy(&self->cond);
- sem_destroy(&self->started);
-};
-
-void *tsync_sibling(void *data)
-{
- long ret = 0;
- struct tsync_sibling *me = data;
- me->system_tid = syscall(__NR_gettid);
-
- pthread_mutex_lock(me->mutex);
- if (me->diverge) {
- /* Just re-apply the root prog to fork the tree */
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER,
- me->prog, 0, 0);
- }
- sem_post(me->started);
- /* Return outside of started so parent notices failures. */
- if (ret) {
- pthread_mutex_unlock(me->mutex);
- return (void *)SIBLING_EXIT_FAILURE;
- }
- do {
- pthread_cond_wait(me->cond, me->mutex);
- me->num_waits = me->num_waits - 1;
- }
- while (me->num_waits);
- pthread_mutex_unlock(me->mutex);
- long nnp = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0);
- if (!nnp)
- return (void*)SIBLING_EXIT_NEWPRIVS;
- read(0, NULL, 0);
- return (void *)SIBLING_EXIT_UNKILLED;
-}
-
-void tsync_start_sibling(struct tsync_sibling *sibling)
-{
- pthread_create(&sibling->tid, NULL, tsync_sibling, (void *)sibling);
-}
-
-TEST_F(TSYNC, siblings_fail_prctl) {
- long ret;
- void *status;
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_prctl, 0, 1),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | EINVAL),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- };
- struct sock_fprog prog = {
- .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
-
- ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
- TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
- }
-
- /* Check prctl failure detection by requesting sib 0 diverge. */
- ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog);
- ASSERT_EQ(0, ret) {
- TH_LOG("setting filter failed");
- }
-
- self->sibling[0].diverge = 1;
- tsync_start_sibling(&self->sibling[0]);
- tsync_start_sibling(&self->sibling[1]);
-
- while (self->sibling_count < TSYNC_SIBLINGS) {
- sem_wait(&self->started);
- self->sibling_count++;
- }
-
- /* Signal the threads to clean up*/
- pthread_mutex_lock(&self->mutex);
- ASSERT_EQ(0, pthread_cond_broadcast(&self->cond)) {
- TH_LOG("cond broadcast non-zero");
- }
- pthread_mutex_unlock(&self->mutex);
-
- /* Ensure diverging sibling failed to call prctl. */
- pthread_join(self->sibling[0].tid, &status);
- EXPECT_EQ(SIBLING_EXIT_FAILURE, (long)status);
- pthread_join(self->sibling[1].tid, &status);
- EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status);
-}
-
-TEST_F(TSYNC, two_siblings_with_ancestor) {
- long ret;
- void *status;
-
- ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
- TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
- }
-
- ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &self->root_prog);
- ASSERT_EQ(0, ret) {
- TH_LOG("Kernel does not support SECCOMP_SET_MODE_FILTER!");
- }
- tsync_start_sibling(&self->sibling[0]);
- tsync_start_sibling(&self->sibling[1]);
-
- while (self->sibling_count < TSYNC_SIBLINGS) {
- sem_wait(&self->started);
- self->sibling_count++;
- }
-
- ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FLAG_FILTER_TSYNC,
- &self->apply_prog);
- ASSERT_EQ(0, ret) {
- TH_LOG("Could install filter on all threads!");
- }
- /* Tell the siblings to test the policy */
- pthread_mutex_lock(&self->mutex);
- ASSERT_EQ(0, pthread_cond_broadcast(&self->cond)) {
- TH_LOG("cond broadcast non-zero");
- }
- pthread_mutex_unlock(&self->mutex);
- /* Ensure they are both killed and don't exit cleanly. */
- pthread_join(self->sibling[0].tid, &status);
- EXPECT_EQ(0x0, (long)status);
- pthread_join(self->sibling[1].tid, &status);
- EXPECT_EQ(0x0, (long)status);
-}
-
-TEST_F(TSYNC, two_sibling_want_nnp) {
- void *status;
-
- /* start siblings before any prctl() operations */
- tsync_start_sibling(&self->sibling[0]);
- tsync_start_sibling(&self->sibling[1]);
- while (self->sibling_count < TSYNC_SIBLINGS) {
- sem_wait(&self->started);
- self->sibling_count++;
- }
-
- /* Tell the siblings to test no policy */
- pthread_mutex_lock(&self->mutex);
- ASSERT_EQ(0, pthread_cond_broadcast(&self->cond)) {
- TH_LOG("cond broadcast non-zero");
- }
- pthread_mutex_unlock(&self->mutex);
-
- /* Ensure they are both upset about lacking nnp. */
- pthread_join(self->sibling[0].tid, &status);
- EXPECT_EQ(SIBLING_EXIT_NEWPRIVS, (long)status);
- pthread_join(self->sibling[1].tid, &status);
- EXPECT_EQ(SIBLING_EXIT_NEWPRIVS, (long)status);
-}
-
-TEST_F(TSYNC, two_siblings_with_no_filter) {
- long ret;
- void *status;
-
- /* start siblings before any prctl() operations */
- tsync_start_sibling(&self->sibling[0]);
- tsync_start_sibling(&self->sibling[1]);
- while (self->sibling_count < TSYNC_SIBLINGS) {
- sem_wait(&self->started);
- self->sibling_count++;
- }
-
- ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
- TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
- }
-
- ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FLAG_FILTER_TSYNC,
- &self->apply_prog);
- ASSERT_EQ(0, ret) {
- TH_LOG("Could install filter on all threads!");
- }
-
- /* Tell the siblings to test the policy */
- pthread_mutex_lock(&self->mutex);
- ASSERT_EQ(0, pthread_cond_broadcast(&self->cond)) {
- TH_LOG("cond broadcast non-zero");
- }
- pthread_mutex_unlock(&self->mutex);
-
- /* Ensure they are both killed and don't exit cleanly. */
- pthread_join(self->sibling[0].tid, &status);
- EXPECT_EQ(0x0, (long)status);
- pthread_join(self->sibling[1].tid, &status);
- EXPECT_EQ(0x0, (long)status);
-}
-
-TEST_F(TSYNC, two_siblings_with_one_divergence) {
- long ret;
- void *status;
-
- ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
- TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
- }
-
- ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &self->root_prog);
- ASSERT_EQ(0, ret) {
- TH_LOG("Kernel does not support SECCOMP_SET_MODE_FILTER!");
- }
- self->sibling[0].diverge = 1;
- tsync_start_sibling(&self->sibling[0]);
- tsync_start_sibling(&self->sibling[1]);
-
- while (self->sibling_count < TSYNC_SIBLINGS) {
- sem_wait(&self->started);
- self->sibling_count++;
- }
-
- ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FLAG_FILTER_TSYNC,
- &self->apply_prog);
- ASSERT_EQ(self->sibling[0].system_tid, ret) {
- TH_LOG("Did not fail on diverged sibling.");
- }
-
- /* Wake the threads */
- pthread_mutex_lock(&self->mutex);
- ASSERT_EQ(0, pthread_cond_broadcast(&self->cond)) {
- TH_LOG("cond broadcast non-zero");
- }
- pthread_mutex_unlock(&self->mutex);
-
- /* Ensure they are both unkilled. */
- pthread_join(self->sibling[0].tid, &status);
- EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status);
- pthread_join(self->sibling[1].tid, &status);
- EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status);
-}
-
-TEST_F(TSYNC, two_siblings_not_under_filter) {
- long ret, sib;
- void *status;
-
- ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
- TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
- }
-
- /*
- * Sibling 0 will have its own seccomp policy
- * and Sibling 1 will not be under seccomp at
- * all. Sibling 1 will enter seccomp and 0
- * will cause failure.
- */
- self->sibling[0].diverge = 1;
- tsync_start_sibling(&self->sibling[0]);
- tsync_start_sibling(&self->sibling[1]);
-
- while (self->sibling_count < TSYNC_SIBLINGS) {
- sem_wait(&self->started);
- self->sibling_count++;
- }
-
- ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &self->root_prog);
- ASSERT_EQ(0, ret) {
- TH_LOG("Kernel does not support SECCOMP_SET_MODE_FILTER!");
- }
-
- ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FLAG_FILTER_TSYNC,
- &self->apply_prog);
- ASSERT_EQ(ret, self->sibling[0].system_tid) {
- TH_LOG("Did not fail on diverged sibling.");
- }
- sib = 1;
- if (ret == self->sibling[0].system_tid)
- sib = 0;
-
- pthread_mutex_lock(&self->mutex);
-
- /* Increment the other siblings num_waits so we can clean up
- * the one we just saw.
- */
- self->sibling[!sib].num_waits += 1;
-
- /* Signal the thread to clean up*/
- ASSERT_EQ(0, pthread_cond_broadcast(&self->cond)) {
- TH_LOG("cond broadcast non-zero");
- }
- pthread_mutex_unlock(&self->mutex);
- pthread_join(self->sibling[sib].tid, &status);
- EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status);
- /* Poll for actual task death. pthread_join doesn't guarantee it. */
- while (!kill(self->sibling[sib].system_tid, 0)) sleep(0.1);
- /* Switch to the remaining sibling */
- sib = !sib;
-
- ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FLAG_FILTER_TSYNC,
- &self->apply_prog);
- ASSERT_EQ(0, ret) {
- TH_LOG("Expected the remaining sibling to sync");
- };
-
- pthread_mutex_lock(&self->mutex);
-
- /* If remaining sibling didn't have a chance to wake up during
- * the first broadcast, manually reduce the num_waits now.
- */
- if (self->sibling[sib].num_waits > 1)
- self->sibling[sib].num_waits = 1;
- ASSERT_EQ(0, pthread_cond_broadcast(&self->cond)) {
- TH_LOG("cond broadcast non-zero");
- }
- pthread_mutex_unlock(&self->mutex);
- pthread_join(self->sibling[sib].tid, &status);
- EXPECT_EQ(0, (long)status);
- /* Poll for actual task death. pthread_join doesn't guarantee it. */
- while (!kill(self->sibling[sib].system_tid, 0)) sleep(0.1);
-
- ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FLAG_FILTER_TSYNC,
- &self->apply_prog);
- ASSERT_EQ(0, ret); /* just us chickens */
-}
-
-/* Make sure restarted syscalls are seen directly as "restart_syscall". */
-TEST(syscall_restart)
-{
- long ret;
- unsigned long msg;
- pid_t child_pid;
- int pipefd[2];
- int status;
- siginfo_t info = { };
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
- offsetof(struct seccomp_data, nr)),
-
-#ifdef __NR_sigreturn
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_sigreturn, 6, 0),
-#endif
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 5, 0),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_exit, 4, 0),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_rt_sigreturn, 3, 0),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_nanosleep, 4, 0),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_restart_syscall, 4, 0),
-
- /* Allow __NR_write for easy logging. */
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_write, 0, 1),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
- /* The nanosleep jump target. */
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE|0x100),
- /* The restart_syscall jump target. */
- BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE|0x200),
- };
- struct sock_fprog prog = {
- .len = (unsigned short) (sizeof(filter)/sizeof(filter[0])),
- .filter = filter,
- };
-#if defined(__arm__)
- struct utsname utsbuf;
-#endif
-
- ASSERT_EQ(0, pipe(pipefd));
-
- child_pid = fork();
- ASSERT_LE(0, child_pid);
- if (child_pid == 0) {
- /* Child uses EXPECT not ASSERT to deliver status correctly. */
- char buf = ' ';
- struct timespec timeout = { };
-
- /* Attach parent as tracer and stop. */
- EXPECT_EQ(0, ptrace(PTRACE_TRACEME));
- EXPECT_EQ(0, raise(SIGSTOP));
-
- EXPECT_EQ(0, close(pipefd[1]));
-
- EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
- TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
- }
-
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
- EXPECT_EQ(0, ret) {
- TH_LOG("Failed to install filter!");
- }
-
- EXPECT_EQ(1, read(pipefd[0], &buf, 1)) {
- TH_LOG("Failed to read() sync from parent");
- }
- EXPECT_EQ('.', buf) {
- TH_LOG("Failed to get sync data from read()");
- }
-
- /* Start nanosleep to be interrupted. */
- timeout.tv_sec = 1;
- errno = 0;
- EXPECT_EQ(0, nanosleep(&timeout, NULL)) {
- TH_LOG("Call to nanosleep() failed (errno %d)", errno);
- }
-
- /* Read final sync from parent. */
- EXPECT_EQ(1, read(pipefd[0], &buf, 1)) {
- TH_LOG("Failed final read() from parent");
- }
- EXPECT_EQ('!', buf) {
- TH_LOG("Failed to get final data from read()");
- }
-
- /* Directly report the status of our test harness results. */
- syscall(__NR_exit, _metadata->passed ? EXIT_SUCCESS
- : EXIT_FAILURE);
- }
- EXPECT_EQ(0, close(pipefd[0]));
-
- /* Attach to child, setup options, and release. */
- ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
- ASSERT_EQ(true, WIFSTOPPED(status));
- ASSERT_EQ(0, ptrace(PTRACE_SETOPTIONS, child_pid, NULL,
- PTRACE_O_TRACESECCOMP));
- ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
- ASSERT_EQ(1, write(pipefd[1], ".", 1));
-
- /* Wait for nanosleep() to start. */
- ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
- ASSERT_EQ(true, WIFSTOPPED(status));
- ASSERT_EQ(SIGTRAP, WSTOPSIG(status));
- ASSERT_EQ(PTRACE_EVENT_SECCOMP, (status >> 16));
- ASSERT_EQ(0, ptrace(PTRACE_GETEVENTMSG, child_pid, NULL, &msg));
- ASSERT_EQ(0x100, msg);
- EXPECT_EQ(__NR_nanosleep, get_syscall(_metadata, child_pid));
-
- /* Might as well check siginfo for sanity while we're here. */
- ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &info));
- ASSERT_EQ(SIGTRAP, info.si_signo);
- ASSERT_EQ(SIGTRAP | (PTRACE_EVENT_SECCOMP << 8), info.si_code);
- EXPECT_EQ(0, info.si_errno);
- EXPECT_EQ(getuid(), info.si_uid);
- /* Verify signal delivery came from child (seccomp-triggered). */
- EXPECT_EQ(child_pid, info.si_pid);
-
- /* Interrupt nanosleep with SIGSTOP (which we'll need to handle). */
- ASSERT_EQ(0, kill(child_pid, SIGSTOP));
- ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
- ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
- ASSERT_EQ(true, WIFSTOPPED(status));
- ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
- /* Verify signal delivery came from parent now. */
- ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &info));
- EXPECT_EQ(getpid(), info.si_pid);
-
- /* Restart nanosleep with SIGCONT, which triggers restart_syscall. */
- ASSERT_EQ(0, kill(child_pid, SIGCONT));
- ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
- ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
- ASSERT_EQ(true, WIFSTOPPED(status));
- ASSERT_EQ(SIGCONT, WSTOPSIG(status));
- ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
-
- /* Wait for restart_syscall() to start. */
- ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
- ASSERT_EQ(true, WIFSTOPPED(status));
- ASSERT_EQ(SIGTRAP, WSTOPSIG(status));
- ASSERT_EQ(PTRACE_EVENT_SECCOMP, (status >> 16));
- ASSERT_EQ(0, ptrace(PTRACE_GETEVENTMSG, child_pid, NULL, &msg));
-
- ASSERT_EQ(0x200, msg);
- ret = get_syscall(_metadata, child_pid);
-#if defined(__arm__)
- /*
- * FIXME:
- * - native ARM registers do NOT expose true syscall.
- * - compat ARM registers on ARM64 DO expose true syscall.
- */
- ASSERT_EQ(0, uname(&utsbuf));
- if (strncmp(utsbuf.machine, "arm", 3) == 0) {
- EXPECT_EQ(__NR_nanosleep, ret);
- } else
-#endif
- {
- EXPECT_EQ(__NR_restart_syscall, ret);
- }
-
- /* Write again to end test. */
- ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
- ASSERT_EQ(1, write(pipefd[1], "!", 1));
- EXPECT_EQ(0, close(pipefd[1]));
-
- ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
- if (WIFSIGNALED(status) || WEXITSTATUS(status))
- _metadata->passed = 0;
-}
-
-/*
- * TODO:
- * - add microbenchmarks
- * - expand NNP testing
- * - better arch-specific TRACE and TRAP handlers.
- * - endianness checking when appropriate
- * - 64-bit arg prodding
- * - arch value testing (x86 modes especially)
- * - ...
- */
-
-// ANDROID:begin
-struct __test_metadata* get_seccomp_test_list() {
- return __test_list;
-}
-// ANDROID:end
-
-TEST_HARNESS_MAIN
diff --git a/tests/tests/os/jni/seccomp-tests/tests/sigsegv.c b/tests/tests/os/jni/seccomp-tests/tests/sigsegv.c
deleted file mode 100644
index a0d06e7..0000000
--- a/tests/tests/os/jni/seccomp-tests/tests/sigsegv.c
+++ /dev/null
@@ -1,179 +0,0 @@
-/* sigsegv.c
- * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- *
- * Forces a denied system call to trigger a SIGSEGV at the instruction after
- * the call using a SIGSYS handler.. This can be useful when debugging
- * frameworks have trouble tracing through the SIGSYS handler.
- * Proof of concept using amd64 registers and 'syscall'.
- */
-
-#include <asm/siginfo.h>
-#define __have_siginfo_t 1
-#define __have_sigval_t 1
-#define __have_sigevent_t 1
-
-#include <linux/filter.h>
-#include <sys/prctl.h>
-#include <linux/prctl.h>
-#include <linux/seccomp.h>
-#include <limits.h>
-#include <stddef.h>
-#include <stdbool.h>
-#include <string.h>
-#include <syscall.h>
-#define __USE_GNU 1
-#include <sys/ucontext.h>
-#include <sys/mman.h>
-
-#include "test_harness.h"
-
-#ifndef PR_SET_NO_NEW_PRIVS
-#define PR_SET_NO_NEW_PRIVS 38
-#define PR_GET_NO_NEW_PRIVS 39
-#endif
-
-#if defined(__i386__)
-#define REG_IP REG_EIP
-#define REG_SP REG_ESP
-#define REG_RESULT REG_EAX
-#define REG_SYSCALL REG_EAX
-#define REG_ARG0 REG_EBX
-#define REG_ARG1 REG_ECX
-#define REG_ARG2 REG_EDX
-#define REG_ARG3 REG_ESI
-#define REG_ARG4 REG_EDI
-#define REG_ARG5 REG_EBP
-#elif defined(__x86_64__)
-#define REG_IP REG_RIP
-#define REG_SP REG_RSP
-#define REG_RESULT REG_RAX
-#define REG_SYSCALL REG_RAX
-#define REG_ARG0 REG_RDI
-#define REG_ARG1 REG_RSI
-#define REG_ARG2 REG_RDX
-#define REG_ARG3 REG_R10
-#define REG_ARG4 REG_R8
-#define REG_ARG5 REG_R9
-#endif
-
-FIXTURE_DATA(TRAP) {
- struct sock_fprog prog;
-};
-
-FIXTURE_SETUP(TRAP) {
- /* instruction after the syscall. Will be arch specific, of course. */
- {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
- offsetof(struct seccomp_data, nr)),
- /* Whitelist anything you might need in the sigaction */
-#ifdef __NR_sigreturn
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_sigreturn, 4, 0),
-#endif
- /* TODO: only allow PROT_NONE */
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_mprotect, 3, 0),
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_exit, 2, 0),
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_rt_sigreturn, 1, 0),
- /* Allow __NR_write so easy logging. */
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_write, 0, 1),
- BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
- BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP),
- };
- memset(&self->prog, 0, sizeof(self->prog));
- self->prog.filter = malloc(sizeof(filter));
- ASSERT_NE(NULL, self->prog.filter);
- memcpy(self->prog.filter, filter, sizeof(filter));
- self->prog.len = (unsigned short)(sizeof(filter)/sizeof(filter[0]));
- }
-}
-
-FIXTURE_TEARDOWN(TRAP) {
- if (self->prog.filter)
- free(self->prog.filter);
-};
-
-struct arch_sigsys {
- void *_call_addr; /* calling user insn */
- int _syscall; /* triggering system call number */
- unsigned int _arch; /* AUDIT_ARCH_* of syscall */
-};
-
-#define _ALIGN(x,sz) (((x + ((sz)-1)) & ~((sz)-1)) - (sz))
-#define ALIGN(x,sz) ((typeof(x))_ALIGN((unsigned long)(x),(unsigned long)(sz)))
-static long local_mprotect(void *target, unsigned long sz)
-{
- register unsigned long res asm ("rax") = __NR_mprotect;
- __attribute__((unused)) register void *addr asm ("rdi") = ALIGN(target, sz);
- __attribute__((unused)) register long len asm ("rsi") = sz;
- __attribute__((unused)) register long num asm ("rdx") = PROT_NONE;
- __asm__("syscall\n");
- return res;
-}
-
-static void TRAP_action(int nr, siginfo_t *info, void *void_context)
-{
- ucontext_t *ctx = (ucontext_t *)void_context;
- char buf[256];
- int len;
- struct arch_sigsys *sys = (struct arch_sigsys *)
-#ifdef si_syscall
- &(info->si_call_addr);
-#else
- &(info->si_pid);
-#endif
-
- if (info->si_code != SYS_SECCOMP)
- return;
- if (!ctx)
- return;
- len = snprintf(buf, sizeof(buf),
- "@0x%lX:%X:%d:0x%lX:0x%lX:0x%lX:0x%lX:0x%lX:0x%lX [0x%lX]\n",
- (unsigned long)sys->_call_addr,
- sys->_arch,
- sys->_syscall,
- (unsigned long)ctx->uc_mcontext.gregs[REG_ARG0],
- (unsigned long)ctx->uc_mcontext.gregs[REG_ARG1],
- (unsigned long)ctx->uc_mcontext.gregs[REG_ARG2],
- (unsigned long)ctx->uc_mcontext.gregs[REG_ARG3],
- (unsigned long)ctx->uc_mcontext.gregs[REG_ARG4],
- (unsigned long)ctx->uc_mcontext.gregs[REG_ARG5],
- (unsigned long)ALIGN(ctx->uc_mcontext.gregs[REG_IP],
- 4096));
- /* Emit some useful logs or whatever. */
- syscall(__NR_write, STDOUT_FILENO, buf, len);
- /* Make the calling page non-exec */
- /* Careful on how it is called since it may make the syscall() instructions non-exec. */
- local_mprotect((void *)ctx->uc_mcontext.gregs[REG_IP], sysconf(_SC_PAGE_SIZE));
-}
-
-TEST_F_SIGNAL(TRAP, sigsegv, SIGSEGV) {
- int ret;
- struct sigaction act;
- sigset_t mask;
- memset(&act, 0, sizeof(act));
- sigemptyset(&mask);
- sigaddset(&mask, SIGSYS);
-
- act.sa_sigaction = &TRAP_action;
- act.sa_flags = SA_SIGINFO;
- ret = sigaction(SIGSYS, &act, NULL);
- ASSERT_EQ(0, ret) {
- TH_LOG("sigaction failed");
- }
- ret = sigprocmask(SIG_UNBLOCK, &mask, NULL);
- ASSERT_EQ(0, ret) {
- TH_LOG("sigprocmask failed");
- }
-
- ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
- ASSERT_EQ(0, ret);
- ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog);
- ASSERT_EQ(0, ret);
-
- /* Call anything! */
- ret = syscall(__NR_getpid);
-}
-
-TEST_HARNESS_MAIN
diff --git a/tests/tests/os/jni/seccomp-tests/tests/test_harness.h b/tests/tests/os/jni/seccomp-tests/tests/test_harness.h
deleted file mode 100644
index b7d44f1..0000000
--- a/tests/tests/os/jni/seccomp-tests/tests/test_harness.h
+++ /dev/null
@@ -1,521 +0,0 @@
-/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- *
- * test_harness.h: simple C unit test helper.
- *
- * Usage:
- * #include "test_harness.h"
- * TEST(standalone_test) {
- * do_some_stuff;
- * EXPECT_GT(10, stuff) {
- * stuff_state_t state;
- * enumerate_stuff_state(&state);
- * TH_LOG("expectation failed with state: %s", state.msg);
- * }
- * more_stuff;
- * ASSERT_NE(some_stuff, NULL) TH_LOG("how did it happen?!");
- * last_stuff;
- * EXPECT_EQ(0, last_stuff);
- * }
- *
- * FIXTURE(my_fixture) {
- * mytype_t *data;
- * int awesomeness_level;
- * };
- * FIXTURE_SETUP(my_fixture) {
- * self->data = mytype_new();
- * ASSERT_NE(NULL, self->data);
- * }
- * FIXTURE_TEARDOWN(my_fixture) {
- * mytype_free(self->data);
- * }
- * TEST_F(my_fixture, data_is_good) {
- * EXPECT_EQ(1, is_my_data_good(self->data));
- * }
- *
- * TEST_HARNESS_MAIN
- *
- * API inspired by code.google.com/p/googletest
- */
-#ifndef TEST_HARNESS_H_
-#define TEST_HARNESS_H_
-
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <android/log.h> // ANDROID
-
-/* All exported functionality should be declared through this macro. */
-#define TEST_API(x) _##x
-
-/*
- * Exported APIs
- */
-
-/* TEST(name) { implementation }
- * Defines a test by name.
- * Names must be unique and tests must not be run in parallel. The
- * implementation containing block is a function and scoping should be treated
- * as such. Returning early may be performed with a bare "return;" statement.
- *
- * EXPECT_* and ASSERT_* are valid in a TEST() { } context.
- */
-#define TEST TEST_API(TEST)
-
-/* TEST_SIGNAL(name, signal) { implementation }
- * Defines a test by name and the expected term signal.
- * Names must be unique and tests must not be run in parallel. The
- * implementation containing block is a function and scoping should be treated
- * as such. Returning early may be performed with a bare "return;" statement.
- *
- * EXPECT_* and ASSERT_* are valid in a TEST() { } context.
- */
-#define TEST_SIGNAL TEST_API(TEST_SIGNAL)
-
-/* FIXTURE(datatype name) {
- * type property1;
- * ...
- * };
- * Defines the data provided to TEST_F()-defined tests as |self|. It should be
- * populated and cleaned up using FIXTURE_SETUP and FIXTURE_TEARDOWN.
- */
-#define FIXTURE TEST_API(FIXTURE)
-
-/* FIXTURE_DATA(datatype name)
- * This call may be used when the type of the fixture data
- * is needed. In general, this should not be needed unless
- * the |self| is being passed to a helper directly.
- */
-#define FIXTURE_DATA TEST_API(FIXTURE_DATA)
-
-/* FIXTURE_SETUP(fixture name) { implementation }
- * Populates the required "setup" function for a fixture. An instance of the
- * datatype defined with _FIXTURE_DATA will be exposed as |self| for the
- * implementation.
- *
- * ASSERT_* are valid for use in this context and will prempt the execution
- * of any dependent fixture tests.
- *
- * A bare "return;" statement may be used to return early.
- */
-#define FIXTURE_SETUP TEST_API(FIXTURE_SETUP)
-
-/* FIXTURE_TEARDOWN(fixture name) { implementation }
- * Populates the required "teardown" function for a fixture. An instance of the
- * datatype defined with _FIXTURE_DATA will be exposed as |self| for the
- * implementation to clean up.
- *
- * A bare "return;" statement may be used to return early.
- */
-#define FIXTURE_TEARDOWN TEST_API(FIXTURE_TEARDOWN)
-
-/* TEST_F(fixture, name) { implementation }
- * Defines a test that depends on a fixture (e.g., is part of a test case).
- * Very similar to TEST() except that |self| is the setup instance of fixture's
- * datatype exposed for use by the implementation.
- */
-#define TEST_F TEST_API(TEST_F)
-
-#define TEST_F_SIGNAL TEST_API(TEST_F_SIGNAL)
-
-/* Use once to append a main() to the test file. E.g.,
- * TEST_HARNESS_MAIN
- */
-#define TEST_HARNESS_MAIN TEST_API(TEST_HARNESS_MAIN)
-
-/*
- * Operators for use in TEST and TEST_F.
- * ASSERT_* calls will stop test execution immediately.
- * EXPECT_* calls will emit a failure warning, note it, and continue.
- */
-
-/* ASSERT_EQ(expected, measured): expected == measured */
-#define ASSERT_EQ TEST_API(ASSERT_EQ)
-/* ASSERT_NE(expected, measured): expected != measured */
-#define ASSERT_NE TEST_API(ASSERT_NE)
-/* ASSERT_LT(expected, measured): expected < measured */
-#define ASSERT_LT TEST_API(ASSERT_LT)
-/* ASSERT_LE(expected, measured): expected <= measured */
-#define ASSERT_LE TEST_API(ASSERT_LE)
-/* ASSERT_GT(expected, measured): expected > measured */
-#define ASSERT_GT TEST_API(ASSERT_GT)
-/* ASSERT_GE(expected, measured): expected >= measured */
-#define ASSERT_GE TEST_API(ASSERT_GE)
-/* ASSERT_NULL(measured): NULL == measured */
-#define ASSERT_NULL TEST_API(ASSERT_NULL)
-/* ASSERT_TRUE(measured): measured != 0 */
-#define ASSERT_TRUE TEST_API(ASSERT_TRUE)
-/* ASSERT_FALSE(measured): measured == 0 */
-#define ASSERT_FALSE TEST_API(ASSERT_FALSE)
-/* ASSERT_STREQ(expected, measured): !strcmp(expected, measured) */
-#define ASSERT_STREQ TEST_API(ASSERT_STREQ)
-/* ASSERT_STRNE(expected, measured): strcmp(expected, measured) */
-#define ASSERT_STRNE TEST_API(ASSERT_STRNE)
-/* EXPECT_EQ(expected, measured): expected == measured */
-#define EXPECT_EQ TEST_API(EXPECT_EQ)
-/* EXPECT_NE(expected, measured): expected != measured */
-#define EXPECT_NE TEST_API(EXPECT_NE)
-/* EXPECT_LT(expected, measured): expected < measured */
-#define EXPECT_LT TEST_API(EXPECT_LT)
-/* EXPECT_LE(expected, measured): expected <= measured */
-#define EXPECT_LE TEST_API(EXPECT_LE)
-/* EXPECT_GT(expected, measured): expected > measured */
-#define EXPECT_GT TEST_API(EXPECT_GT)
-/* EXPECT_GE(expected, measured): expected >= measured */
-#define EXPECT_GE TEST_API(EXPECT_GE)
-/* EXPECT_NULL(measured): NULL == measured */
-#define EXPECT_NULL TEST_API(EXPECT_NULL)
-/* EXPECT_TRUE(measured): 0 != measured */
-#define EXPECT_TRUE TEST_API(EXPECT_TRUE)
-/* EXPECT_FALSE(measured): 0 == measured */
-#define EXPECT_FALSE TEST_API(EXPECT_FALSE)
-/* EXPECT_STREQ(expected, measured): !strcmp(expected, measured) */
-#define EXPECT_STREQ TEST_API(EXPECT_STREQ)
-/* EXPECT_STRNE(expected, measured): strcmp(expected, measured) */
-#define EXPECT_STRNE TEST_API(EXPECT_STRNE)
-
-/* TH_LOG(format, ...)
- * Optional debug logging function available for use in tests.
- * Logging may be enabled or disabled by defining TH_LOG_ENABLED.
- * E.g., #define TH_LOG_ENABLED 1
- * If no definition is provided, logging is enabled by default.
- */
-#define TH_LOG TEST_API(TH_LOG)
-
-/*
- * Internal implementation.
- *
- */
-
-/* Utilities exposed to the test definitions */
-#ifndef TH_LOG_STREAM
-# define TH_LOG_STREAM stderr
-#endif
-
-#ifndef TH_LOG_ENABLED
-# define TH_LOG_ENABLED 1
-#endif
-
-#define _TH_LOG(fmt, ...) do { \
- if (TH_LOG_ENABLED) \
- __TH_LOG(fmt, ##__VA_ARGS__); \
-} while (0)
-
-/* Unconditional logger for internal use. */
-// ANDROID:begin
-#define __TH_LOG(fmt, ...) \
- __android_log_print(ANDROID_LOG_ERROR, "SeccompBpfTest-KernelUnit", "%s:%d:%s:" fmt "\n", \
- __FILE__, __LINE__, _metadata->name, ##__VA_ARGS__)
-// ANDROID:end
-
-/* Defines the test function and creates the registration stub. */
-#define _TEST(test_name) __TEST_IMPL(test_name, -1)
-
-#define _TEST_SIGNAL(test_name, signal) __TEST_IMPL(test_name, signal)
-
-#define __TEST_IMPL(test_name, _signal) \
- static void test_name(struct __test_metadata *_metadata); \
- static struct __test_metadata _##test_name##_object = \
- { name: "global." #test_name, fn: &(test_name), termsig: (_signal) }; \
- static void __attribute__((constructor)) _register_##test_name(void) { \
- __register_test(&_##test_name##_object); \
- } \
- static void test_name( \
- struct __test_metadata __attribute__((unused)) *_metadata)
-
-/* Wraps the struct name so we have one less argument to pass around. */
-#define _FIXTURE_DATA(fixture_name) struct _test_data_##fixture_name
-
-/* Called once per fixture to setup the data and register. */
-#define _FIXTURE(fixture_name) \
- static void __attribute__((constructor)) \
- _register_##fixture_name##_data(void) { \
- __fixture_count++; \
- } \
- _FIXTURE_DATA(fixture_name)
-
-/* Prepares the setup function for the fixture. |_metadata| is included
- * so that ASSERT_* work as a convenience.
- */
-#define _FIXTURE_SETUP(fixture_name) \
- void fixture_name##_setup( \
- struct __test_metadata __attribute__((unused)) *_metadata, \
- _FIXTURE_DATA(fixture_name) __attribute__((unused)) *self)
-#define _FIXTURE_TEARDOWN(fixture_name) \
- void fixture_name##_teardown( \
- struct __test_metadata __attribute__((unused)) *_metadata, \
- _FIXTURE_DATA(fixture_name) __attribute__((unused)) *self)
-
-/* Emits test registration and helpers for fixture-based test
- * cases.
- * TODO(wad) register fixtures on dedicated test lists.
- */
-#define _TEST_F(fixture_name, test_name) \
- __TEST_F_IMPL(fixture_name, test_name, -1)
-
-#define _TEST_F_SIGNAL(fixture_name, test_name, signal) \
- __TEST_F_IMPL(fixture_name, test_name, signal)
-
-#define __TEST_F_IMPL(fixture_name, test_name, signal) \
- static void fixture_name##_##test_name( \
- struct __test_metadata *_metadata, \
- _FIXTURE_DATA(fixture_name) *self); \
- static inline void wrapper_##fixture_name##_##test_name( \
- struct __test_metadata *_metadata) { \
- /* fixture data is allocated, setup, and torn down per call. */ \
- _FIXTURE_DATA(fixture_name) self; \
- memset(&self, 0, sizeof(_FIXTURE_DATA(fixture_name))); \
- fixture_name##_setup(_metadata, &self); \
- /* Let setup failure terminate early. */ \
- if (!_metadata->passed) return; \
- fixture_name##_##test_name(_metadata, &self); \
- fixture_name##_teardown(_metadata, &self); \
- } \
- static struct __test_metadata _##fixture_name##_##test_name##_object = { \
- name: #fixture_name "." #test_name, \
- fn: &wrapper_##fixture_name##_##test_name, \
- termsig: (signal), \
- }; \
- static void __attribute__((constructor)) \
- _register_##fixture_name##_##test_name(void) { \
- __register_test(&_##fixture_name##_##test_name##_object); \
- } \
- static void fixture_name##_##test_name( \
- struct __test_metadata __attribute__((unused)) *_metadata, \
- _FIXTURE_DATA(fixture_name) __attribute__((unused)) *self)
-
-/* Exports a simple wrapper to run the test harness. */
-#define _TEST_HARNESS_MAIN \
- static void __attribute__((constructor)) __constructor_order_last(void) { \
- if (!__constructor_order) \
- __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD; \
- } \
- int seccomp_test_main(int argc, char **argv) { return test_harness_run(argc, argv); } // ANDROID
-
-#define _ASSERT_EQ(_expected, _seen) \
- __EXPECT(_expected, _seen, ==, 1)
-#define _ASSERT_NE(_expected, _seen) \
- __EXPECT(_expected, _seen, !=, 1)
-#define _ASSERT_LT(_expected, _seen) \
- __EXPECT(_expected, _seen, <, 1)
-#define _ASSERT_LE(_expected, _seen) \
- __EXPECT(_expected, _seen, <=, 1)
-#define _ASSERT_GT(_expected, _seen) \
- __EXPECT(_expected, _seen, >, 1)
-#define _ASSERT_GE(_expected, _seen) \
- __EXPECT(_expected, _seen, >=, 1)
-#define _ASSERT_NULL(_seen) \
- __EXPECT(NULL, _seen, ==, 1)
-
-#define _ASSERT_TRUE(_seen) \
- _ASSERT_NE(0, _seen)
-#define _ASSERT_FALSE(_seen) \
- _ASSERT_EQ(0, _seen)
-#define _ASSERT_STREQ(_expected, _seen) \
- __EXPECT_STR(_expected, _seen, ==, 1)
-#define _ASSERT_STRNE(_expected, _seen) \
- __EXPECT_STR(_expected, _seen, !=, 1)
-
-#define _EXPECT_EQ(_expected, _seen) \
- __EXPECT(_expected, _seen, ==, 0)
-#define _EXPECT_NE(_expected, _seen) \
- __EXPECT(_expected, _seen, !=, 0)
-#define _EXPECT_LT(_expected, _seen) \
- __EXPECT(_expected, _seen, <, 0)
-#define _EXPECT_LE(_expected, _seen) \
- __EXPECT(_expected, _seen, <=, 0)
-#define _EXPECT_GT(_expected, _seen) \
- __EXPECT(_expected, _seen, >, 0)
-#define _EXPECT_GE(_expected, _seen) \
- __EXPECT(_expected, _seen, >=, 0)
-
-#define _EXPECT_NULL(_seen) \
- __EXPECT(NULL, _seen, ==, 0)
-#define _EXPECT_TRUE(_seen) \
- _EXPECT_NE(0, _seen)
-#define _EXPECT_FALSE(_seen) \
- _EXPECT_EQ(0, _seen)
-
-#define _EXPECT_STREQ(_expected, _seen) \
- __EXPECT_STR(_expected, _seen, ==, 0)
-#define _EXPECT_STRNE(_expected, _seen) \
- __EXPECT_STR(_expected, _seen, !=, 0)
-
-/* Support an optional handler after and ASSERT_* or EXPECT_*. The approach is
- * not thread-safe, but it should be fine in most sane test scenarios.
- *
- * Using __bail(), which optionally abort()s, is the easiest way to early
- * return while still providing an optional block to the API consumer.
- */
-#define OPTIONAL_HANDLER(_assert) \
- for (; _metadata->trigger; _metadata->trigger = __bail(_assert))
-
-#define __EXPECT(_expected, _seen, _t, _assert) do { \
- /* Avoid multiple evaluation of the cases */ \
- __typeof__(_expected) __exp = (_expected); \
- __typeof__(_seen) __seen = (_seen); \
- if (!(__exp _t __seen)) { \
- unsigned long long __exp_print = 0; \
- unsigned long long __seen_print = 0; \
- /* Avoid casting complaints the scariest way we can. */ \
- memcpy(&__exp_print, &__exp, sizeof(__exp)); \
- memcpy(&__seen_print, &__seen, sizeof(__seen)); \
- __TH_LOG("Expected %s (%llu) %s %s (%llu)", \
- #_expected, __exp_print, #_t, \
- #_seen, __seen_print); \
- _metadata->passed = 0; \
- /* Ensure the optional handler is triggered */ \
- _metadata->trigger = 1; \
- } \
-} while (0); OPTIONAL_HANDLER(_assert)
-
-#define __EXPECT_STR(_expected, _seen, _t, _assert) do { \
- const char *__exp = (_expected); \
- const char *__seen = (_seen); \
- if (!(strcmp(__exp, __seen) _t (0))) { \
- __TH_LOG("Expected '%s' %s '%s'.", __exp, #_t, __seen); \
- _metadata->passed = 0; \
- _metadata->trigger = 1; \
- } \
-} while (0); OPTIONAL_HANDLER(_assert)
-
-/* Contains all the information for test execution and status checking. */
-struct __test_metadata {
- const char *name;
- void (*fn)(struct __test_metadata *);
- int termsig;
- int passed;
- int trigger; /* extra handler after the evaluation */
- struct __test_metadata *prev, *next;
-};
-
-/* Storage for the (global) tests to be run. */
-static struct __test_metadata *__test_list = NULL;
-static unsigned int __test_count = 0;
-static unsigned int __fixture_count = 0;
-static int __constructor_order = 0;
-
-#define _CONSTRUCTOR_ORDER_FORWARD 1
-#define _CONSTRUCTOR_ORDER_BACKWARD (-1)
-
-/*
- * Since constructors are called in reverse order, reverse the test
- * list so tests are run in source declaration order.
- * https://gcc.gnu.org/onlinedocs/gccint/Initialization.html
- * However, it seems not all toolchains do this correctly, so use
- * __constructor_order to detect which direction is called first
- * and adjust list building logic to get things running in the right
- * direction.
- */
-static inline void __register_test(struct __test_metadata *t) {
- __test_count++;
- /* Circular linked list where only prev is circular. */
- if (__test_list == NULL) {
- __test_list = t;
- t->next = NULL;
- t->prev = t;
- return;
- }
- if (__constructor_order == _CONSTRUCTOR_ORDER_FORWARD) {
- t->next = NULL;
- t->prev = __test_list->prev;
- t->prev->next = t;
- __test_list->prev = t;
- } else {
- t->next = __test_list;
- t->next->prev = t;
- t->prev = t;
- __test_list = t;
- }
-}
-
-static inline int __bail(int for_realz) {
- if (for_realz)
- abort();
- return 0;
-}
-
-void __run_test(struct __test_metadata *t) {
- pid_t child_pid;
- int status;
- t->passed = 1;
- t->trigger = 0;
- printf("[ RUN ] %s\n", t->name);
- child_pid = fork();
- if (child_pid < 0) {
- printf("ERROR SPAWNING TEST CHILD\n");
- t->passed = 0;
- } else if (child_pid == 0) {
- t->fn(t);
- _exit(t->passed);
- } else {
- /* TODO(wad) add timeout support. */
- waitpid(child_pid, &status, 0);
- if (WIFEXITED(status)) {
- t->passed = t->termsig == -1 ? WEXITSTATUS(status) : 0;
- if (t->termsig != -1) {
- fprintf(TH_LOG_STREAM,
- "%s: Test exited normally instead of by signal (code: %d)\n",
- t->name,
- WEXITSTATUS(status));
- }
- } else if (WIFSIGNALED(status)) {
- t->passed = 0;
- if (WTERMSIG(status) == SIGABRT) {
- fprintf(TH_LOG_STREAM,
- "%s: Test terminated by assertion\n",
- t->name);
- } else if (WTERMSIG(status) == t->termsig) {
- t->passed = 1;
- } else {
- fprintf(TH_LOG_STREAM,
- "%s: Test terminated unexpectedly by signal %d\n",
- t->name,
- WTERMSIG(status));
- }
- } else {
- fprintf(TH_LOG_STREAM,
- "%s: Test ended in some other way [%u]\n",
- t->name,
- status);
- }
- }
- printf("[ %4s ] %s\n", (t->passed ? "OK" : "FAIL"), t->name);
-}
-
-static int test_harness_run(int __attribute__((unused)) argc,
- char __attribute__((unused)) **argv) {
- struct __test_metadata *t;
- int ret = 0;
- unsigned int count = 0;
- unsigned int pass_count = 0;
-
- /* TODO(wad) add optional arguments similar to gtest. */
- printf("[==========] Running %u tests from %u test cases.\n",
- __test_count, __fixture_count + 1);
- for (t = __test_list; t; t = t->next) {
- count++;
- __run_test(t);
- if (t->passed)
- pass_count++;
- else
- ret = 1;
- }
- /* TODO(wad) organize by fixtures since ordering is not guaranteed now. */
- printf("[==========] %u / %u tests passed.\n", pass_count, count);
- printf("[ %s ]\n", (ret ? "FAILED" : "PASSED"));
- return ret;
-}
-
-static void __attribute__((constructor)) __constructor_order_first(void) {
- if (!__constructor_order)
- __constructor_order = _CONSTRUCTOR_ORDER_FORWARD;
-}
-
-#endif /* TEST_HARNESS_H_ */
diff --git a/tests/tests/os/src/android/os/cts/SeccompTest.java b/tests/tests/os/src/android/os/cts/SeccompTest.java
index d70a647..a58ed0d 100644
--- a/tests/tests/os/src/android/os/cts/SeccompTest.java
+++ b/tests/tests/os/src/android/os/cts/SeccompTest.java
@@ -98,8 +98,9 @@
"global.KILL_one_arg_one",
"global.KILL_one_arg_six",
"global.arg_out_of_range",
- "global.ERRNO_one",
- "global.ERRNO_one_ok",
+ "global.ERRNO_valid",
+ "global.ERRNO_zero",
+ /* "global.ERRNO_capped", // presently fails */
};
runKernelUnitTestSuite(tests);
}
@@ -180,9 +181,9 @@
private void runKernelUnitTestSuite(final String[] tests) {
for (final String test : tests) {
// TODO: Replace the URL with the documentation when it's finished.
- assertTrue(test + " failed. This test requires kernel functionality to pass. "
- + "Please go to http://XXXXX for instructions on how to enable or "
- + "backport the required functionality.",
+ assertTrue(test + " failed. This test requires kernel functionality to pass. Please go to "
+ + "http://source.android.com/devices/tech/config/kernel.html#Seccomp-BPF-TSYNC"
+ + " for instructions on how to enable or backport the required functionality.",
runKernelUnitTest(test));
}
}
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 13df2a9..4500969 100644
--- a/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
+++ b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
@@ -22,10 +22,15 @@
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.os.Environment;
+import android.os.ProxyFileDescriptorCallback;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.storage.OnObbStateChangeListener;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import android.test.AndroidTestCase;
import android.test.ComparisonFailure;
import android.util.Log;
@@ -36,7 +41,9 @@
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.InputStream;
+import java.io.SyncFailedException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -44,6 +51,8 @@
import java.util.Arrays;
import java.util.List;
+import junit.framework.AssertionFailedError;
+
public class StorageManagerTest extends AndroidTestCase {
private static final String TAG = StorageManager.class.getSimpleName();
@@ -243,6 +252,262 @@
assertStorageVolumesEquals(primary, childVolume);
}
+ private static class TestProxyFileDescriptorCallback extends ProxyFileDescriptorCallback {
+ final byte[] bytes;
+ int fsyncCount;
+ int releaseCount;
+ ErrnoException onGetSizeError = null;
+ ErrnoException onReadError = null;
+ ErrnoException onWriteError = null;
+ ErrnoException onFsyncError = null;
+
+ TestProxyFileDescriptorCallback(int size, String seed) {
+ final byte[] seedBytes = seed.getBytes();
+ bytes = new byte[size];
+ for (int i = 0; i < size; i++) {
+ bytes[i] = seedBytes[i % seedBytes.length];
+ }
+ }
+
+ @Override
+ public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
+ if (onWriteError != null) {
+ throw onWriteError;
+ }
+ for (int i = 0; i < size; i++) {
+ bytes[(int)(i + offset)] = data[i];
+ }
+ return size;
+ }
+
+ @Override
+ public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+ if (onReadError != null) {
+ throw onReadError;
+ }
+ final int len = (int)(Math.min(size, data.length - offset));
+ for (int i = 0; i < len; i++) {
+ data[i] = bytes[(int)(i + offset)];
+ }
+ return len;
+ }
+
+ @Override
+ public long onGetSize() throws ErrnoException {
+ if (onGetSizeError != null) {
+ throw onGetSizeError;
+ }
+ return bytes.length;
+ }
+
+ @Override
+ public void onFsync() throws ErrnoException {
+ if (onFsyncError != null) {
+ throw onFsyncError;
+ }
+ fsyncCount++;
+ }
+
+ @Override
+ public void onRelease() {
+ releaseCount++;
+ }
+ }
+
+ public void testOpenProxyFileDescriptor() throws Exception {
+ final TestProxyFileDescriptorCallback appleCallback =
+ new TestProxyFileDescriptorCallback(1024 * 1024, "Apple");
+ final TestProxyFileDescriptorCallback orangeCallback =
+ new TestProxyFileDescriptorCallback(1024 * 128, "Orange");
+ final TestProxyFileDescriptorCallback cherryCallback =
+ new TestProxyFileDescriptorCallback(1024 * 1024, "Cherry");
+ try (final ParcelFileDescriptor appleFd = mStorageManager.openProxyFileDescriptor(
+ ParcelFileDescriptor.MODE_READ_ONLY, appleCallback);
+ final ParcelFileDescriptor orangeFd = mStorageManager.openProxyFileDescriptor(
+ ParcelFileDescriptor.MODE_WRITE_ONLY, orangeCallback);
+ final ParcelFileDescriptor cherryFd = mStorageManager.openProxyFileDescriptor(
+ ParcelFileDescriptor.MODE_READ_WRITE, cherryCallback)) {
+ // Stat
+ assertEquals(appleCallback.onGetSize(), appleFd.getStatSize());
+ assertEquals(orangeCallback.onGetSize(), orangeFd.getStatSize());
+ assertEquals(cherryCallback.onGetSize(), cherryFd.getStatSize());
+
+ final byte[] bytes = new byte[100];
+
+ // Read
+ for (int i = 0; i < 2; i++) {
+ Os.read(appleFd.getFileDescriptor(), bytes, 0, 100);
+ for (int j = 0; j < 100; j++) {
+ assertEquals(appleCallback.bytes[i * 100 + j], bytes[j]);
+ }
+ }
+ try {
+ Os.read(orangeFd.getFileDescriptor(), bytes, 0, 100);
+ fail();
+ } catch (ErrnoException exp) {
+ assertEquals(OsConstants.EBADF, exp.errno);
+ }
+ for (int i = 0; i < 2; i++) {
+ Os.read(cherryFd.getFileDescriptor(), bytes, 0, 100);
+ for (int j = 0; j < 100; j++) {
+ assertEquals(cherryCallback.bytes[i * 100 + j], bytes[j]);
+ }
+ }
+
+ // Pread
+ Os.pread(appleFd.getFileDescriptor(), bytes, 0, 100, 500);
+ for (int j = 0; j < 100; j++) {
+ assertEquals(appleCallback.bytes[500 + j], bytes[j]);
+ }
+ try {
+ Os.pread(orangeFd.getFileDescriptor(), bytes, 0, 100, 500);
+ fail();
+ } catch (ErrnoException exp) {
+ assertEquals(OsConstants.EBADF, exp.errno);
+ }
+ Os.pread(cherryFd.getFileDescriptor(), bytes, 0, 100, 500);
+ for (int j = 0; j < 100; j++) {
+ assertEquals(cherryCallback.bytes[500 + j], bytes[j]);
+ }
+
+ // Write
+ final byte[] writeSeed = "Strawberry".getBytes();
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = writeSeed[i % writeSeed.length];
+ }
+ try {
+ Os.write(appleFd.getFileDescriptor(), bytes, 0, 100);
+ fail();
+ } catch (ErrnoException exp) {
+ assertEquals(OsConstants.EBADF, exp.errno);
+ }
+ for (int i = 0; i < 2; i++) {
+ Os.write(orangeFd.getFileDescriptor(), bytes, 0, 100);
+ for (int j = 0; j < 100; j++) {
+ assertEquals(bytes[j], orangeCallback.bytes[i * 100 + j]);
+ }
+ }
+ Os.lseek(cherryFd.getFileDescriptor(), 0, OsConstants.SEEK_SET);
+ for (int i = 0; i < 2; i++) {
+ Os.write(cherryFd.getFileDescriptor(), bytes, 0, 100);
+ for (int j = 0; j < 100; j++) {
+ assertEquals(bytes[j], cherryCallback.bytes[i * 100 + j]);
+ }
+ }
+
+ // Pwrite
+ try {
+ Os.pwrite(appleFd.getFileDescriptor(), bytes, 0, 100, 500);
+ fail();
+ } catch (ErrnoException exp) {
+ assertEquals(OsConstants.EBADF, exp.errno);
+ }
+ Os.pwrite(orangeFd.getFileDescriptor(), bytes, 0, 100, 500);
+ for (int j = 0; j < 100; j++) {
+ assertEquals(bytes[j], orangeCallback.bytes[500 + j]);
+ }
+ Os.pwrite(cherryFd.getFileDescriptor(), bytes, 0, 100, 500);
+ for (int j = 0; j < 100; j++) {
+ assertEquals(bytes[j], cherryCallback.bytes[500 + j]);
+ }
+
+ // Flush
+ assertEquals(0, appleCallback.fsyncCount);
+ assertEquals(0, orangeCallback.fsyncCount);
+ assertEquals(0, cherryCallback.fsyncCount);
+ appleFd.getFileDescriptor().sync();
+ orangeFd.getFileDescriptor().sync();
+ cherryFd.getFileDescriptor().sync();
+ assertEquals(1, appleCallback.fsyncCount);
+ assertEquals(1, orangeCallback.fsyncCount);
+ assertEquals(1, cherryCallback.fsyncCount);
+
+ // Before release
+ assertEquals(0, appleCallback.releaseCount);
+ assertEquals(0, orangeCallback.releaseCount);
+ assertEquals(0, cherryCallback.releaseCount);
+ }
+
+ // Release
+ int retry = 3;
+ while (true) {
+ try {
+ assertEquals(1, appleCallback.releaseCount);
+ assertEquals(1, orangeCallback.releaseCount);
+ assertEquals(1, cherryCallback.releaseCount);
+ break;
+ } catch (AssertionFailedError error) {
+ if (retry-- > 0) {
+ Thread.sleep(500);
+ continue;
+ } else {
+ throw error;
+ }
+ }
+ }
+ }
+
+ public void testOpenProxyFileDescriptor_error() throws Exception {
+ final TestProxyFileDescriptorCallback callback =
+ new TestProxyFileDescriptorCallback(1024 * 1024, "Error");
+ final byte[] bytes = new byte[128];
+ callback.onGetSizeError = new ErrnoException("onGetSize", OsConstants.ENOENT);
+ try {
+ try (final ParcelFileDescriptor fd = mStorageManager.openProxyFileDescriptor(
+ ParcelFileDescriptor.MODE_READ_WRITE, callback)) {}
+ fail();
+ } catch (IOException exp) {}
+ callback.onGetSizeError = null;
+
+ try (final ParcelFileDescriptor fd = mStorageManager.openProxyFileDescriptor(
+ ParcelFileDescriptor.MODE_READ_WRITE, callback)) {
+ callback.onReadError = new ErrnoException("onRead", OsConstants.EIO);
+ try {
+ Os.read(fd.getFileDescriptor(), bytes, 0, bytes.length);
+ fail();
+ } catch (ErrnoException error) {}
+
+ callback.onReadError = new ErrnoException("onRead", OsConstants.ENOSYS);
+ try {
+ Os.read(fd.getFileDescriptor(), bytes, 0, bytes.length);
+ fail();
+ } catch (ErrnoException error) {}
+
+ callback.onReadError = null;
+ Os.read(fd.getFileDescriptor(), bytes, 0, bytes.length);
+
+ callback.onWriteError = new ErrnoException("onWrite", OsConstants.EIO);
+ try {
+ Os.write(fd.getFileDescriptor(), bytes, 0, bytes.length);
+ fail();
+ } catch (ErrnoException error) {}
+
+ callback.onWriteError = new ErrnoException("onWrite", OsConstants.ENOSYS);
+ try {
+ Os.write(fd.getFileDescriptor(), bytes, 0, bytes.length);
+ fail();
+ } catch (ErrnoException error) {}
+
+ callback.onWriteError = null;
+ Os.write(fd.getFileDescriptor(), bytes, 0, bytes.length);
+
+ callback.onFsyncError = new ErrnoException("onFsync", OsConstants.EIO);
+ try {
+ fd.getFileDescriptor().sync();
+ fail();
+ } catch (SyncFailedException error) {}
+
+ callback.onFsyncError = new ErrnoException("onFsync", OsConstants.ENOSYS);
+ try {
+ fd.getFileDescriptor().sync();
+ fail();
+ } catch (SyncFailedException error) {}
+
+ callback.onFsyncError = null;
+ fd.getFileDescriptor().sync();
+ }
+ }
+
private void assertStorageVolumesEquals(StorageVolume volume, StorageVolume clone)
throws Exception {
// Asserts equals() method.
diff --git a/tests/tests/permission/Android.mk b/tests/tests/permission/Android.mk
index af0f8c4..68fa193 100644
--- a/tests/tests/permission/Android.mk
+++ b/tests/tests/permission/Android.mk
@@ -30,7 +30,10 @@
LOCAL_JAVA_LIBRARIES := telephony-common
LOCAL_STATIC_JAVA_LIBRARIES := \
- ctstestrunner guava android-ex-camera2
+ ctstestrunner \
+ guava \
+ android-ex-camera2 \
+ legacy-android-test
LOCAL_JNI_SHARED_LIBRARIES := libctspermission_jni libnativehelper_compat_libc++
diff --git a/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
index 2a4b6da..e2112aa 100644
--- a/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
@@ -244,6 +244,32 @@
}
+ /**
+ * Verify that TelephonyManager.getImei requires Permission.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE}.
+ */
+ @Test
+ public void testGetImei() {
+ if (!mHasTelephony) {
+ return;
+ }
+
+ try {
+ String imei = mTelephonyManager.getImei();
+ fail("Got IMEI: " + imei);
+ } catch (SecurityException e) {
+ // expected
+ }
+ try {
+ String imei = mTelephonyManager.getImei(0);
+ fail("Got IMEI: " + imei);
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
private static Context getContext() {
return InstrumentationRegistry.getContext();
}
diff --git a/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java b/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
index e1505f7..11dfb99 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
@@ -105,8 +105,11 @@
assertTrue("Not whitelisted permissions are granted for package "
+ pkg.packageName + ": " + grantedNotInWhitelist,
grantedNotInWhitelist.isEmpty());
- }
+ assertTrue("Requested permissions not granted for package "
+ + pkg.packageName + ": " + notGranted,
+ notGranted.isEmpty());
+ }
}
}
diff --git a/tests/tests/provider/Android.mk b/tests/tests/provider/Android.mk
index 6a7d871..ce0a890 100644
--- a/tests/tests/provider/Android.mk
+++ b/tests/tests/provider/Android.mk
@@ -31,7 +31,11 @@
LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util ctstestrunner ub-uiautomator
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ junit \
+ legacy-android-test
LOCAL_JNI_SHARED_LIBRARIES := libcts_jni libnativehelper_compat_libc++
diff --git a/tests/tests/security/Android.mk b/tests/tests/security/Android.mk
index b335065..ddd78f1 100644
--- a/tests/tests/security/Android.mk
+++ b/tests/tests/security/Android.mk
@@ -21,7 +21,15 @@
# Include both the 32 and 64 bit versions
LOCAL_MULTILIB := both
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestserver ctstestrunner compatibility-device-util guava
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-common \
+ ctstestserver \
+ ctstestrunner \
+ compatibility-device-util \
+ guava \
+ platform-test-annotations \
+ legacy-android-test
LOCAL_JAVA_LIBRARIES := android.test.runner org.apache.http.legacy
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 757d719..6e67855 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -50,6 +50,14 @@
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
</intent-filter>
</activity>
+ <activity
+ android:name="android.security.cts.MotionEventTestActivity"
+ android:label="Test MotionEvent">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+ </intent-filter>
+ </activity>
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/security/res/raw/bug_33137046.mp4 b/tests/tests/security/res/raw/bug_33137046.mp4
new file mode 100644
index 0000000..01f49b2
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_33137046.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_33300701.tiff b/tests/tests/security/res/raw/bug_33300701.tiff
new file mode 100644
index 0000000..ea7a477
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_33300701.tiff
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/AudioSecurityTest.java b/tests/tests/security/src/android/security/cts/AudioSecurityTest.java
new file mode 100644
index 0000000..ecad5dc
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/AudioSecurityTest.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2016 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.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.media.audiofx.AudioEffect;
+import android.util.Log;
+
+import com.android.compatibility.common.util.CtsAndroidTestCase;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.UUID;
+
+public class AudioSecurityTest extends CtsAndroidTestCase {
+ private static final String TAG = "AudioSecurityTest";
+
+ private static final int ERROR_DEAD_OBJECT = -7; // AudioEffect.ERROR_DEAD_OBJECT
+
+ // should match audio_effect.h (native)
+ private static final int EFFECT_CMD_SET_PARAM = 5;
+ private static final int EFFECT_CMD_GET_PARAM = 8;
+ private static final int EFFECT_CMD_OFFLOAD = 20;
+ private static final int SIZEOF_EFFECT_PARAM_T = 12;
+
+ private static void verifyZeroReply(byte[] reply) throws Exception {
+ int count = 0;
+ for (byte b : reply) {
+ if (b != 0) {
+ count++;
+ }
+ }
+ assertEquals("reply has " + count + " nonzero values", 0 /* expected */, count);
+ }
+
+ // @FunctionalInterface
+ private interface TestEffect {
+ void test(AudioEffect audioEffect) throws Exception;
+ }
+
+ private static void testAllEffects(String testName, TestEffect testEffect) throws Exception {
+ int failures = 0;
+ for (AudioEffect.Descriptor descriptor : AudioEffect.queryEffects()) {
+ final AudioEffect audioEffect;
+ try {
+ audioEffect = (AudioEffect)AudioEffect.class.getConstructor(
+ UUID.class, UUID.class, int.class, int.class).newInstance(
+ descriptor.type,
+ descriptor.uuid, // uuid overrides type
+ 0 /* priority */, 0 /* audioSession */);
+ } catch (Exception e) {
+ Log.w(TAG, "effect " + testName + " " + descriptor.name
+ + " cannot be created (ignoring)");
+ continue; // OK;
+ }
+ try {
+ testEffect.test(audioEffect);
+ Log.d(TAG, "effect " + testName + " " + descriptor.name + " success");
+ } catch (Exception e) {
+ Log.e(TAG, "effect " + testName + " " + descriptor.name + " failed!");
+ ++failures;
+ } catch (AssertionError e) {
+ Log.e(TAG, "effect " + testName + " " + descriptor.name + " failed!");
+ ++failures;
+ }
+ }
+ assertEquals("found " + testName + " " + failures + " failures",
+ 0 /* expected */, failures);
+ }
+
+ // b/28173666
+ public void testAllEffectsGetParameterAttemptOffload_CVE_2016_3745() throws Exception {
+ testAllEffects("get parameter attempt offload",
+ new TestEffect() {
+ @Override
+ public void test(AudioEffect audioEffect) throws Exception {
+ testAudioEffectGetParameter(audioEffect, true /* offload */);
+ }
+ });
+ }
+
+ // b/32438594
+ // b/32624850
+ // b/32635664
+ public void testAllEffectsGetParameter2AttemptOffload_CVE_2017_0398() throws Exception {
+ testAllEffects("get parameter2 attempt offload",
+ new TestEffect() {
+ @Override
+ public void test(AudioEffect audioEffect) throws Exception {
+ testAudioEffectGetParameter2(audioEffect, true /* offload */);
+ }
+ });
+ }
+
+ // b/30204301
+ public void testAllEffectsSetParameterAttemptOffload_CVE_2016_3924() throws Exception {
+ testAllEffects("set parameter attempt offload",
+ new TestEffect() {
+ @Override
+ public void test(AudioEffect audioEffect) throws Exception {
+ testAudioEffectSetParameter(audioEffect, true /* offload */);
+ }
+ });
+ }
+
+ private static void testAudioEffectGetParameter(
+ AudioEffect audioEffect, boolean offload) throws Exception {
+ if (audioEffect == null) {
+ return;
+ }
+ try {
+ // 1) set offload_enabled
+ if (offload) {
+ byte command[] = new byte[8];
+ Arrays.fill(command, (byte)1);
+ byte reply[] = new byte[4]; // ignored
+
+ /* ignored */ AudioEffect.class.getDeclaredMethod(
+ "command", int.class, byte[].class, byte[].class).invoke(
+ audioEffect, EFFECT_CMD_OFFLOAD, command, reply);
+ }
+
+ // 2) get parameter with invalid psize
+ {
+ byte command[] = new byte[30];
+ Arrays.fill(command, (byte)0xDD);
+ byte reply[] = new byte[30];
+
+ Integer ret = (Integer) AudioEffect.class.getDeclaredMethod(
+ "command", int.class, byte[].class, byte[].class).invoke(
+ audioEffect, EFFECT_CMD_GET_PARAM, command, reply);
+
+ assertTrue("Audio server might have crashed", ret != ERROR_DEAD_OBJECT);
+ verifyZeroReply(reply);
+ }
+
+ // NOTE: an alternative way of checking crash:
+ //
+ // Thread.sleep(1000 /* millis */);
+ // assertTrue("Audio server might have crashed",
+ // audioEffect.setEnabled(false) != AudioEffect.ERROR_DEAD_OBJECT);
+ } catch (NoSuchMethodException e) {
+ Log.w(TAG, "AudioEffect.command() does not exist (ignoring)"); // OK
+ } finally {
+ audioEffect.release();
+ }
+ }
+
+ private static void testAudioEffectGetParameter2(
+ AudioEffect audioEffect, boolean offload) throws Exception {
+ if (audioEffect == null) {
+ return;
+ }
+ try {
+ // 1) set offload_enabled
+ if (offload) {
+ byte command[] = new byte[8];
+ Arrays.fill(command, (byte)1);
+ byte reply[] = new byte[4]; // ignored
+
+ /* ignored */ AudioEffect.class.getDeclaredMethod(
+ "command", int.class, byte[].class, byte[].class).invoke(
+ audioEffect, EFFECT_CMD_OFFLOAD, command, reply);
+ }
+
+ // 2) get parameter with small command size but large psize
+ {
+ final int parameterSize = 0x100000;
+
+ byte command[] = ByteBuffer.allocate(5 * 4 /* capacity */)
+ .order(ByteOrder.nativeOrder())
+ .putInt(0) // status (unused)
+ .putInt(parameterSize) // psize (very large)
+ .putInt(0) // vsize
+ .putInt(0x04030201) // data[0] (param too small for psize)
+ .putInt(0x08070605) // data[4]
+ .array();
+ byte reply[] = new byte[parameterSize + SIZEOF_EFFECT_PARAM_T];
+
+ Integer ret = (Integer) AudioEffect.class.getDeclaredMethod(
+ "command", int.class, byte[].class, byte[].class).invoke(
+ audioEffect, EFFECT_CMD_GET_PARAM, command, reply);
+
+ verifyZeroReply(reply);
+ assertTrue("Audio server might have crashed", ret != ERROR_DEAD_OBJECT);
+ }
+ } catch (NoSuchMethodException e) {
+ Log.w(TAG, "AudioEffect.command() does not exist (ignoring)"); // OK
+ } finally {
+ audioEffect.release();
+ }
+ }
+
+ private static void testAudioEffectGetParameter3(AudioEffect audioEffect) throws Exception {
+ if (audioEffect == null) {
+ return;
+ }
+ try {
+ // 1) get parameter with zero command size
+ {
+ final int parameterSize = 0x10;
+
+ Integer ret = (Integer) AudioEffect.class.getDeclaredMethod(
+ "command", int.class, byte[].class, byte[].class).invoke(
+ audioEffect,
+ EFFECT_CMD_GET_PARAM,
+ new byte[0] /* command */,
+ new byte[parameterSize + SIZEOF_EFFECT_PARAM_T] /* reply */);
+
+ assertTrue("Audio server might have crashed", ret != ERROR_DEAD_OBJECT);
+ }
+ } catch (NoSuchMethodException e) {
+ Log.w(TAG, "AudioEffect.command() does not exist (ignoring)"); // OK
+ } finally {
+ audioEffect.release();
+ }
+ }
+
+ private static void testAudioEffectSetParameter(
+ AudioEffect audioEffect, boolean offload) throws Exception {
+ if (audioEffect == null) {
+ return;
+ }
+ try {
+ // 1) set offload_enabled
+ if (offload) {
+ byte command[] = new byte[8];
+ Arrays.fill(command, (byte)1);
+ byte reply[] = new byte[4]; // ignored
+
+ /* ignored */ AudioEffect.class.getDeclaredMethod(
+ "command", int.class, byte[].class, byte[].class).invoke(
+ audioEffect, EFFECT_CMD_OFFLOAD, command, reply);
+ }
+
+ // 2) set parameter with invalid psize
+ {
+ byte command[] = ByteBuffer.allocate(5 * 4 /* capacity */)
+ .order(ByteOrder.nativeOrder())
+ .putInt(0) // status (unused)
+ .putInt(0xdddddddd) // psize (very large)
+ .putInt(4) // vsize
+ .putInt(1) // data[0] (param too small for psize)
+ .putInt(0) // data[4]
+ .array();
+ byte reply[] = new byte[4]; // returns status code (ignored)
+
+ Integer ret = (Integer) AudioEffect.class.getDeclaredMethod(
+ "command", int.class, byte[].class, byte[].class).invoke(
+ audioEffect, EFFECT_CMD_SET_PARAM, command, reply);
+
+ assertTrue("Audio server might have crashed", ret != ERROR_DEAD_OBJECT);
+ // on failure reply may contain the status code.
+ }
+ } catch (NoSuchMethodException e) {
+ Log.w(TAG, "AudioEffect.command() does not exist (ignoring)"); // OK
+ } finally {
+ audioEffect.release();
+ }
+ }
+
+ private static void testAudioEffectSetOffload(AudioEffect audioEffect) throws Exception {
+ if (audioEffect == null) {
+ return;
+ }
+ try {
+ // 1) set offload_enabled with zero command and reply size
+ {
+ Integer ret = (Integer) AudioEffect.class.getDeclaredMethod(
+ "command", int.class, byte[].class, byte[].class).invoke(
+ audioEffect,
+ EFFECT_CMD_OFFLOAD,
+ new byte[0] /* command */,
+ new byte[0] /* reply */);
+
+ assertTrue("Audio server might have crashed", ret != ERROR_DEAD_OBJECT);
+ }
+ } catch (NoSuchMethodException e) {
+ Log.w(TAG, "AudioEffect.command() does not exist (ignoring)"); // OK
+ } finally {
+ audioEffect.release();
+ }
+ }
+
+ // should match effect_visualizer.h (native)
+ private static final String VISUALIZER_TYPE = "e46b26a0-dddd-11db-8afd-0002a5d5c51b";
+ private static final int VISUALIZER_CMD_CAPTURE = 0x10000;
+ private static final int VISUALIZER_PARAM_CAPTURE_SIZE = 0;
+
+ // b/31781965
+ public void testVisualizerCapture_CVE_2017_0396() throws Exception {
+ // Capture params
+ final int CAPTURE_SIZE = 1 << 24; // 16MB seems to be large enough to cause a SEGV.
+ final byte[] captureBuf = new byte[CAPTURE_SIZE];
+
+ // Track params
+ final int sampleRate = 48000;
+ final int format = AudioFormat.ENCODING_PCM_16BIT;
+ final int loops = 1;
+ final int seconds = 1;
+ final int channelCount = 2;
+ final int bufferFrames = seconds * sampleRate;
+ final int bufferSamples = bufferFrames * channelCount;
+ final int bufferSize = bufferSamples * 2; // bytes per sample for 16 bits
+ final short data[] = new short[bufferSamples]; // zero data
+
+ for (AudioEffect.Descriptor descriptor : AudioEffect.queryEffects()) {
+ if (descriptor.type.compareTo(UUID.fromString(VISUALIZER_TYPE)) != 0) {
+ continue;
+ }
+
+ AudioEffect audioEffect = null;
+ AudioTrack audioTrack = null;
+
+ try {
+ // create track and play
+ {
+ audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
+ AudioFormat.CHANNEL_OUT_STEREO, format, bufferSize,
+ AudioTrack.MODE_STATIC);
+ assertEquals("Cannot write to audio track",
+ bufferSamples,
+ audioTrack.write(data, 0 /* offsetInBytes */, data.length));
+ assertEquals("AudioTrack not initialized",
+ AudioTrack.STATE_INITIALIZED,
+ audioTrack.getState());
+ assertEquals("Cannot set loop points",
+ android.media.AudioTrack.SUCCESS,
+ audioTrack.setLoopPoints(0 /* startInFrames */, bufferFrames, loops));
+ audioTrack.play();
+ }
+
+ // wait for track to really begin playing
+ Thread.sleep(200 /* millis */);
+
+ // create effect
+ {
+ audioEffect = (AudioEffect) AudioEffect.class.getConstructor(
+ UUID.class, UUID.class, int.class, int.class).newInstance(
+ descriptor.type, descriptor.uuid, 0 /* priority */,
+ audioTrack.getAudioSessionId());
+ }
+
+ // set capture size
+ {
+ byte command[] = ByteBuffer.allocate(5 * 4 /* capacity */)
+ .order(ByteOrder.nativeOrder())
+ .putInt(0) // status (unused)
+ .putInt(4) // psize (sizeof(param))
+ .putInt(4) // vsize (sizeof(value))
+ .putInt(VISUALIZER_PARAM_CAPTURE_SIZE) // data[0] (param)
+ .putInt(CAPTURE_SIZE) // data[4] (value)
+ .array();
+
+ Integer ret = (Integer) AudioEffect.class.getDeclaredMethod(
+ "command", int.class, byte[].class, byte[].class).invoke(
+ audioEffect,
+ EFFECT_CMD_SET_PARAM,
+ command, new byte[4] /* reply */);
+ Log.d(TAG, "setparam returns " + ret);
+ assertTrue("Audio server might have crashed", ret != ERROR_DEAD_OBJECT);
+ }
+
+ // enable effect
+ {
+ final int ret = audioEffect.setEnabled(true);
+ assertEquals("Cannot enable audio effect", 0 /* expected */, ret);
+ }
+
+ // wait for track audio data to be processed, otherwise capture
+ // will not really return audio data.
+ Thread.sleep(200 /* millis */);
+
+ // capture data
+ {
+ Integer ret = (Integer) AudioEffect.class.getDeclaredMethod(
+ "command", int.class, byte[].class, byte[].class).invoke(
+ audioEffect,
+ VISUALIZER_CMD_CAPTURE,
+ new byte[0] /* command */, captureBuf /* reply */);
+ Log.d(TAG, "capture returns " + ret);
+ assertTrue("Audio server might have crashed", ret != ERROR_DEAD_OBJECT);
+ }
+ } finally {
+ if (audioEffect != null) {
+ audioEffect.release();
+ }
+ if (audioTrack != null) {
+ audioTrack.release();
+ }
+ }
+ }
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/MotionEventTest.java b/tests/tests/security/src/android/security/cts/MotionEventTest.java
new file mode 100644
index 0000000..b407f5c
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/MotionEventTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2017 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.assertEquals;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Gravity;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class MotionEventTest {
+ private static final String TAG = "MotionEventTest";
+ private Activity mActivity;
+ private Instrumentation mInstrumentation;
+
+ @Rule
+ public ActivityTestRule<MotionEventTestActivity> mActivityRule =
+ new ActivityTestRule<>(MotionEventTestActivity.class);
+
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mActivity = mActivityRule.getActivity();
+ PollingCheck.waitFor(mActivity::hasWindowFocus);
+ }
+
+ /**
+ * Test for whether ACTION_OUTSIDE events contain information about whether touches are
+ * obscured.
+ *
+ * If ACTION_OUTSIDE_EVENTS contain information about whether the touch is obscured, then a
+ * pattern of invisible, untouchable, unfocusable SYSTEM_ALERT_WINDOWS can be placed across the
+ * screen to determine approximate locations of touch events without the user knowing.
+ */
+ @Test
+ public void testActionOutsideDoesNotContainedObscuredInformation() throws Exception {
+ enableAppOps();
+ final OnTouchListener listener = new OnTouchListener();
+ final Point size = new Point();
+ final View[] viewHolder = new View[1];
+ mActivity.runOnUiThread(() -> {
+ final WindowManager wm = mActivity.getSystemService(WindowManager.class);
+ wm.getDefaultDisplay().getSize(size);
+
+ WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
+ WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+ wmlp.width = size.x / 4;
+ wmlp.height = size.y / 4;
+ wmlp.gravity = Gravity.TOP | Gravity.LEFT;
+ wmlp.setTitle(mActivity.getPackageName());
+
+ ViewGroup.LayoutParams vglp = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+
+ View v = new View(mActivity);
+ v.setOnTouchListener(listener);
+ v.setBackgroundColor(Color.GREEN);
+ v.setLayoutParams(vglp);
+ wm.addView(v, wmlp);
+
+ wmlp.gravity = Gravity.TOP | Gravity.RIGHT;
+
+ v = new View(mActivity);
+ v.setBackgroundColor(Color.BLUE);
+ v.setOnTouchListener(listener);
+ v.setLayoutParams(vglp);
+ viewHolder[0] = v;
+
+ wm.addView(v, wmlp);
+ });
+ mInstrumentation.waitForIdleSync();
+
+ FutureTask<Point> task = new FutureTask<>(() -> {
+ final int[] viewLocation = new int[2];
+ viewHolder[0].getLocationOnScreen(viewLocation);
+ return new Point(viewLocation[0], viewLocation[1]);
+ });
+ mActivity.runOnUiThread(task);
+ Point viewLocation = task.get(5, TimeUnit.SECONDS);
+ injectTap(viewLocation.x, viewLocation.y);
+
+ List<MotionEvent> outsideEvents = listener.getOutsideEvents();
+ assertEquals(2, outsideEvents.size());
+ for (MotionEvent e : outsideEvents) {
+ assertEquals(0, e.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED);
+ }
+ }
+
+
+ private void enableAppOps() {
+ StringBuilder cmd = new StringBuilder();
+ cmd.append("appops set ");
+ cmd.append(mInstrumentation.getContext().getPackageName());
+ cmd.append(" android:system_alert_window allow");
+ mInstrumentation.getUiAutomation().executeShellCommand(cmd.toString());
+
+ StringBuilder query = new StringBuilder();
+ query.append("appops get ");
+ query.append(mInstrumentation.getContext().getPackageName());
+ query.append(" android:system_alert_window");
+ String queryStr = query.toString();
+
+ String result;
+ do {
+ ParcelFileDescriptor pfd =
+ mInstrumentation.getUiAutomation().executeShellCommand(queryStr);
+ InputStream inputStream = new FileInputStream(pfd.getFileDescriptor());
+ result = convertStreamToString(inputStream);
+ } while (result.contains("No operations"));
+ }
+
+ private String convertStreamToString(InputStream is) {
+ try (Scanner s = new Scanner(is).useDelimiter("\\A")) {
+ return s.hasNext() ? s.next() : "";
+ }
+ }
+
+ private void injectTap(int x, int y) {
+ long downTime = SystemClock.uptimeMillis();
+ injectEvent(MotionEvent.ACTION_DOWN, x, y, downTime);
+ injectEvent(MotionEvent.ACTION_UP, x, y, downTime);
+ }
+
+ private void injectEvent(int action, int x, int y, long downTime) {
+ final UiAutomation automation = mInstrumentation.getUiAutomation();
+ final long eventTime = SystemClock.uptimeMillis();
+ MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, 0);
+ event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ automation.injectInputEvent(event, true);
+ event.recycle();
+ }
+
+ private static class OnTouchListener implements View.OnTouchListener {
+ private List<MotionEvent> mOutsideEvents;
+
+ public OnTouchListener() {
+ mOutsideEvents = new ArrayList<>();
+ }
+
+ public boolean onTouch(View v, MotionEvent e) {
+ if (e.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ mOutsideEvents.add(MotionEvent.obtain(e));
+ }
+ return true;
+ }
+
+ public List<MotionEvent> getOutsideEvents() {
+ return mOutsideEvents;
+ }
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/MotionEventTestActivity.java b/tests/tests/security/src/android/security/cts/MotionEventTestActivity.java
new file mode 100644
index 0000000..d986aff
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/MotionEventTestActivity.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2017 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.security.cts.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class MotionEventTestActivity extends Activity { }
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index 50e6edf..e0642ba 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -33,6 +33,7 @@
import android.media.MediaCodecList;
import android.media.MediaExtractor;
import android.media.MediaFormat;
+import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.opengl.GLES20;
import android.opengl.GLES11Ext;
@@ -68,6 +69,10 @@
before any existing test methods
***********************************************************/
+ public void testStagefright_bug_33137046() throws Exception {
+ doStagefrightTest(R.raw.bug_33137046);
+ }
+
public void testStagefright_cve_2016_2507() throws Exception {
doStagefrightTest(R.raw.cve_2016_2507);
}
@@ -220,6 +225,7 @@
private void doStagefrightTest(final int rid) throws Exception {
doStagefrightTestMediaPlayer(rid);
doStagefrightTestMediaCodec(rid);
+ doStagefrightTestMediaMetadataRetriever(rid);
}
private Surface getDummySurface() {
@@ -502,4 +508,60 @@
thr.stopLooper();
thr.join();
}
+ private void doStagefrightTestMediaMetadataRetriever(final int rid) throws Exception {
+
+ final MediaPlayerCrashListener mpcl = new MediaPlayerCrashListener();
+
+ LooperThread thr = new LooperThread(new Runnable() {
+ @Override
+ public void run() {
+
+ MediaPlayer mp = new MediaPlayer();
+ mp.setOnErrorListener(mpcl);
+ try {
+ AssetFileDescriptor fd = getInstrumentation().getContext().getResources()
+ .openRawResourceFd(R.raw.good);
+
+ // the onErrorListener won't receive MEDIA_ERROR_SERVER_DIED until
+ // setDataSource has been called
+ mp.setDataSource(fd.getFileDescriptor(),
+ fd.getStartOffset(),
+ fd.getLength());
+ } catch (Exception e) {
+ // this is a known-good file, so no failure should occur
+ fail("setDataSource of known-good file failed");
+ }
+
+ synchronized(mpcl) {
+ mpcl.notify();
+ }
+ Looper.loop();
+ mp.release();
+ }
+ });
+ thr.start();
+ // wait until the thread has initialized the MediaPlayer
+ synchronized(mpcl) {
+ mpcl.wait();
+ }
+
+ Resources resources = getInstrumentation().getContext().getResources();
+ AssetFileDescriptor fd = resources.openRawResourceFd(rid);
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ try {
+ retriever.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
+ retriever.getEmbeddedPicture();
+ retriever.getFrameAtTime();
+
+ retriever.release();
+ String rname = resources.getResourceEntryName(rid);
+ String cve = rname.replace("_", "-").toUpperCase();
+ assertFalse("Device *IS* vulnerable to " + cve,
+ mpcl.waitForError() == MediaPlayer.MEDIA_ERROR_SERVER_DIED);
+ thr.stopLooper();
+ }
}
diff --git a/tests/tests/security/src/android/security/cts/ZeroHeightTiffTest.java b/tests/tests/security/src/android/security/cts/ZeroHeightTiffTest.java
new file mode 100644
index 0000000..f81da6b
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/ZeroHeightTiffTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 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.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.test.AndroidTestCase;
+
+import java.io.InputStream;
+
+import android.security.cts.R;
+
+public class ZeroHeightTiffTest extends AndroidTestCase {
+ /**
+ * Verifies that the device fails to decode a zero height tiff file.
+ *
+ * Prior to fixing bug 33300701, decoding resulted in undefined behavior (divide by zero).
+ * With the fix, decoding will fail, without dividing by zero.
+ */
+ public void test_android_bug_33300701() {
+ InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_33300701);
+ Bitmap bitmap = BitmapFactory.decodeStream(exploitImage);
+ assertNull(bitmap);
+ }
+}
diff --git a/tests/tests/shortcutmanager/AndroidManifest.xml b/tests/tests/shortcutmanager/AndroidManifest.xml
index 1fc96fb..d69ce5b 100755
--- a/tests/tests/shortcutmanager/AndroidManifest.xml
+++ b/tests/tests/shortcutmanager/AndroidManifest.xml
@@ -43,6 +43,13 @@
</intent-filter>
</activity-alias>
+ <activity-alias android:name="main_shortcut_config"
+ android:targetActivity="android.content.pm.cts.shortcutmanager.MyActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.CREATE_SHORTCUT" />
+ </intent-filter>
+ </activity-alias>
+
<!-- It's not exporeted, but should still be launchable. -->
<activity android:name="android.content.pm.cts.shortcutmanager.ShortcutLaunchedActivity"
android:exported="false"/>
diff --git a/tests/tests/shortcutmanager/common/src/android.content.pm.cts.shortcutmanager.common/Constants.java b/tests/tests/shortcutmanager/common/src/android.content.pm.cts.shortcutmanager.common/Constants.java
index 7ca5336..9d3db4a 100644
--- a/tests/tests/shortcutmanager/common/src/android.content.pm.cts.shortcutmanager.common/Constants.java
+++ b/tests/tests/shortcutmanager/common/src/android.content.pm.cts.shortcutmanager.common/Constants.java
@@ -15,11 +15,13 @@
*/
package android.content.pm.cts.shortcutmanager.common;
+import java.security.SecureRandom;
+
public class Constants {
public static final String ACTION_THROTTLING_TEST =
"android.content.pm.cts.shortcutmanager.ACTION_THROTTLING_TEST";
- public static final String ACTION_THROTTLING_REPLY =
- "android.content.pm.cts.shortcutmanager.ACTION_THROTTLING_REPLY";
+ public static final String ACTION_REPLY =
+ "android.content.pm.cts.shortcutmanager.ACTION_REPLY";
public static final String EXTRA_METHOD = "method";
public static final String EXTRA_REPLY_ACTION = "reply_action";
@@ -37,4 +39,14 @@
public static final String INLINE_REPLY_TITLE = "InlineReplyTestTitle";
public static final String INLINE_REPLY_REMOTE_INPUT_CAPTION = "__INLINE_REPLY_REMOTE_INPUT__";
+
+ public static final String IGNORE = "IGNORE";
+ public static final String REQUEST_IGNORED_MESSAGE = "REQUEST_IGNORED_MESSAGE";
+
+ public static final String ALREADY_PINNED = "ALREADY_PINNED";
+
+ public static final String HAS_ICON = "HAS_ICON";
+ public static final String LABEL = "LABEL";
+
+ public static final SecureRandom sRandom = new SecureRandom();
}
diff --git a/tests/tests/shortcutmanager/common/src/android.content.pm.cts.shortcutmanager.common/ReplyUtil.java b/tests/tests/shortcutmanager/common/src/android.content.pm.cts.shortcutmanager.common/ReplyUtil.java
new file mode 100644
index 0000000..b1b0ae5
--- /dev/null
+++ b/tests/tests/shortcutmanager/common/src/android.content.pm.cts.shortcutmanager.common/ReplyUtil.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 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.content.pm.cts.shortcutmanager.common;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.retryUntil;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+public class ReplyUtil {
+ private ReplyUtil() {
+ }
+
+ private static final String MAIN_CTS_PACKAGE = "android.content.pm.cts.shortcutmanager";
+
+ public static void runTestAndReply(Context context, String replyAction, Runnable test) {
+ try {
+ test.run();
+
+ sendReply(context, replyAction, null);
+ } catch (Throwable e) {
+ String error = "Test failed: " + e.getMessage() + "\n" + Log.getStackTraceString(e);
+ sendReply(context, replyAction, error);
+ }
+ }
+
+ public static void sendReply(Context context, String replyAction,
+ String failureMessageOrNullForSuccess) {
+ // Create the reply bundle.
+ final Bundle ret = new Bundle();
+ if (failureMessageOrNullForSuccess == null) {
+ ret.putBoolean("success", true);
+ } else {
+ ret.putString("error", failureMessageOrNullForSuccess);
+ }
+
+ // Send reply
+ final Intent reply = new Intent(replyAction).setPackage(MAIN_CTS_PACKAGE);
+ reply.putExtras(ret);
+
+ context.sendBroadcast(reply);
+ }
+
+ public static void sendSuccessReply(Context context, String replyAction) {
+ sendReply(context, replyAction, null);
+ }
+
+ public static void invokeAndWaitForReply(Context context, Consumer<String> r) {
+ final AtomicReference<Intent> ret = new AtomicReference<>();
+
+ // Register the reply receiver
+
+ // Use a random reply action every time.
+ final String replyAction = Constants.ACTION_REPLY + Constants.sRandom.nextLong();
+ final IntentFilter filter = new IntentFilter(replyAction);
+
+ final BroadcastReceiver resultReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ ret.set(intent);
+ }
+ };
+
+ context.registerReceiver(resultReceiver, filter);
+
+ try {
+ // Run the code.
+ r.accept(replyAction);
+
+ // Wait for the response.
+ retryUntil(() -> ret.get() != null, "Didn't receive result broadcast", 120);
+
+ if (ret.get().getExtras().getBoolean("success")) {
+ return;
+ }
+ Assert.fail(ret.get().getExtras().getString("error"));
+ } finally {
+ context.unregisterReceiver(resultReceiver);
+ }
+ }
+}
diff --git a/tests/tests/shortcutmanager/packages/launchermanifest/Android.mk b/tests/tests/shortcutmanager/packages/launchermanifest/Android.mk
index acdb149..a11c978 100644
--- a/tests/tests/shortcutmanager/packages/launchermanifest/Android.mk
+++ b/tests/tests/shortcutmanager/packages/launchermanifest/Android.mk
@@ -23,7 +23,17 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
LOCAL_SDK_VERSION := current
@@ -43,7 +53,17 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
LOCAL_SDK_VERSION := current
@@ -63,7 +83,17 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
LOCAL_SDK_VERSION := current
diff --git a/tests/tests/shortcutmanager/packages/launchermanifest/AndroidManifest.xml b/tests/tests/shortcutmanager/packages/launchermanifest/AndroidManifest.xml
index 4290c7f..ac4925e 100755
--- a/tests/tests/shortcutmanager/packages/launchermanifest/AndroidManifest.xml
+++ b/tests/tests/shortcutmanager/packages/launchermanifest/AndroidManifest.xml
@@ -43,6 +43,11 @@
<category android:name="android.intent.category.HOME" />
</intent-filter>
</activity-alias>
+ <activity android:name="ShortcutConfirmPin">
+ <intent-filter>
+ <action android:name="android.content.pm.action.CONFIRM_PIN_ITEM" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/tests/shortcutmanager/packages/launchermanifest_nonshared/Android.mk b/tests/tests/shortcutmanager/packages/launchermanifest_nonshared/Android.mk
index 4aebb74..bc4aaa7 100644
--- a/tests/tests/shortcutmanager/packages/launchermanifest_nonshared/Android.mk
+++ b/tests/tests/shortcutmanager/packages/launchermanifest_nonshared/Android.mk
@@ -22,7 +22,17 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
LOCAL_SDK_VERSION := current
diff --git a/tests/tests/shortcutmanager/packages/packagemanifest/Android.mk b/tests/tests/shortcutmanager/packages/packagemanifest/Android.mk
index 458cb4e..1103194 100644
--- a/tests/tests/shortcutmanager/packages/packagemanifest/Android.mk
+++ b/tests/tests/shortcutmanager/packages/packagemanifest/Android.mk
@@ -22,7 +22,17 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
LOCAL_SDK_VERSION := current
@@ -42,7 +52,17 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
LOCAL_SDK_VERSION := current
@@ -62,7 +82,17 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
LOCAL_SDK_VERSION := current
diff --git a/tests/tests/shortcutmanager/packages/packagemanifest_nonshared/Android.mk b/tests/tests/shortcutmanager/packages/packagemanifest_nonshared/Android.mk
index 793fa66..0b1ff53 100644
--- a/tests/tests/shortcutmanager/packages/packagemanifest_nonshared/Android.mk
+++ b/tests/tests/shortcutmanager/packages/packagemanifest_nonshared/Android.mk
@@ -22,7 +22,17 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
LOCAL_SDK_VERSION := current
diff --git a/tests/tests/shortcutmanager/packages/src/android/content/pm/cts/shortcutmanager/packages/ShortcutConfirmPin.java b/tests/tests/shortcutmanager/packages/src/android/content/pm/cts/shortcutmanager/packages/ShortcutConfirmPin.java
new file mode 100644
index 0000000..9a2659b
--- /dev/null
+++ b/tests/tests/shortcutmanager/packages/src/android/content/pm/cts/shortcutmanager/packages/ShortcutConfirmPin.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 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.content.pm.cts.shortcutmanager.packages;
+
+import android.app.Activity;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.PinItemRequest;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.cts.shortcutmanager.common.Constants;
+import android.content.pm.cts.shortcutmanager.common.ReplyUtil;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * Activity that receives a "pin shortcut" request, and accepts automatically.
+ */
+public class ShortcutConfirmPin extends Activity {
+ private static final String TAG = "ShortcutConfirmPin";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Log.i(TAG, "ShortcutConfirmPin.onCreate");
+
+ String replyAction = null;
+ try {
+ final LauncherApps launcherApps = getSystemService(LauncherApps.class);
+ final PinItemRequest request = launcherApps.getPinItemRequest(getIntent());
+
+ // This really must be non-null; otherwise we can't send a reply.
+ final ShortcutInfo shortcut = request.getShortcutInfo();
+ if (shortcut == null) {
+ Log.e(TAG, "request.getShortcutInfo() NOT expected to be NULL");
+ return;
+ }
+
+ replyAction = shortcut.getExtras().getString(Constants.EXTRA_REPLY_ACTION);
+
+ if (!request.isValid()) {
+ ReplyUtil.sendReply(this, replyAction, "request.isValid() expected to be TRUE");
+ return;
+ }
+ if (request.getRequestType() != PinItemRequest.REQUEST_TYPE_SHORTCUT) {
+ ReplyUtil.sendReply(this, replyAction,
+ "request.getRequestType() expected to be REQUEST_TYPE_SHORTCUT");
+ return;
+ }
+
+ if (shortcut.getExtras().getBoolean(Constants.IGNORE)) {
+ // Send a reply so that the caller can tell if the request has been sent,
+ // and ignored.
+ ReplyUtil.sendReply(this, replyAction, Constants.REQUEST_IGNORED_MESSAGE);
+ return;
+ }
+
+ // Check the shortcut's fields.
+ final boolean expectPinned = shortcut.getExtras().getBoolean(Constants.ALREADY_PINNED);
+ if (shortcut.isPinned() != expectPinned) {
+ ReplyUtil.sendReply(this, replyAction, "isPinned() expected to be " + expectPinned);
+ return;
+ }
+
+ final String expectLabel = shortcut.getExtras().getString(Constants.LABEL);
+ if (!Objects.equals(expectLabel, shortcut.getShortLabel())) {
+ ReplyUtil.sendReply(this, replyAction,
+ "getShortLabel() expected to be '" + expectLabel + "', but was '"
+ + shortcut.getShortLabel() + "'");
+ return;
+ }
+ final Drawable icon = launcherApps.getShortcutBadgedIconDrawable(
+ shortcut, DisplayMetrics.DENSITY_DEFAULT);
+ if (shortcut.getExtras().getBoolean(Constants.HAS_ICON)) {
+ // Send a reply so that the caller can tell if the request has been sent,
+ // and ignored.
+ if (icon == null) {
+ ReplyUtil.sendReply(this, replyAction, "Expected to have icon");
+ return;
+ }
+ } else {
+ if (icon != null) {
+ ReplyUtil.sendReply(this, replyAction, "Not expected to have icon");
+ return;
+ }
+ }
+
+ request.accept();
+ if (request.isValid()) {
+ ReplyUtil.sendReply(this, replyAction,
+ "request.isValid() expected to be FALSE after accept()");
+ return;
+ }
+ ReplyUtil.sendSuccessReply(this, replyAction);
+ } catch (Exception e) {
+ Log.e(TAG, "Caught exception", e);
+ if (replyAction != null) {
+ ReplyUtil.sendReply(this, replyAction, "Caught exception: " + e);
+ }
+ } finally {
+ finish();
+ }
+ }
+}
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerConfigActivityTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerConfigActivityTest.java
new file mode 100644
index 0000000..0614d84
--- /dev/null
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerConfigActivityTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2017 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.content.pm.cts.shortcutmanager;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.*;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps.PinItemRequest;
+import android.content.pm.ShortcutInfo;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Tests for various APIs related to starting shortcut config activity.
+ */
+@SmallTest
+public class ShortcutManagerConfigActivityTest extends ShortcutManagerCtsTestsBase {
+
+ private static final String SHORTCUT_ID = "s12345";
+
+ private static final String CONFIG_ACTIVITY_NAME =
+ "android.content.pm.cts.shortcutmanager.main_shortcut_config";
+
+ public void testGetShortcutConfigActivityList() throws Exception {
+ setDefaultLauncher(getInstrumentation(), mLauncherContext1);
+
+ runWithCaller(mLauncherContext1, () -> {
+ assertNotNull(getConfigActivity());
+ assertNotNull(getLauncherApps().getShortcutConfigActivityIntent(getConfigActivity()));
+ });
+
+ // Get config activity works even for non-default activity.
+ setDefaultLauncher(getInstrumentation(), mLauncherContext4);
+
+ runWithCaller(mLauncherContext1, () -> {
+ assertNotNull(getConfigActivity());
+ // throws exception when default launcher is different.
+ assertExpectException(SecurityException.class, null, () ->
+ getLauncherApps().getShortcutConfigActivityIntent(getConfigActivity()));
+ });
+ }
+
+ public void testCorrectIntentSenderCreated() throws Throwable {
+ setDefaultLauncher(getInstrumentation(), mLauncherContext1);
+ final AtomicReference<IntentSender> sender = new AtomicReference<>();
+ runWithCaller(mLauncherContext1, () ->
+ sender.set(getLauncherApps().getShortcutConfigActivityIntent(getConfigActivity())));
+
+ Instrumentation.ActivityMonitor monitor =
+ getInstrumentation().addMonitor((String) null, null, false);
+
+ runTestOnUiThread(() -> {
+ try {
+ mLauncherContext1.startIntentSender(sender.get(), null, 0, 0, 0);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ // Verify that the config activity was launched with proper action.
+ Activity activity = monitor.waitForActivityWithTimeout(10000);
+ assertNotNull(activity);
+ Intent intent = activity.getIntent();
+ assertEquals(Intent.ACTION_CREATE_SHORTCUT, intent.getAction());
+ assertEquals(CONFIG_ACTIVITY_NAME, intent.getComponent().getClassName());
+ activity.finish();
+ }
+
+ public void testCreateShortcutResultIntent_defaultLauncher() throws Exception {
+ setDefaultLauncher(getInstrumentation(), mLauncherContext1);
+ PinItemRequest request = getShortcutRequestForPackage1();
+ runWithCaller(mLauncherContext1, () -> {
+ assertTrue(request.isValid());
+ assertTrue(request.accept());
+ });
+ }
+
+ public void testCreateShortcutResultIntent_defaultChanges() throws Exception {
+ setDefaultLauncher(getInstrumentation(), mLauncherContext1);
+ PinItemRequest request = getShortcutRequestForPackage1();
+
+ setDefaultLauncher(getInstrumentation(), mLauncherContext4);
+ // Launcher1 can still access the request
+ runWithCaller(mLauncherContext1, () -> {
+ assertTrue(request.isValid());
+ assertTrue(request.accept());
+ });
+ }
+
+ public void testCreateShortcutResultIntent_noDefault() throws Exception {
+ setDefaultLauncher(getInstrumentation(), mLauncherContext4);
+ PinItemRequest request = getShortcutRequestForPackage1();
+
+ // Launcher1 can still access the request
+ runWithCaller(mLauncherContext1, () -> {
+ assertFalse(request.isValid());
+ assertExpectException(SecurityException.class, null, request::accept);
+ });
+ }
+
+ private PinItemRequest getShortcutRequestForPackage1() {
+ final AtomicReference<PinItemRequest> result = new AtomicReference<>();
+ runWithCaller(mPackageContext1, () -> {
+ final ShortcutInfo shortcut = makeShortcutBuilder(SHORTCUT_ID)
+ .setShortLabel("label1")
+ .setIntent(new Intent(Intent.ACTION_MAIN))
+ .build();
+ Intent intent = getManager().createShortcutResultIntent(shortcut);
+ assertNotNull(intent);
+ PinItemRequest request = getLauncherApps().getPinItemRequest(intent);
+ assertNotNull(request);
+ assertEquals(PinItemRequest.REQUEST_TYPE_SHORTCUT, request.getRequestType());
+ assertEquals(SHORTCUT_ID, request.getShortcutInfo().getId());
+ result.set(request);
+ });
+ return result.get();
+ }
+
+ private LauncherActivityInfo getConfigActivity() {
+ for (LauncherActivityInfo info : getLauncherApps().getShortcutConfigActivityList(
+ getTestContext().getPackageName(), getUserHandle())) {
+ if (CONFIG_ACTIVITY_NAME.equals(info.getComponentName().getClassName())) {
+ return info;
+ }
+ }
+ return null;
+ }
+}
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java
index 0242b41..610fada 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java
@@ -63,8 +63,6 @@
}
}
- protected static final SecureRandom sRandom = new SecureRandom();
-
private Context mCurrentCallerPackage;
private int mUserId;
private UserHandle mUserHandle;
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerRequestPinTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerRequestPinTest.java
new file mode 100644
index 0000000..34ce2d5
--- /dev/null
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerRequestPinTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 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.content.pm.cts.shortcutmanager;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.retryUntil;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.setDefaultLauncher;
+
+import android.content.Intent;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.content.pm.cts.shortcutmanager.common.Constants;
+import android.content.pm.cts.shortcutmanager.common.ReplyUtil;
+import android.os.PersistableBundle;
+import android.util.Log;
+
+import java.util.List;
+
+public class ShortcutManagerRequestPinTest extends ShortcutManagerCtsTestsBase {
+ private static final String TAG = "ShortcutMRPT";
+
+ public void testIsRequestPinShortcutSupported() {
+
+ // Launcher 1 supports it.
+ setDefaultLauncher(getInstrumentation(), mLauncherContext1);
+
+ runWithCaller(mPackageContext1, () -> {
+ assertTrue(getManager().isRequestPinShortcutSupported());
+ });
+
+ // Launcher 4 does *not* supports it.
+ setDefaultLauncher(getInstrumentation(), mLauncherContext4);
+
+ runWithCaller(mPackageContext1, () -> {
+ assertFalse(getManager().isRequestPinShortcutSupported());
+ });
+ }
+
+ /**
+ * A test for {@link ShortcutManager#requestPinShortcut}, a very simple case.
+ */
+ public void testRequestPinShortcut() {
+ setDefaultLauncher(getInstrumentation(), mLauncherContext1);
+
+ final String SHORTCUT_ID = "s12345";
+
+ runWithCaller(mPackageContext1, () -> {
+ assertTrue(getManager().isRequestPinShortcutSupported());
+
+ ReplyUtil.invokeAndWaitForReply(getTestContext(), (replyAction) -> {
+ final PersistableBundle extras = new PersistableBundle();
+ extras.putString(Constants.EXTRA_REPLY_ACTION, replyAction);
+ extras.putString(Constants.LABEL, "label1");
+
+ final ShortcutInfo shortcut = makeShortcutBuilder(SHORTCUT_ID)
+ .setShortLabel("label1")
+ .setIntent(new Intent(Intent.ACTION_MAIN))
+ .setExtras(extras)
+ .build();
+
+ Log.i(TAG, "Calling requestPinShortcut...");
+ assertTrue(getManager().requestPinShortcut(shortcut, /* intent sender */ null));
+ Log.i(TAG, "Done.");
+ });
+ });
+ runWithCaller(mLauncherContext1, () -> {
+ final ShortcutQuery query = new ShortcutQuery()
+ .setPackage(mPackageContext1.getPackageName())
+ .setShortcutIds(list(SHORTCUT_ID))
+ .setQueryFlags(ShortcutQuery.FLAG_MATCH_DYNAMIC
+ | ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_MANIFEST);
+ Log.i(TAG, "Waiting for shortcut to be visible to launcher...");
+ retryUntil(() -> {
+ final List<ShortcutInfo> shortcuts = getLauncherApps().getShortcuts(query,
+ android.os.Process.myUserHandle());
+ if (shortcuts == null) {
+ // Launcher not responded yet.
+ return false;
+ }
+ assertWith(shortcuts)
+ .haveIds(SHORTCUT_ID)
+ .areAllPinned()
+ .areAllNotDynamic()
+ .areAllNotManifest();
+ return true;
+ }, "Shortcut still not pinned");
+ });
+ }
+
+ // TODO Various other cases (already pinned, etc)
+ // TODO Various error cases (missing mandatory fields, etc)
+}
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerThrottlingTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerThrottlingTest.java
index 3eb42cf..55afe48 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerThrottlingTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerThrottlingTest.java
@@ -19,15 +19,12 @@
import static android.content.pm.cts.shortcutmanager.common.Constants.INLINE_REPLY_REMOTE_INPUT_CAPTION;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resetThrottling;
-import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.retryUntil;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.runCommandForNoOutput;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.cts.shortcutmanager.common.Constants;
+import android.content.pm.cts.shortcutmanager.common.ReplyUtil;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
@@ -36,8 +33,6 @@
import android.test.suitebuilder.annotation.Suppress;
import android.view.KeyEvent;
-import java.util.concurrent.atomic.AtomicReference;
-
/**
* The actual test is implemented in the CtsShortcutManagerThrottlingTest module.
* This class uses broadcast receivers to communicate with it, because if we just used an
@@ -52,45 +47,14 @@
"android.content.pm.cts.shortcutmanager.throttling";
private void callTest(String method) {
-
- final AtomicReference<Intent> ret = new AtomicReference<>();
-
- // Register the reply receiver
-
- // Use a random reply action every time.
- final String replyAction = Constants.ACTION_THROTTLING_REPLY + sRandom.nextLong();
- final IntentFilter filter = new IntentFilter(replyAction);
-
- final BroadcastReceiver r = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- ret.set(intent);
- }
- };
-
- getTestContext().registerReceiver(r, filter);
-
- try {
- // Send the request broadcast.
-
+ ReplyUtil.invokeAndWaitForReply(getTestContext(), (replyAction) -> {
final Intent i = new Intent(Constants.ACTION_THROTTLING_TEST);
i.putExtra(Constants.EXTRA_METHOD, method);
i.putExtra(Constants.EXTRA_REPLY_ACTION, replyAction);
i.setComponent(ComponentName.unflattenFromString(
TARGET_PACKAGE + "/.ShortcutManagerThrottlingTestReceiver"));
getTestContext().sendBroadcast(i);
-
- // Wait for the response.
- retryUntil(() -> ret.get() != null, "Didn't receive result broadcast",
- 120); // Wait much longer
-
- if (ret.get().getExtras().getBoolean("success")) {
- return;
- }
- fail(ret.get().getExtras().getString("error"));
- } finally {
- getTestContext().unregisterReceiver(r);
- }
+ });
}
@Override
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerUsageTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerUsageTest.java
index aa0ad82..56e842f 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerUsageTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerUsageTest.java
@@ -23,6 +23,7 @@
import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStatsManager;
+import android.content.pm.cts.shortcutmanager.common.Constants;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.format.Time;
@@ -54,7 +55,7 @@
Time tobj = new Time();
tobj.set(System.currentTimeMillis());
return tobj.format("%Y-%m-%d %H:%M:%S") + "." + signature + "."
- + sRandom.nextLong();
+ + Constants.sRandom.nextLong();
}
private boolean hasEvent(UsageEvents events, String packageName, String id) {
diff --git a/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/BgService.java b/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/BgService.java
index fbf8079..a6468ef 100644
--- a/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/BgService.java
+++ b/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/BgService.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.content.pm.ShortcutManager;
import android.content.pm.cts.shortcutmanager.common.Constants;
+import android.content.pm.cts.shortcutmanager.common.ReplyUtil;
import android.os.IBinder;
import android.util.Log;
diff --git a/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/FgService.java b/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/FgService.java
index 86f9dc7..a040715 100644
--- a/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/FgService.java
+++ b/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/FgService.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.cts.shortcutmanager.common.Constants;
+import android.content.pm.cts.shortcutmanager.common.ReplyUtil;
import android.os.IBinder;
import android.util.Log;
diff --git a/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/MyActivity.java b/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/MyActivity.java
index d04a890..ecd9964 100644
--- a/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/MyActivity.java
+++ b/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/MyActivity.java
@@ -17,6 +17,7 @@
import android.app.Activity;
import android.content.pm.cts.shortcutmanager.common.Constants;
+import android.content.pm.cts.shortcutmanager.common.ReplyUtil;
import android.os.Bundle;
import android.util.Log;
diff --git a/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/ReplyUtil.java b/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/ReplyUtil.java
deleted file mode 100644
index 1b0471c..0000000
--- a/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/ReplyUtil.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2016 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.content.pm.cts.shortcutmanager.throttling;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-
-public class ReplyUtil {
- private ReplyUtil() {
- }
-
- public static void runTestAndReply(Context context, String replyAction, Runnable test) {
- try {
- test.run();
-
- sendReply(context, replyAction, null);
- } catch (Throwable e) {
- String error = "Test failed: " + e.getMessage() + "\n" + Log.getStackTraceString(e);
- sendReply(context, replyAction, error);
- }
- }
-
- public static void sendReply(Context context, String replyAction,
- String failureMessageOrNullForSuccess) {
- // Create the reply bundle.
- final Bundle ret = new Bundle();
- if (failureMessageOrNullForSuccess == null) {
- ret.putBoolean("success", true);
- } else {
- ret.putString("error", failureMessageOrNullForSuccess);
- }
-
- // Send reply
- final Intent reply = new Intent(replyAction);
- reply.putExtras(ret);
-
- context.sendBroadcast(reply);
- }
-}
diff --git a/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/ShortcutManagerThrottlingTestReceiver.java b/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/ShortcutManagerThrottlingTestReceiver.java
index 6edf7a8..6e067e5 100644
--- a/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/ShortcutManagerThrottlingTestReceiver.java
+++ b/tests/tests/shortcutmanager/throttling/src/android/content/pm/cts/shortcutmanager/throttling/ShortcutManagerThrottlingTestReceiver.java
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.content.pm.ShortcutManager;
import android.content.pm.cts.shortcutmanager.common.Constants;
+import android.content.pm.cts.shortcutmanager.common.ReplyUtil;
import android.util.Log;
diff --git a/tests/tests/speech/Android.mk b/tests/tests/speech/Android.mk
index 68e0cba..1134398 100755
--- a/tests/tests/speech/Android.mk
+++ b/tests/tests/speech/Android.mk
@@ -21,7 +21,10 @@
# and when built explicitly put it in the data partition
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctstestrunner \
+ android-support-test \
+ legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/systemui/Android.mk b/tests/tests/systemui/Android.mk
index c31d825..6aed2f8 100644
--- a/tests/tests/systemui/Android.mk
+++ b/tests/tests/systemui/Android.mk
@@ -26,7 +26,10 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctstestrunner \
+ android-support-test \
+ legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/telephony/Android.mk b/tests/tests/telephony/Android.mk
index 75da56e..e502384 100644
--- a/tests/tests/telephony/Android.mk
+++ b/tests/tests/telephony/Android.mk
@@ -24,7 +24,10 @@
LOCAL_JAVA_LIBRARIES := telephony-common
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctstestrunner \
+ compatibility-device-util \
+ legacy-android-test
LOCAL_HOST_SHARED_LIBRARIES := compatibility-device-telephony-preconditions
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index 83c3833..7d581c5 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -37,6 +37,7 @@
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
import android.telephony.CellLocation;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
@@ -46,13 +47,12 @@
import com.android.compatibility.common.util.TestThread;
import com.android.internal.telephony.PhoneConstants;
+import java.util.regex.Pattern;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.regex.Pattern;
-
@RunWith(AndroidJUnit4.class)
public class TelephonyManagerTest {
private TelephonyManager mTelephonyManager;
@@ -183,8 +183,11 @@
mTelephonyManager.getDeviceId();
mTelephonyManager.getDeviceId(mTelephonyManager.getDefaultSim());
mTelephonyManager.getDeviceSoftwareVersion();
+ mTelephonyManager.getImei();
+ mTelephonyManager.getImei(mTelephonyManager.getDefaultSim());
mTelephonyManager.getPhoneCount();
mTelephonyManager.getDataEnabled();
+ mTelephonyManager.getNetworkSpecifier();
TelecomManager telecomManager = (TelecomManager) getContext()
.getSystemService(Context.TELECOM_SERVICE);
@@ -474,6 +477,70 @@
assertEquals(mServiceState, mTelephonyManager.getServiceState());
}
+ @Test
+ public void testGetCarrierConfig() {
+ if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+ Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
+ return;
+ }
+ CarrierConfigManager carrierConfigManager =
+ getContext().getSystemService(CarrierConfigManager.class);
+ assertEquals(mTelephonyManager.getCarrierConfig(), carrierConfigManager.getConfig());
+ }
+
+ /**
+ * Tests that the device properly reports either a valid IMEI if GSM or null.
+ */
+ @Test
+ public void testGetImei() {
+ String imei = mTelephonyManager.getImei();
+ if (mTelephonyManager.getSimState() == TelephonyManager.SIM_STATE_ABSENT && imei == null) {
+ // If no SIM card is present, IMEI can be null.
+ return;
+ }
+ verifyImei(imei, mTelephonyManager.getDeviceId());
+ }
+
+ /**
+ * Tests that the device properly reports either a valid IMEI if GSM or null.
+ */
+ @Test
+ public void testGetImeiForSlotId() {
+ // Test for slot id = 0.
+ String imei = mTelephonyManager.getImei(0);
+ verifyImei(imei, mTelephonyManager.getDeviceId(0));
+ // Also verify that no exception is thrown for any slot id (including invalid ones)
+ for (int i = -1; i <= mTelephonyManager.getPhoneCount(); i++) {
+ mTelephonyManager.getImei(i);
+ }
+ }
+
+ private void verifyImei(String imei, String deviceId) {
+ int phoneType = mTelephonyManager.getPhoneType();
+ switch (phoneType) {
+ case TelephonyManager.PHONE_TYPE_GSM:
+ assertGsmDeviceId(imei);
+ assertEquals(imei, deviceId);
+ break;
+ case TelephonyManager.PHONE_TYPE_CDMA:
+ // LTE device is using IMEI as device id
+ if (mTelephonyManager.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE) {
+ assertGsmDeviceId(imei);
+ assertEquals(imei, deviceId);
+ break;
+ }
+ // Fall through
+ case TelephonyManager.PHONE_TYPE_NONE:
+ if (imei != null) {
+ assertGsmDeviceId(imei);
+ }
+ // An IMEI is not required for PHONE_TYPE_NONE or non-LTE PHONE_TYPE_CDMA
+ break;
+ default:
+ throw new IllegalArgumentException("Did you add a new phone type? " + phoneType);
+ }
+ }
+
private static Context getContext() {
return InstrumentationRegistry.getContext();
}
diff --git a/tests/tests/telephony2/Android.mk b/tests/tests/telephony2/Android.mk
index 08a7df3..3ba6018 100644
--- a/tests/tests/telephony2/Android.mk
+++ b/tests/tests/telephony2/Android.mk
@@ -22,7 +22,10 @@
# and when built explicitly put it in the data partition
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctstestrunner \
+ compatibility-device-util \
+ legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java
index 5055972..24f9932 100644
--- a/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java
@@ -173,8 +173,7 @@
debugLayout(source, staticLayout);
- int lineCount = staticLayout.getLineCount();
- assertTrue("Number of lines", lineCount <= maxLines);
+ final int lineCount = staticLayout.getLineCount();
for (int line = 0; line < lineCount; line++) {
int lineStart = staticLayout.getLineStart(line);
diff --git a/tests/tests/uirendering/Android.mk b/tests/tests/uirendering/Android.mk
index e1f6eef..9d00124 100644
--- a/tests/tests/uirendering/Android.mk
+++ b/tests/tests/uirendering/Android.mk
@@ -30,7 +30,8 @@
compatibility-device-util \
ctstestrunner \
mockito-target-minus-junit4 \
- android-support-test
+ android-support-test \
+ legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapTests.java
new file mode 100644
index 0000000..d9b8d7f
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapTests.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2016 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.uirendering.cts.testclasses;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.uirendering.cts.R;
+import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
+import android.uirendering.cts.bitmapverifiers.ColorCountVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.ViewInitializer;
+import android.view.FrameMetrics;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.Window;
+import android.widget.FrameLayout;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class BitmapTests extends ActivityTestBase {
+ class BitmapView extends View {
+ private Bitmap mBitmap;
+ private int mColor;
+
+ public BitmapView(Context context) {
+ super(context);
+ mBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ setColor(Color.BLUE);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.drawBitmap(mBitmap, new Rect(0, 0, 1, 1), canvas.getClipBounds(), null);
+ }
+
+ public void setColor(int color) {
+ mColor = color;
+ mBitmap.setPixel(0, 0, color);
+ }
+
+ public int getColor() {
+ return mColor;
+ }
+ }
+
+ /*
+ * The following test verifies that bitmap changes during render thread animation won't
+ * be visible: we changed a bitmap from blue to red during circular reveal (an RT animation),
+ * and changed it back to blue before the end of the animation; we should never see any
+ * red pixel.
+ */
+ @Test
+ public void testChangeDuringRtAnimation() {
+ class RtOnlyFrameCounter implements Window.OnFrameMetricsAvailableListener {
+ private int count = 0;
+
+ @Override
+ public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics,
+ int dropCountSinceLastInvocation) {
+ if (frameMetrics.getMetric(FrameMetrics.ANIMATION_DURATION) == 0
+ && frameMetrics.getMetric(FrameMetrics.INPUT_HANDLING_DURATION) == 0
+ && frameMetrics.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION) == 0) {
+ count++;
+ };
+ }
+
+ public boolean isLargeEnough() {
+ return count >= 5;
+ }
+ }
+
+ ViewInitializer initializer = new ViewInitializer() {
+ Animator mAnimator;
+ RtOnlyFrameCounter mCounter = new RtOnlyFrameCounter();
+
+ @Override
+ public void initializeView(View view) {
+ FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
+
+ final BitmapView child = new BitmapView(view.getContext());
+ child.setLayoutParams(new FrameLayout.LayoutParams(50, 50));
+ root.addView(child);
+
+ mAnimator = ViewAnimationUtils.createCircularReveal(child, 0, 0, 0, 90);
+ mAnimator.setDuration(3000);
+ mAnimator.start();
+
+ Handler handler = new Handler();
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ child.setColor(Color.RED);
+ try {
+ Thread.sleep(1000);
+ } catch (Exception e) {
+ // do nothing
+ }
+ child.setColor(Color.BLUE);
+ }
+ }, 1000);
+ getActivity().getWindow().addOnFrameMetricsAvailableListener(mCounter, handler);
+ }
+
+ @Override
+ public void teardownView() {
+ mAnimator.cancel();
+ Assert.assertTrue(mCounter.isLargeEnough());
+ }
+ };
+
+ createTest()
+ .addLayout(R.layout.frame_layout, initializer, true)
+ .runWithAnimationVerifier(new ColorCountVerifier(Color.RED, 0));
+ }
+
+ /*
+ * The following test verifies that bitmap changes during UI thread animation are
+ * visible: we keep changing a bitmap's color between red and blue in sync with the
+ * background, and we should only see pure blue or red.
+ */
+ @Test
+ public void testChangeDuringUiAnimation() {
+ class PureBlueOrRedVerifier extends BitmapVerifier {
+ @Override
+ public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
+ int blueCount = 0;
+ int redCount = 0;
+ for (int x = 0; x < width; x++) {
+ for (int y = 0; y < height; y++) {
+ if (bitmap[indexFromXAndY(x, y, stride, offset)] == Color.BLUE) {
+ blueCount++;
+ } else if (bitmap[indexFromXAndY(x, y, stride, offset)] == Color.RED) {
+ redCount++;
+ }
+ }
+ }
+ return blueCount == width * height || redCount == width * height;
+ }
+ }
+
+ ViewInitializer initializer = new ViewInitializer() {
+ ValueAnimator mAnimator;
+
+ @Override
+ public void initializeView(View view) {
+ FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
+ root.setBackgroundColor(Color.BLUE);
+
+ final BitmapView child = new BitmapView(view.getContext());
+
+ // The child size is strictly less than the test canvas size,
+ // and we are moving it up and down inside the canvas.
+ child.setLayoutParams(new FrameLayout.LayoutParams(ActivityTestBase.TEST_WIDTH / 2,
+ ActivityTestBase.TEST_HEIGHT / 2));
+ root.addView(child);
+ child.setColor(Color.BLUE);
+
+ mAnimator = ValueAnimator.ofInt(0, ActivityTestBase.TEST_HEIGHT / 2);
+ mAnimator.setRepeatMode(mAnimator.REVERSE);
+ mAnimator.setRepeatCount(mAnimator.INFINITE);
+ mAnimator.setDuration(400);
+ mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int v = (Integer) mAnimator.getAnimatedValue();
+ child.setTranslationY(v);
+ if (child.getColor() == Color.BLUE) {
+ root.setBackgroundColor(Color.RED);
+ child.setColor(Color.RED);
+ } else {
+ root.setBackgroundColor(Color.BLUE);
+ child.setColor(Color.BLUE);
+ }
+ }
+ });
+ mAnimator.start();
+ }
+
+ @Override
+ public void teardownView() {
+ mAnimator.cancel();
+ }
+ };
+
+ createTest()
+ .addLayout(R.layout.frame_layout, initializer, true)
+ .runWithAnimationVerifier(new PureBlueOrRedVerifier());
+ }
+}
diff --git a/tests/tests/util/Android.mk b/tests/tests/util/Android.mk
index 866e566..32d4584 100644
--- a/tests/tests/util/Android.mk
+++ b/tests/tests/util/Android.mk
@@ -24,7 +24,11 @@
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-annotations android-support-test ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-annotations \
+ android-support-test \
+ ctstestrunner \
+ legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/view/Android.mk b/tests/tests/view/Android.mk
index 9b505dd..f582b41 100644
--- a/tests/tests/view/Android.mk
+++ b/tests/tests/view/Android.mk
@@ -34,7 +34,8 @@
ctstestrunner \
mockito-target-minus-junit4 \
platform-test-annotations \
- ub-uiautomator
+ ub-uiautomator \
+ legacy-android-test
LOCAL_JNI_SHARED_LIBRARIES := libctsview_jni libnativehelper_compat_libc++
diff --git a/tests/tests/view/res/layout/tooltip_layout.xml b/tests/tests/view/res/layout/tooltip_layout.xml
index 8291b31..eac341d 100644
--- a/tests/tests/view/res/layout/tooltip_layout.xml
+++ b/tests/tests/view/res/layout/tooltip_layout.xml
@@ -39,7 +39,7 @@
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="View with tooltip"
- android:tooltip="tooltip text"/>
+ android:tooltipText="tooltip text"/>
</LinearLayout>
@@ -57,4 +57,28 @@
android:layout_margin="10dp"
android:background="#ddd"/>
+ <RelativeLayout
+ android:id="@+id/overlap_group"
+ android:layout_width="100dp"
+ android:layout_height="30dp"
+ android:layout_margin="10dp"
+ android:tooltipText="overlap tooltip group"
+ android:background="#ddd">
+ <View
+ android:id="@+id/overlap1"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:tooltipText="overlap tooltip 1" />
+ <View
+ android:id="@+id/overlap2"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:tooltipText="overlap tooltip 2" />
+ <View
+ android:id="@+id/overlap3"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:tooltipText="overlap tooltip 3" />
+ </RelativeLayout>
+
</LinearLayout>
diff --git a/tests/tests/view/res/layout/view_layout.xml b/tests/tests/view/res/layout/view_layout.xml
index 64ebe39..920da83 100644
--- a/tests/tests/view/res/layout/view_layout.xml
+++ b/tests/tests/view/res/layout/view_layout.xml
@@ -123,6 +123,26 @@
</LinearLayout>
</LinearLayout>
+ <RelativeLayout
+ android:id="@+id/pointer_icon_overlap"
+ android:layout_width="100px"
+ android:layout_height="100px"
+ android:pointerIcon="crosshair">
+ <View
+ android:id="@+id/pointer_icon_overlap_child1"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+ <View
+ android:id="@+id/pointer_icon_overlap_child2"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ <View
+ android:id="@+id/pointer_icon_overlap_child3"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:pointerIcon="help" />
+ </RelativeLayout>
+
<FrameLayout
android:id="@+id/aggregate_visibility_parent"
android:layout_width="wrap_content"
diff --git a/tests/tests/view/res/menu/content_description.xml b/tests/tests/view/res/menu/content_description.xml
new file mode 100644
index 0000000..45b594a
--- /dev/null
+++ b/tests/tests/view/res/menu/content_description.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:id="@+id/item1"
+ android:title="item1"
+ android:contentDescription="description1" />
+
+ <item android:id="@+id/item2"
+ android:title="item2"
+ android:contentDescription="description2" />
+
+ <item android:id="@+id/item3"
+ android:title="item3" />
+
+</menu>
diff --git a/tests/tests/view/res/menu/tooltip.xml b/tests/tests/view/res/menu/tooltip.xml
new file mode 100644
index 0000000..b15a9b4
--- /dev/null
+++ b/tests/tests/view/res/menu/tooltip.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:id="@+id/item1"
+ android:title="item1"
+ android:tooltipText="tooltip1" />
+
+ <item android:id="@+id/item2"
+ android:title="item2"
+ android:tooltipText="tooltip2" />
+
+ <item android:id="@+id/item3"
+ android:title="item3" />
+
+</menu>
diff --git a/tests/tests/view/src/android/view/cts/MenuInflaterTest.java b/tests/tests/view/src/android/view/cts/MenuInflaterTest.java
index 9da739e..bc9933f 100644
--- a/tests/tests/view/src/android/view/cts/MenuInflaterTest.java
+++ b/tests/tests/view/src/android/view/cts/MenuInflaterTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.app.Activity;
@@ -32,6 +33,7 @@
import android.support.test.runner.AndroidJUnit4;
import android.view.Menu;
import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.SubMenu;
import android.widget.PopupMenu;
@@ -251,6 +253,50 @@
assertTrue(mMenu.findItem(R.id.nongroup_checkable_item_3).isChecked());
}
+ @UiThreadTest
+ @Test
+ public void testInflateTooltipFromXml() {
+ mMenuInflater.inflate(R.menu.tooltip, mMenu);
+
+ MenuItem item1 = mMenu.findItem(R.id.item1);
+ MenuItem item2 = mMenu.findItem(R.id.item2);
+ MenuItem item3 = mMenu.findItem(R.id.item3);
+
+ assertEquals("tooltip1", item1.getTooltipText());
+
+ assertEquals("tooltip2", item2.getTooltipText());
+ item2.setTooltipText(null);
+ assertNull(item2.getTooltipText());
+ item2.setTooltipText("tooltip2_new");
+ assertEquals("tooltip2_new", item2.getTooltipText());
+
+ assertNull(item3.getTooltipText());
+ item3.setTooltipText("tooltip3");
+ assertEquals("tooltip3", item3.getTooltipText());
+ }
+
+ @UiThreadTest
+ @Test
+ public void testInflateContentDescriptionFromXml() {
+ mMenuInflater.inflate(R.menu.content_description, mMenu);
+
+ MenuItem item1 = mMenu.findItem(R.id.item1);
+ MenuItem item2 = mMenu.findItem(R.id.item2);
+ MenuItem item3 = mMenu.findItem(R.id.item3);
+
+ assertEquals("description1", item1.getContentDescription());
+
+ assertEquals("description2", item2.getContentDescription());
+ item2.setContentDescription(null);
+ assertNull(item2.getContentDescription());
+ item2.setContentDescription("description2_new");
+ assertEquals("description2_new", item2.getContentDescription());
+
+ assertNull(item3.getContentDescription());
+ item3.setContentDescription("description3");
+ assertEquals("description3", item3.getContentDescription());
+ }
+
private void verifyDrawableContent(BitmapDrawable b, int resId) {
Bitmap expected = BitmapFactory.decodeResource(mActivity.getResources(), resId);
WidgetTestUtils.assertEquals(expected, b.getBitmap());
diff --git a/tests/tests/view/src/android/view/cts/TooltipTest.java b/tests/tests/view/src/android/view/cts/TooltipTest.java
index 1903431..2c0c581 100644
--- a/tests/tests/view/src/android/view/cts/TooltipTest.java
+++ b/tests/tests/view/src/android/view/cts/TooltipTest.java
@@ -93,8 +93,8 @@
}
}
- private void setTooltip(View view, CharSequence tooltip) throws Throwable {
- mActivityRule.runOnUiThread(() -> view.setTooltip(tooltip));
+ private void setTooltipText(View view, CharSequence tooltipText) throws Throwable {
+ mActivityRule.runOnUiThread(() -> view.setTooltipText(tooltipText));
}
private boolean hasTooltip(View view) {
@@ -113,6 +113,10 @@
mInstrumentation.waitForIdleSync();
}
+ private void setVisibility(View view, int visibility) throws Throwable {
+ mActivityRule.runOnUiThread(() -> view.setVisibility(visibility));
+ }
+
private void callPerformLongClick(View view) throws Throwable {
mActivityRule.runOnUiThread(() -> view.performLongClick(0, 0));
}
@@ -191,28 +195,28 @@
@Test
public void testGetSetTooltip() throws Throwable {
// No tooltip set in resource
- assertEquals(null, mNoTooltipView.getTooltip());
+ assertEquals(null, mNoTooltipView.getTooltipText());
// Set the tooltip, read it back
- final String tooltipString1 = "new tooltip";
- setTooltip(mNoTooltipView, tooltipString1);
- assertEquals(tooltipString1, mNoTooltipView.getTooltip());
+ final String tooltipText1 = "new tooltip";
+ setTooltipText(mNoTooltipView, tooltipText1);
+ assertEquals(tooltipText1, mNoTooltipView.getTooltipText());
// Clear the tooltip.
- setTooltip(mNoTooltipView, null);
- assertEquals(null, mNoTooltipView.getTooltip());
+ setTooltipText(mNoTooltipView, null);
+ assertEquals(null, mNoTooltipView.getTooltipText());
// Check the tooltip set in resource
- assertEquals("tooltip text", mTooltipView.getTooltip());
+ assertEquals("tooltip text", mTooltipView.getTooltipText());
// Clear the tooltip set in resource
- setTooltip(mTooltipView, null);
- assertEquals(null, mTooltipView.getTooltip());
+ setTooltipText(mTooltipView, null);
+ assertEquals(null, mTooltipView.getTooltipText());
// Set the tooltip again, read it back
- final String tooltipString2 = "new tooltip 2";
- setTooltip(mTooltipView, tooltipString2);
- assertEquals(tooltipString2, mTooltipView.getTooltip());
+ final String tooltipText2 = "new tooltip 2";
+ setTooltipText(mTooltipView, tooltipText2);
+ assertEquals(tooltipText2, mTooltipView.getTooltipText());
}
@Test
@@ -249,10 +253,10 @@
callPerformLongClick(mTooltipView);
assertTrue(hasTooltip(mTooltipView));
- setTooltip(mTooltipView, "updated tooltip");
+ setTooltipText(mTooltipView, "updated tooltip");
assertTrue(hasTooltip(mTooltipView));
- setTooltip(mTooltipView, null);
+ setTooltipText(mTooltipView, null);
assertFalse(hasTooltip(mTooltipView));
}
@@ -627,7 +631,7 @@
@Test
public void testMouseHoverTooltipUnsetWhileHovering() throws Throwable {
injectHoverMove(mTooltipView);
- setTooltip(mTooltipView, null);
+ setTooltipText(mTooltipView, null);
waitOut(ViewConfiguration.getHoverTooltipShowTimeout());
assertFalse(hasTooltip(mTooltipView));
}
@@ -646,7 +650,7 @@
mNoTooltipView.setOnHoverListener((v, event) -> true);
mTooltipView.setOnHoverListener((v, event) -> true);
- setTooltip(mTopmostView, "tooltip");
+ setTooltipText(mTopmostView, "tooltip");
// Hover over a child with a tooltip works normally.
injectLongHoverMove(mTooltipView);
@@ -680,7 +684,7 @@
assertFalse(hasTooltip(mTopmostView));
// Set a tooltip on the intermediate parent, now it is showing tooltips.
- setTooltip(mGroupView, "tooltip");
+ setTooltipText(mGroupView, "tooltip");
injectLongHoverMove(mNoTooltipView);
assertFalse(hasTooltip(mNoTooltipView));
assertFalse(hasTooltip(mTopmostView));
@@ -743,4 +747,27 @@
addView(mTopmostView, mGroupView);
assertFalse(hasTooltip(mTooltipView));
}
+
+ @Test
+ public void testMouseHoverOverlap() throws Throwable {
+ final View parent = mActivity.findViewById(R.id.overlap_group);
+ final View child1 = mActivity.findViewById(R.id.overlap1);
+ final View child2 = mActivity.findViewById(R.id.overlap2);
+ final View child3 = mActivity.findViewById(R.id.overlap3);
+
+ injectLongHoverMove(parent);
+ assertTrue(hasTooltip(child3));
+
+ setVisibility(child3, View.GONE);
+ injectLongHoverMove(parent);
+ assertTrue(hasTooltip(child2));
+
+ setTooltipText(child2, null);
+ injectLongHoverMove(parent);
+ assertTrue(hasTooltip(child1));
+
+ setVisibility(child1, View.INVISIBLE);
+ injectLongHoverMove(parent);
+ assertTrue(hasTooltip(parent));
+ }
}
diff --git a/tests/tests/view/src/android/view/cts/ViewGroupTest.java b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
index 57fb3b6..868a398 100644
--- a/tests/tests/view/src/android/view/cts/ViewGroupTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
@@ -117,6 +117,7 @@
public void testAddFocusables() {
mMockViewGroup.setFocusable(true);
+ // Child is focusable.
ArrayList<View> list = new ArrayList<>();
list.add(mTextView);
mMockViewGroup.addView(mTextView);
@@ -124,12 +125,33 @@
assertEquals(2, list.size());
+ // Parent blocks descendants.
list = new ArrayList<>();
list.add(mTextView);
mMockViewGroup.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
mMockViewGroup.setFocusable(false);
mMockViewGroup.addFocusables(list, 0);
assertEquals(1, list.size());
+
+ // Both parent and child are focusable.
+ list.clear();
+ mMockViewGroup.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
+ mTextView.setFocusable(true);
+ mMockViewGroup.setFocusable(true);
+ mMockViewGroup.addFocusables(list, 0);
+ assertEquals(2, list.size());
+
+ // Group is a cluster.
+ list.clear();
+ mMockViewGroup.setKeyboardNavigationCluster(true);
+ mMockViewGroup.addFocusables(list, View.FOCUS_FORWARD);
+ assertEquals(0, list.size());
+ mMockViewGroup.addFocusables(list, View.FOCUS_DOWN);
+ assertEquals(2, list.size());
+ list.clear();
+ mTextView.requestFocus();
+ mMockViewGroup.addFocusables(list, View.FOCUS_FORWARD);
+ assertEquals(2, list.size());
}
@UiThreadTest
@@ -751,6 +773,22 @@
@UiThreadTest
@Test
+ public void testFindFocusWithCluster() {
+ // v1 is a cluster, so it doesn't accept focus from outside if it.
+ View v1 = new MockView(mContext);
+ v1.setFocusable(true);
+ v1.setKeyboardNavigationCluster(true);
+ View v2 = new MockView(mContext);
+ v2.setFocusable(true);
+ mMockViewGroup.addView(v1);
+ mMockViewGroup.addView(v2);
+
+ mMockViewGroup.requestFocus();
+ assertSame(v2, mMockViewGroup.findFocus());
+ }
+
+ @UiThreadTest
+ @Test
public void testFitSystemWindows() {
Rect rect = new Rect(1, 1, 100, 100);
assertFalse(mMockViewGroup.fitSystemWindows(rect));
@@ -806,12 +844,33 @@
MockView child = new MockView(mContext);
mMockViewGroup.addView(child);
child.addView(mMockTextView);
- assertNotNull(child.focusSearch(mMockTextView, 1));
assertSame(mMockTextView, child.focusSearch(mMockTextView, 1));
}
@UiThreadTest
@Test
+ public void testFocusSearchWithCluster() {
+ // focusSearch for FORWARD treats cluster like a root.
+ View view = new View(mContext);
+ view.setFocusable(true);
+ View auntView = new View(mContext);
+ auntView.setFocusable(true);
+ MockViewGroup viewParent = new MockViewGroup(mContext);
+ viewParent.addView(view);
+ mMockViewGroup.addView(viewParent);
+ mMockViewGroup.addView(auntView);
+ mMockViewGroup.returnActualFocusSearchResult = true;
+ viewParent.returnActualFocusSearchResult = true;
+ mMockViewGroup.setIsRootNamespace(true);
+ view.requestFocus();
+
+ assertSame(auntView, view.focusSearch(View.FOCUS_FORWARD));
+ viewParent.setKeyboardNavigationCluster(true);
+ assertSame(view, view.focusSearch(View.FOCUS_FORWARD));
+ }
+
+ @UiThreadTest
+ @Test
public void testGatherTransparentRegion() {
Region region = new Region();
mMockTextView.setAnimation(new AlphaAnimation(mContext, null));
@@ -1469,6 +1528,23 @@
@UiThreadTest
@Test
+ public void testRequestFocusWithCluster() {
+ // requestFocus skips nested clusters.
+ View v1 = new View(mContext);
+ v1.setFocusable(true);
+ View v2 = new View(mContext);
+ v2.setFocusable(true);
+ mMockViewGroup.addView(v1);
+ mMockViewGroup.addView(v2);
+
+ assertSame(null, mMockViewGroup.findFocus());
+ v1.setKeyboardNavigationCluster(true);
+ assertTrue(mMockViewGroup.requestFocus());
+ assertSame(v2, mMockViewGroup.findFocus());
+ }
+
+ @UiThreadTest
+ @Test
public void testRequestTransparentRegion() {
MockViewGroup parent = new MockViewGroup(mContext);
MockView child1 = new MockView(mContext);
@@ -2338,6 +2414,7 @@
public int top;
public int right;
public int bottom;
+ public boolean returnActualFocusSearchResult;
public MockViewGroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
@@ -2605,8 +2682,12 @@
@Override
public View focusSearch(View focused, int direction) {
- super.focusSearch(focused, direction);
- return focused;
+ if (returnActualFocusSearchResult) {
+ return super.focusSearch(focused, direction);
+ } else {
+ super.focusSearch(focused, direction);
+ return focused;
+ }
}
@Override
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 4701fda..6512f1b 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -440,6 +440,39 @@
}
@Test
+ public void testPointerIconOverlap() throws Throwable {
+ View parent = mActivity.findViewById(R.id.pointer_icon_overlap);
+ View child1 = mActivity.findViewById(R.id.pointer_icon_overlap_child1);
+ View child2 = mActivity.findViewById(R.id.pointer_icon_overlap_child2);
+ View child3 = mActivity.findViewById(R.id.pointer_icon_overlap_child3);
+
+ PointerIcon iconParent = PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_HAND);
+ PointerIcon iconChild1 = PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_HELP);
+ PointerIcon iconChild2 = PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_TEXT);
+ PointerIcon iconChild3 = PointerIcon.getSystemIcon(mActivity, PointerIcon.TYPE_GRAB);
+
+ parent.setPointerIcon(iconParent);
+ child1.setPointerIcon(iconChild1);
+ child2.setPointerIcon(iconChild2);
+ child3.setPointerIcon(iconChild3);
+
+ MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0);
+
+ assertEquals(iconChild3, parent.onResolvePointerIcon(event, 0));
+
+ setVisibilityOnUiThread(child3, View.GONE);
+ assertEquals(iconChild2, parent.onResolvePointerIcon(event, 0));
+
+ child2.setPointerIcon(null);
+ assertEquals(iconChild1, parent.onResolvePointerIcon(event, 0));
+
+ setVisibilityOnUiThread(child1, View.GONE);
+ assertEquals(iconParent, parent.onResolvePointerIcon(event, 0));
+
+ event.recycle();
+ }
+
+ @Test
public void testCreatePointerIcons() {
assertSystemPointerIcon(PointerIcon.TYPE_NULL);
assertSystemPointerIcon(PointerIcon.TYPE_DEFAULT);
diff --git a/tests/tests/widget/Android.mk b/tests/tests/widget/Android.mk
index c19c27e..c6d85c6 100644
--- a/tests/tests/widget/Android.mk
+++ b/tests/tests/widget/Android.mk
@@ -27,7 +27,8 @@
android-common \
compatibility-device-util \
ctstestrunner \
- platform-test-annotations
+ platform-test-annotations \
+ legacy-android-test
LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 577c52a..e102ec5 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -3356,22 +3356,6 @@
"\"smcp\" on", mTextView.getFontFeatureSettings());
}
- @UiThreadTest
- @Test
- public void testSetGetFontVariationSettings() {
- mTextView = new TextView(mActivity);
-
- // The default font variation settings should be null.
- assertNull(mTextView.getFontVariationSettings());
-
- final String setting = "'wdth' 2.0";
- mTextView.setFontVariationSettings(setting);
- assertEquals(setting, mTextView.getFontVariationSettings());
-
- mTextView.setFontVariationSettings("");
- assertNull(mTextView.getFontVariationSettings());
- }
-
@Test
public void testGetOffsetForPositionSingleLineLtr() throws Throwable {
// asserts getOffsetPosition returns correct values for a single line LTR text
@@ -6618,6 +6602,32 @@
assertTrue(autoSizeTextViewXY.getTextSize() < sizeSetInPixels);
}
+ @UiThreadTest
+ @Test
+ public void testOnInitializeA11yNodeInfo_populatesHintTextProperly() {
+ final TextView textView = new TextView(mActivity);
+ textView.setText("", BufferType.EDITABLE);
+ final String hintText = "Hint text";
+ textView.setHint(hintText);
+ AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ textView.onInitializeAccessibilityNodeInfo(info);
+ assertTrue("Hint text flag set incorrectly for accessibility", info.isShowingHintText());
+ assertTrue("Hint text not showing as accessibility text",
+ TextUtils.equals(hintText, info.getText()));
+ assertTrue("Hint text not provided to accessibility",
+ TextUtils.equals(hintText, info.getHintText()));
+
+ final String nonHintText = "Something else";
+ textView.setText(nonHintText, BufferType.EDITABLE);
+ textView.onInitializeAccessibilityNodeInfo(info);
+ assertFalse("Hint text flag set incorrectly for accessibility", info.isShowingHintText());
+ assertTrue("Text not provided to accessibility",
+ TextUtils.equals(nonHintText, info.getText()));
+ assertTrue("Hint text not provided to accessibility",
+ TextUtils.equals(hintText, info.getHintText()));
+ }
+
+
/**
* Some TextView attributes require non-fixed width and/or layout height. This function removes
* all other existing views from the layout leaving only one auto-size TextView (for exercising
diff --git a/tools/cts-tradefed/res/config/deqp-tests-for-n.xml b/tools/cts-tradefed/res/config/deqp-tests-for-n.xml
deleted file mode 100644
index 014cd0b..0000000
--- a/tools/cts-tradefed/res/config/deqp-tests-for-n.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<configuration description="Run deqp tests that were used for NYC">
-
- <include name="cts" />
-
- <option name="plan" value="cts-deqp-nyc" />
-
- <option name="skip-preconditions" value="true" />
- <option name="skip-connectivity-check" value="true" />
-
- <!-- Include all Vulkan tests, exclude N tests -->
- <option name="compatibility:module-arg" value="CtsDeqpTestCases:include-filter:dEQP-EGL.*" />
- <option name="compatibility:module-arg" value="CtsDeqpTestCases:include-filter:dEQP-GLES2.*" />
- <option name="compatibility:module-arg" value="CtsDeqpTestCases:include-filter:dEQP-GLES3.*" />
- <option name="compatibility:module-arg" value="CtsDeqpTestCases:include-filter-file:nyc-gles31-master.txt" />
- <option name="compatibility:module-arg" value="CtsDeqpTestCases:include-filter-file:nyc-vk-master.txt" />
-</configuration>
diff --git a/tools/cts-tradefed/res/config/deqp-tests-new-for-o.xml b/tools/cts-tradefed/res/config/deqp-tests-new-for-o.xml
deleted file mode 100644
index 4cdb52e..0000000
--- a/tools/cts-tradefed/res/config/deqp-tests-new-for-o.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<configuration description="Run deqp tests new in OC">
-
- <include name="cts" />
-
- <option name="plan" value="cts-deqp-staging" />
-
- <option name="skip-preconditions" value="true" />
- <option name="skip-connectivity-check" value="true" />
-
- <!-- Include all Vulkan tests, exclude N tests -->
- <option name="compatibility:module-arg" value="CtsDeqpTestCases:include-filter:dEQP-GLES31.*" />
- <option name="compatibility:module-arg" value="CtsDeqpTestCases:exclude-filter-file:nyc-vk-master.txt" />
- <option name="compatibility:module-arg" value="CtsDeqpTestCases:include-filter:dEQP-VK.*" />
- <option name="compatibility:module-arg" value="CtsDeqpTestCases:exclude-filter-file:nyc-gles31-master.txt" />
-</configuration>